From ec6e4f3a063f5cc4cd7b19132ef7cdbea6b71e36 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 16 Feb 2021 14:46:16 +0100 Subject: [PATCH 001/195] adapt user collection properties --- app/config/collections.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/config/collections.php b/app/config/collections.php index 9170b07573..3307e7a12c 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -204,7 +204,7 @@ $collections = [ 'key' => 'email', 'type' => Database::SYSTEM_VAR_TYPE_EMAIL, 'default' => '', - 'required' => true, + 'required' => false, 'array' => false, ], [ @@ -222,7 +222,7 @@ $collections = [ 'key' => 'password', 'type' => Database::SYSTEM_VAR_TYPE_TEXT, 'default' => '', - 'required' => true, + 'required' => false, 'array' => false, ], [ From 9d189165040fe956ef4aaa9dfcb119aa95faae39 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 16 Feb 2021 14:46:30 +0100 Subject: [PATCH 002/195] create new endpoint --- app/controllers/api/account.php | 141 ++++++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 5 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index e8ae76312b..bae62db4a8 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -564,6 +564,128 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ; }); +App::post('/v1/account/sessions/anonymous') + ->desc('Create Anonymous Account') + ->groups(['api', 'account']) + ->label('event', 'account.sessions.create') + ->label('scope', 'public') + ->label('sdk.platform', [APP_PLATFORM_CLIENT]) + ->label('sdk.namespace', 'account') + ->label('sdk.method', 'createAnonymous') + ->label('sdk.description', '/docs/references/account/create-anonymous.md') + ->label('sdk.response.code', Response::STATUS_CODE_CREATED) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_SESSION) + ->label('abuse-limit', 50) + ->label('abuse-key', 'ip:{ip}') + ->inject('request') + ->inject('response') + ->inject('locale') + ->inject('user') + ->inject('projectDB') + ->inject('geodb') + ->inject('audits') + ->action(function ($request, $response, $locale, $user, $projectDB, $geodb, $audits) { + /** @var Utopia\Swoole\Request $request */ + /** @var Appwrite\Utopia\Response $response */ + /** @var Utopia\Locale\Locale $locale */ + /** @var Appwrite\Database\Document $user */ + /** @var Appwrite\Database\Database $projectDB */ + /** @var MaxMind\Db\Reader $geodb */ + /** @var Appwrite\Event\Event $audits */ + + $protocol = $request->getProtocol(); + + if ($user->getId()) { + throw new Exception('Failed to create anonymous user.', 401); + } + + Authorization::disable(); + try { + $user = $projectDB->createDocument([ + '$collection' => Database::SYSTEM_COLLECTION_USERS, + '$permissions' => [ + 'read' => ['*'], + 'write' => ['user:{self}'] + ], + 'email' => null, + 'emailVerification' => false, + 'status' => Auth::USER_STATUS_UNACTIVATED, + 'password' => null, + 'passwordUpdate' => \time(), + 'registration' => \time(), + 'reset' => false, + 'name' => null + ]); + } catch (Exception $th) { + throw new Exception('Failed saving user to DB', 500); + } + + Authorization::enable(); + + if (false === $user) { + throw new Exception('Failed saving user to DB', 500); + } + + // Create session token, verify user account and update OAuth2 ID and Access Token + + $detector = new Detector($request->getUserAgent('UNKNOWN')); + $record = $geodb->get($request->getIP()); + $secret = Auth::tokenGenerator(); + $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $session = new Document(array_merge( + [ + '$collection' => Database::SYSTEM_COLLECTION_TOKENS, + '$permissions' => ['read' => ['user:' . $user['$id']], 'write' => ['user:' . $user['$id']]], + 'userId' => $user->getId(), + 'type' => Auth::TOKEN_TYPE_LOGIN, + 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak + 'expire' => $expiry, + 'userAgent' => $request->getUserAgent('UNKNOWN'), + 'ip' => $request->getIP(), + 'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--', + ], + $detector->getOS(), + $detector->getClient(), + $detector->getDevice() + )); + + $user->setAttribute('tokens', $session, Document::SET_TYPE_APPEND); + + Authorization::setRole('user:'.$user->getId()); + + $user = $projectDB->updateDocument($user->getArrayCopy()); + + if (false === $user) { + throw new Exception('Failed saving user to DB', 500); + } + + $audits + ->setParam('userId', $user->getId()) + ->setParam('event', 'account.sessions.create') + ->setParam('resource', 'users/'.$user->getId()) + ; + + if (!Config::getParam('domainVerification')) { + $response + ->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])) + ; + } + + $response + ->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->setStatusCode(Response::STATUS_CODE_CREATED) + ; + + $session + ->setAttribute('current', true) + ->setAttribute('countryName', (isset($countries[$session->getAttribute('countryCode')])) ? $countries[$session->getAttribute('countryCode')] : $locale->getText('locale.country.unknown')) + ; + + $response->dynamic($session, Response::MODEL_SESSION); + }); + App::post('/v1/account/jwt') ->desc('Create Account JWT') ->groups(['api', 'account']) @@ -878,7 +1000,12 @@ App::patch('/v1/account/email') /** @var Appwrite\Database\Database $projectDB */ /** @var Appwrite\Event\Event $audits */ - if (!Auth::passwordVerify($password, $user->getAttribute('password'))) { // Double check user password + $isAnonymousUser = is_null($user->getAttribute('email')) && is_null($user->getAttribute('password')); // Check if request is from an anonymous account for converting + + if ( + !$isAnonymousUser && + !Auth::passwordVerify($password, $user->getAttribute('password')) + ) { // Double check user password throw new Exception('Invalid credentials', 401); } @@ -896,10 +1023,14 @@ App::patch('/v1/account/email') // TODO after this user needs to confirm mail again - $user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [ - 'email' => $email, - 'emailVerification' => false, - ])); + $user = $projectDB->updateDocument(\array_merge( + $user->getArrayCopy(), + ($isAnonymousUser ? [ 'password' => Auth::passwordHash($password) ] : []), + [ + 'email' => $email, + 'emailVerification' => false, + ] + )); if (false === $user) { throw new Exception('Failed saving user to DB', 500); From f199e70a8fc9552f0fc76b674d2b9e5432079adf Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 16 Feb 2021 14:46:44 +0100 Subject: [PATCH 003/195] update docs --- docs/references/account/create-anonymous.md | 1 + docs/references/account/update-email.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 docs/references/account/create-anonymous.md diff --git a/docs/references/account/create-anonymous.md b/docs/references/account/create-anonymous.md new file mode 100644 index 0000000000..61895604aa --- /dev/null +++ b/docs/references/account/create-anonymous.md @@ -0,0 +1 @@ +Use this endpoint to allow a new user to register an anonymous account in your project. This route will also create a new session for the user. To allow the new user to convert an anonymous account to a normal account account, you need to update its [email and password](/docs/client/account#accountUpdateEmail). \ No newline at end of file diff --git a/docs/references/account/update-email.md b/docs/references/account/update-email.md index d99b9b4a1d..7be36b7cdf 100644 --- a/docs/references/account/update-email.md +++ b/docs/references/account/update-email.md @@ -1 +1,2 @@ -Update currently logged in user account email address. After changing user address, user confirmation status is being reset and a new confirmation mail is sent. For security measures, user password is required to complete this request. \ No newline at end of file +Update currently logged in user account email address. After changing user address, user confirmation status is being reset and a new confirmation mail is sent. For security measures, user password is required to complete this request. +This endpoint can also be used to convert an anonymous account to a normal one, by passing an email address and a new password. \ No newline at end of file From 6c717b7a314ac74ede2d69ebff0d43cb6d2379d2 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 16 Feb 2021 14:49:21 +0100 Subject: [PATCH 004/195] add tests --- tests/e2e/Services/Account/AccountBase.php | 33 ++++++ .../Account/AccountCustomClientTest.php | 106 ++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/tests/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php index 5476e04552..b8aa1063aa 100644 --- a/tests/e2e/Services/Account/AccountBase.php +++ b/tests/e2e/Services/Account/AccountBase.php @@ -49,6 +49,39 @@ trait AccountBase $this->assertEquals($response['headers']['status-code'], 409); + $response = $this->client->call(Client::METHOD_POST, '/account', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'email' => '', + 'password' => '', + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + $response = $this->client->call(Client::METHOD_POST, '/account', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'email' => $email, + 'password' => '', + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + $response = $this->client->call(Client::METHOD_POST, '/account', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'email' => '', + 'password' => $password, + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + return [ 'id' => $id, 'email' => $email, diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index a111655307..2a0eea743f 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -226,4 +226,110 @@ class AccountCustomClientTest extends Scope return []; } + + public function testCreateAnonymousAccount() + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/anonymous', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]); + + $this->assertEquals($response['headers']['status-code'], 201); + + $session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$this->getProject()['$id']]; + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/anonymous', [ + '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); + + return $session; + } + + /** + * @depends testCreateAnonymousAccount + */ + public function testUpdateAccountPassword($session):array + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/password', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session, + ]), [ + 'password' => 'new-password', + 'oldPassword' => null, + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + return []; + } + + /** + * @depends testCreateAnonymousAccount + */ + public function testConvertAnonymousAccount($session):array + { + $email = uniqid().'new@localhost.test'; + $password = 'new-password'; + + /** + * Test for SUCCESS + */ + $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, + ]), [ + 'email' => $email, + 'password' => $password, + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertIsNumeric($response['body']['registration']); + $this->assertEquals($response['body']['email'], $email); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_PATCH, '/account/email', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ])); + + $this->assertEquals($response['headers']['status-code'], 401); + + $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, + ]), [ + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + return []; + } } \ No newline at end of file From 6005551023befdd556b8ca8056bf823a87ee31e8 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 16 Feb 2021 15:14:36 +0100 Subject: [PATCH 005/195] update tests --- tests/e2e/Services/Account/AccountBase.php | 4 -- .../Account/AccountCustomClientTest.php | 53 ++++++++++--------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/tests/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php index b8aa1063aa..38d985ab06 100644 --- a/tests/e2e/Services/Account/AccountBase.php +++ b/tests/e2e/Services/Account/AccountBase.php @@ -405,7 +405,6 @@ trait AccountBase $this->assertEquals($response['headers']['status-code'], 200); $this->assertIsArray($response['body']); $this->assertNotEmpty($response['body']); - $this->assertNotEmpty($response['body']); $this->assertNotEmpty($response['body']['$id']); $this->assertIsNumeric($response['body']['registration']); $this->assertEquals($response['body']['email'], $email); @@ -473,7 +472,6 @@ trait AccountBase $this->assertEquals($response['headers']['status-code'], 200); $this->assertIsArray($response['body']); $this->assertNotEmpty($response['body']); - $this->assertNotEmpty($response['body']); $this->assertNotEmpty($response['body']['$id']); $this->assertIsNumeric($response['body']['registration']); $this->assertEquals($response['body']['email'], $email); @@ -540,7 +538,6 @@ trait AccountBase $this->assertEquals($response['headers']['status-code'], 200); $this->assertIsArray($response['body']); $this->assertNotEmpty($response['body']); - $this->assertNotEmpty($response['body']); $this->assertNotEmpty($response['body']['$id']); $this->assertIsNumeric($response['body']['registration']); $this->assertEquals($response['body']['email'], $newEmail); @@ -598,7 +595,6 @@ trait AccountBase $this->assertEquals($response['headers']['status-code'], 200); $this->assertIsArray($response['body']); $this->assertNotEmpty($response['body']); - $this->assertNotEmpty($response['body']); $this->assertEquals('prefValue1', $response['body']['prefs']['prefKey1']); $this->assertEquals('prefValue2', $response['body']['prefs']['prefKey2']); diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index 2a0eea743f..498037d3e2 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -260,10 +260,10 @@ class AccountCustomClientTest extends Scope /** * @depends testCreateAnonymousAccount */ - public function testUpdateAccountPassword($session):array + public function testUpdateAnonymousAccountPassword($session):array { /** - * Test for SUCCESS + * Test for FAILURE */ $response = $this->client->call(Client::METHOD_PATCH, '/account/password', array_merge([ 'origin' => 'http://localhost', @@ -272,7 +272,7 @@ class AccountCustomClientTest extends Scope 'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session, ]), [ 'password' => 'new-password', - 'oldPassword' => null, + 'oldPassword' => '', ]); $this->assertEquals($response['headers']['status-code'], 400); @@ -280,6 +280,31 @@ class AccountCustomClientTest extends Scope return []; } + /** + * @depends testUpdateAnonymousAccountPassword + */ + public function testUpdateAnonymousAccountEmail($session):array + { + $email = uniqid().'new@localhost.test'; + + /** + * Test for FAILURE + */ + $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, + ]), [ + 'email' => $email, + 'password' => '', + ]); + + $this->assertEquals($response['headers']['status-code'], 401); + + return []; + } + /** * @depends testCreateAnonymousAccount */ @@ -304,32 +329,10 @@ class AccountCustomClientTest extends Scope $this->assertEquals($response['headers']['status-code'], 200); $this->assertIsArray($response['body']); $this->assertNotEmpty($response['body']); - $this->assertNotEmpty($response['body']); $this->assertNotEmpty($response['body']['$id']); $this->assertIsNumeric($response['body']['registration']); $this->assertEquals($response['body']['email'], $email); - /** - * Test for FAILURE - */ - $response = $this->client->call(Client::METHOD_PATCH, '/account/email', array_merge([ - 'origin' => 'http://localhost', - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ])); - - $this->assertEquals($response['headers']['status-code'], 401); - - $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, - ]), [ - ]); - - $this->assertEquals($response['headers']['status-code'], 400); - return []; } } \ No newline at end of file From c9b46d93dceba48739a76d2a4943769069c5f651 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 16 Feb 2021 15:16:09 +0100 Subject: [PATCH 006/195] fix comment --- app/controllers/api/account.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index bae62db4a8..5ab3e6e159 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -627,7 +627,7 @@ App::post('/v1/account/sessions/anonymous') throw new Exception('Failed saving user to DB', 500); } - // Create session token, verify user account and update OAuth2 ID and Access Token + // Create session token $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); From b559f8068e5b33f5c52bd9f9ba362060ba572915 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 16 Feb 2021 15:20:15 +0100 Subject: [PATCH 007/195] add test to login after converting --- .../e2e/Services/Account/AccountCustomClientTest.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index 498037d3e2..56dba168c6 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -333,6 +333,17 @@ class AccountCustomClientTest extends Scope $this->assertIsNumeric($response['body']['registration']); $this->assertEquals($response['body']['email'], $email); + $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); + return []; } } \ No newline at end of file From 6a31e19d731727f9099de9af30cf6ed0e084a138 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 16 Feb 2021 16:01:15 +0100 Subject: [PATCH 008/195] add test to convert to existing email --- .../Account/AccountCustomClientTest.php | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index 56dba168c6..73b79e2c83 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -313,9 +313,35 @@ class AccountCustomClientTest extends Scope $email = uniqid().'new@localhost.test'; $password = 'new-password'; + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_POST, '/account', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'email' => $email, + 'password' => $password + ]); + + $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, + ]), [ + 'email' => $email, + 'password' => $password, + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + /** * Test for SUCCESS */ + $email = uniqid().'new@localhost.test'; + $response = $this->client->call(Client::METHOD_PATCH, '/account/email', array_merge([ 'origin' => 'http://localhost', 'content-type' => 'application/json', From 1387fb50f108818c65f49532a0bff95608c613cc Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 16 Feb 2021 16:51:08 +0100 Subject: [PATCH 009/195] add tests for oauth2 --- app/controllers/api/account.php | 9 +++ .../Account/AccountCustomClientTest.php | 56 +++++++++++++++++-- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 5ab3e6e159..277f4e7372 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -515,6 +515,15 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--', ], $detector->getOS(), $detector->getClient(), $detector->getDevice())); + $isAnonymousUser = is_null($user->getAttribute('email')) && is_null($user->getAttribute('password')); + + if ($isAnonymousUser) { + $user + ->setAttribute('name', $oauth2->getUserName($accessToken)) + ->setAttribute('email', $oauth2->getUserEmail($accessToken)) + ; + } + $user ->setAttribute('oauth2'.\ucfirst($provider), $oauth2ID) ->setAttribute('oauth2'.\ucfirst($provider).'AccessToken', $accessToken) diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index 73b79e2c83..cf1af8749a 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -305,11 +305,9 @@ class AccountCustomClientTest extends Scope return []; } - /** - * @depends testCreateAnonymousAccount - */ - public function testConvertAnonymousAccount($session):array + public function testConvertAnonymousAccount():array { + $session = $this->testCreateAnonymousAccount(); $email = uniqid().'new@localhost.test'; $password = 'new-password'; @@ -372,4 +370,54 @@ class AccountCustomClientTest extends Scope return []; } + + public function testConvertAnonymousAccountOAuth2():array + { + $session = $this->testCreateAnonymousAccount(); + $provider = 'mock'; + $appId = '1'; + $secret = '123456'; + + /** + * Test for SUCCESS + */ + $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', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'provider' => $provider, + 'appId' => $appId, + 'secret' => $secret, + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + + $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, + ]), [ + 'success' => 'http://localhost/v1/mock/tests/general/oauth2/success', + 'failure' => 'http://localhost/v1/mock/tests/general/oauth2/failure', + ]); + + $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, + ])); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals($response['body']['name'], 'User Name'); + $this->assertEquals($response['body']['email'], 'user@localhost.test'); + + return []; + } } \ No newline at end of file From d1ae8aa15c90342207e5cbbed4714b91c71a7081 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Wed, 17 Feb 2021 09:47:07 +0100 Subject: [PATCH 010/195] adapt to review --- app/controllers/api/account.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 277f4e7372..5c095c2311 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -109,7 +109,7 @@ App::post('/v1/account') throw new Exception('Account already exists', 409); } - Authorization::enable(); + Authorization::reset(); Authorization::unsetRole('role:'.Auth::USER_ROLE_GUEST); Authorization::setRole('user:'.$user->getId()); @@ -485,7 +485,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') throw new Exception('Account already exists', 409); } - Authorization::enable(); + Authorization::reset(); if (false === $user) { throw new Exception('Failed saving user to DB', 500); @@ -630,7 +630,7 @@ App::post('/v1/account/sessions/anonymous') throw new Exception('Failed saving user to DB', 500); } - Authorization::enable(); + Authorization::reset(); if (false === $user) { throw new Exception('Failed saving user to DB', 500); From 8d3d3bca681f88b23195875e0c2a2d7aa1f61b4f Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Wed, 17 Feb 2021 10:38:45 +0100 Subject: [PATCH 011/195] added anon login to changelog --- CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 490007bcd6..5b10ab43c4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,9 @@ +# Version DEV (NOT RELEASED YET) + +## Features + +- Added Anonymous Login ([RFC-010](https://github.com/appwrite/rfc/blob/main/010-anonymous-login.md)) + # Version 0.7.0 ## Features From 2c1247d667b4b17fbc29c66254dd83ce712ff13d Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Thu, 18 Feb 2021 15:52:27 +0100 Subject: [PATCH 012/195] change sdk method name --- app/controllers/api/account.php | 6 +++--- .../{create-anonymous.md => create-session-anonymous.md} | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename docs/references/account/{create-anonymous.md => create-session-anonymous.md} (100%) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 5c095c2311..d90118d906 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -574,14 +574,14 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') }); App::post('/v1/account/sessions/anonymous') - ->desc('Create Anonymous Account') + ->desc('Create Anonymous Session') ->groups(['api', 'account']) ->label('event', 'account.sessions.create') ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT]) ->label('sdk.namespace', 'account') - ->label('sdk.method', 'createAnonymous') - ->label('sdk.description', '/docs/references/account/create-anonymous.md') + ->label('sdk.method', 'createAnonymousSession') + ->label('sdk.description', '/docs/references/account/create-session-anonymous.md') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_SESSION) diff --git a/docs/references/account/create-anonymous.md b/docs/references/account/create-session-anonymous.md similarity index 100% rename from docs/references/account/create-anonymous.md rename to docs/references/account/create-session-anonymous.md From 312ab657946f24a5bb84a61ac920fd2b9df28c56 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Fri, 19 Feb 2021 11:02:02 +0100 Subject: [PATCH 013/195] decouple oauth2 from user to tokens --- app/config/collections.php | 47 +++++++++++++++++++-------------- app/controllers/api/account.php | 10 ++++--- app/controllers/api/teams.php | 2 ++ src/Appwrite/Auth/Auth.php | 6 +++++ 4 files changed, 42 insertions(+), 23 deletions(-) diff --git a/app/config/collections.php b/app/config/collections.php index 9170b07573..9164cd93b4 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -318,6 +318,33 @@ $collections = [ 'required' => true, 'array' => false, ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Provider', + 'key' => 'provider', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Provider User Identifier', + 'key' => 'providerUid', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Provider Token', + 'key' => 'providerToken', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], [ '$collection' => Database::SYSTEM_COLLECTION_RULES, 'label' => 'Secret', @@ -1617,26 +1644,6 @@ foreach ($providers as $index => $provider) { 'array' => false, 'filter' => ['encrypt'], ]; - - $collections[Database::SYSTEM_COLLECTION_USERS]['rules'][] = [ - '$collection' => Database::SYSTEM_COLLECTION_RULES, - 'label' => 'OAuth2 '.\ucfirst($index).' ID', - 'key' => 'oauth2'.\ucfirst($index), - 'type' => Database::SYSTEM_VAR_TYPE_TEXT, - 'default' => '', - 'required' => false, - 'array' => false, - ]; - - $collections[Database::SYSTEM_COLLECTION_USERS]['rules'][] = [ - '$collection' => Database::SYSTEM_COLLECTION_RULES, - 'label' => 'OAuth2 '.\ucfirst($index).' Access Token', - 'key' => 'oauth2'.\ucfirst($index).'AccessToken', - 'type' => Database::SYSTEM_VAR_TYPE_TEXT, - 'default' => '', - 'required' => false, - 'array' => false, - ]; } return $collections; \ No newline at end of file diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index e8ae76312b..26c76a7f30 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -194,6 +194,8 @@ App::post('/v1/account/sessions') '$permissions' => ['read' => ['user:'.$profile->getId()], 'write' => ['user:'.$profile->getId()]], 'userId' => $profile->getId(), 'type' => Auth::TOKEN_TYPE_LOGIN, + 'provider' => Auth::TOKEN_PROVIDER_EMAIL, + 'providerUid' => $email, 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'expire' => $expiry, 'userAgent' => $request->getUserAgent('UNKNOWN'), @@ -449,7 +451,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'limit' => 1, 'filters' => [ '$collection='.Database::SYSTEM_COLLECTION_USERS, - 'oauth2'.\ucfirst($provider).'='.$oauth2ID, + 'tokens.provider='.$provider, + 'tokens.providerUid='.$oauth2ID ], ]) : $user; @@ -508,6 +511,9 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') '$permissions' => ['read' => ['user:'.$user['$id']], 'write' => ['user:'.$user['$id']]], 'userId' => $user->getId(), 'type' => Auth::TOKEN_TYPE_LOGIN, + 'provider' => $provider, + 'providerUid' => $oauth2ID, + 'providerToken' => $accessToken, 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'expire' => $expiry, 'userAgent' => $request->getUserAgent('UNKNOWN'), @@ -516,8 +522,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ], $detector->getOS(), $detector->getClient(), $detector->getDevice())); $user - ->setAttribute('oauth2'.\ucfirst($provider), $oauth2ID) - ->setAttribute('oauth2'.\ucfirst($provider).'AccessToken', $accessToken) ->setAttribute('status', Auth::USER_STATUS_ACTIVATED) ->setAttribute('tokens', $session, Document::SET_TYPE_APPEND) ; diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index c4a9e4875d..a674eacf0d 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -599,6 +599,8 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status') '$permissions' => ['read' => ['user:'.$user->getId()], 'write' => ['user:'.$user->getId()]], 'userId' => $user->getId(), 'type' => Auth::TOKEN_TYPE_LOGIN, + 'provider' => Auth::TOKEN_PROVIDER_EMAIL, + 'providerUid' => $user->getAttribute('email'), 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'expire' => $expiry, 'userAgent' => $request->getUserAgent('UNKNOWN'), diff --git a/src/Appwrite/Auth/Auth.php b/src/Appwrite/Auth/Auth.php index f2d786c937..b9da8cde7d 100644 --- a/src/Appwrite/Auth/Auth.php +++ b/src/Appwrite/Auth/Auth.php @@ -33,6 +33,12 @@ class Auth const TOKEN_TYPE_RECOVERY = 3; const TOKEN_TYPE_INVITE = 4; + /** + * Session Providers. + */ + const TOKEN_PROVIDER_EMAIL = 'email'; + const TOKEN_PROVIDER_ANONYMOUS = 'anonymous'; + /** * Token Expiration times. */ From d4bd9c0d06f3f8032ef166b989f2de58cd39a0d3 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Fri, 19 Feb 2021 13:12:47 +0100 Subject: [PATCH 014/195] decouple user session from tokens --- app/config/collections.php | 93 ++++++++++++++++++++++++++---- app/controllers/api/account.php | 71 +++++++++++------------ app/controllers/api/teams.php | 7 +-- app/controllers/api/users.php | 17 +++--- app/init.php | 2 +- src/Appwrite/Auth/Auth.php | 30 +++++++++- src/Appwrite/Database/Database.php | 1 + tests/unit/Auth/AuthTest.php | 71 ++++++++++++++++++----- 8 files changed, 214 insertions(+), 78 deletions(-) diff --git a/app/config/collections.php b/app/config/collections.php index 9164cd93b4..92e687cd17 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -271,6 +271,16 @@ $collections = [ 'required' => true, 'array' => false, ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Sessions', + 'key' => 'sessions', + 'type' => Database::SYSTEM_VAR_TYPE_DOCUMENT, + 'default' => [], + 'required' => false, + 'array' => true, + 'list' => [Database::SYSTEM_COLLECTION_SESSIONS], + ], [ '$collection' => Database::SYSTEM_COLLECTION_RULES, 'label' => 'Tokens', @@ -293,11 +303,11 @@ $collections = [ ], ], ], - Database::SYSTEM_COLLECTION_TOKENS => [ + Database::SYSTEM_COLLECTION_SESSIONS => [ '$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS, - '$id' => Database::SYSTEM_COLLECTION_TOKENS, + '$id' => Database::SYSTEM_COLLECTION_SESSIONS, '$permissions' => ['read' => ['*']], - 'name' => 'Token', + 'name' => 'Sessions', 'structure' => true, 'rules' => [ [ @@ -306,15 +316,15 @@ $collections = [ 'key' => 'userId', 'type' => Database::SYSTEM_VAR_TYPE_TEXT, 'default' => null, - 'required' => false, + 'required' => true, 'array' => false, ], [ '$collection' => Database::SYSTEM_COLLECTION_RULES, - 'label' => 'Type', - 'key' => 'type', - 'type' => Database::SYSTEM_VAR_TYPE_NUMERIC, - 'default' => null, + 'label' => 'Secret', + 'key' => 'secret', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', 'required' => true, 'array' => false, ], @@ -324,7 +334,7 @@ $collections = [ 'key' => 'provider', 'type' => Database::SYSTEM_VAR_TYPE_TEXT, 'default' => '', - 'required' => false, + 'required' => true, 'array' => false, ], [ @@ -333,7 +343,7 @@ $collections = [ 'key' => 'providerUid', 'type' => Database::SYSTEM_VAR_TYPE_TEXT, 'default' => '', - 'required' => false, + 'required' => true, 'array' => false, ], [ @@ -500,6 +510,69 @@ $collections = [ ], ], ], + Database::SYSTEM_COLLECTION_TOKENS => [ + '$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS, + '$id' => Database::SYSTEM_COLLECTION_TOKENS, + '$permissions' => ['read' => ['*']], + 'name' => 'Token', + 'structure' => true, + 'rules' => [ + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'User ID', + 'key' => 'userId', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => null, + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Type', + 'key' => 'type', + 'type' => Database::SYSTEM_VAR_TYPE_NUMERIC, + 'default' => null, + 'required' => true, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Secret', + 'key' => 'secret', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => true, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Expire', + 'key' => 'expire', + 'type' => Database::SYSTEM_VAR_TYPE_NUMERIC, + 'default' => 0, + 'required' => true, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'User Agent', + 'key' => 'userAgent', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => true, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'IP', + 'key' => 'ip', + 'type' => Database::SYSTEM_VAR_TYPE_IP, + 'default' => '', + 'required' => true, + 'array' => false, + ], + ], + ], Database::SYSTEM_COLLECTION_MEMBERSHIPS => [ '$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS, '$id' => Database::SYSTEM_COLLECTION_MEMBERSHIPS, diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 26c76a7f30..a81e38288a 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -190,11 +190,10 @@ App::post('/v1/account/sessions') $secret = Auth::tokenGenerator(); $session = new Document(array_merge( [ - '$collection' => Database::SYSTEM_COLLECTION_TOKENS, + '$collection' => Database::SYSTEM_COLLECTION_SESSIONS, '$permissions' => ['read' => ['user:'.$profile->getId()], 'write' => ['user:'.$profile->getId()]], 'userId' => $profile->getId(), - 'type' => Auth::TOKEN_TYPE_LOGIN, - 'provider' => Auth::TOKEN_PROVIDER_EMAIL, + 'provider' => Auth::SESSION_PROVIDER_EMAIL, 'providerUid' => $email, 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'expire' => $expiry, @@ -212,7 +211,7 @@ App::post('/v1/account/sessions') throw new Exception('Failed saving session to DB', 500); } - $profile->setAttribute('tokens', $session, Document::SET_TYPE_APPEND); + $profile->setAttribute('sessions', $session, Document::SET_TYPE_APPEND); $profile = $projectDB->updateDocument($profile->getArrayCopy()); @@ -441,7 +440,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') throw new Exception('Missing ID from OAuth2 provider', 400); } - $current = Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret); + $current = Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret); if ($current) { $projectDB->deleteDocument($current); //throw new Exception('User already logged in', 401); @@ -451,8 +450,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'limit' => 1, 'filters' => [ '$collection='.Database::SYSTEM_COLLECTION_USERS, - 'tokens.provider='.$provider, - 'tokens.providerUid='.$oauth2ID + 'sessions.provider='.$provider, + 'sessions.providerUid='.$oauth2ID ], ]) : $user; @@ -507,10 +506,9 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $secret = Auth::tokenGenerator(); $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; $session = new Document(array_merge([ - '$collection' => Database::SYSTEM_COLLECTION_TOKENS, + '$collection' => Database::SYSTEM_COLLECTION_SESSIONS, '$permissions' => ['read' => ['user:'.$user['$id']], 'write' => ['user:'.$user['$id']]], 'userId' => $user->getId(), - 'type' => Auth::TOKEN_TYPE_LOGIN, 'provider' => $provider, 'providerUid' => $oauth2ID, 'providerToken' => $accessToken, @@ -523,7 +521,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $user ->setAttribute('status', Auth::USER_STATUS_ACTIVATED) - ->setAttribute('tokens', $session, Document::SET_TYPE_APPEND) + ->setAttribute('sessions', $session, Document::SET_TYPE_APPEND) ; Authorization::setRole('user:'.$user->getId()); @@ -585,16 +583,18 @@ App::post('/v1/account/jwt') /** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Database\Document $user */ - $tokens = $user->getAttribute('tokens', []); - $session = new Document(); + $sessions = $user->getAttribute('sessions', []); + $current = new Document(); - foreach ($tokens as $token) { /** @var Appwrite\Database\Document $token */ - if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too - $session = $token; + foreach ($sessions as $session) { + /** @var Appwrite\Database\Document $session */ + + if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + $current = $session; } } - if($session->isEmpty()) { + if($current->isEmpty()) { throw new Exception('No valid session found', 401); } @@ -608,7 +608,7 @@ App::post('/v1/account/jwt') // 'scopes' => ['user'], // 'iss' => 'http://api.mysite.com', 'userId' => $user->getId(), - 'sessionId' => $session->getId(), + 'sessionId' => $current->getId(), ])]), Response::MODEL_JWT); }); @@ -673,22 +673,19 @@ App::get('/v1/account/sessions') /** @var Appwrite\Database\Document $user */ /** @var Utopia\Locale\Locale $locale */ - $tokens = $user->getAttribute('tokens', []); - $sessions = []; + $sessions = $user->getAttribute('sessions', []); $countries = $locale->getText('countries'); - $current = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_LOGIN, Auth::$secret); + $current = Auth::sessionVerify($sessions, Auth::$secret); - foreach ($tokens as $token) { /* @var $token Document */ - if (Auth::TOKEN_TYPE_LOGIN != $token->getAttribute('type')) { - continue; - } + foreach ($sessions as $key => $session) { + /** @var Document $session */ - $token->setAttribute('countryName', (isset($countries[$token->getAttribute('contryCode')])) - ? $countries[$token->getAttribute('contryCode')] + $session->setAttribute('countryName', (isset($countries[$session->getAttribute('contryCode')])) + ? $countries[$session->getAttribute('contryCode')] : $locale->getText('locale.country.unknown')); - $token->setAttribute('current', ($current == $token->getId()) ? true : false); + $session->setAttribute('current', ($current == $session->getId()) ? true : false); - $sessions[] = $token; + $sessions[$key] = $session; } $response->dynamic(new Document([ @@ -1052,14 +1049,14 @@ App::delete('/v1/account/sessions/:sessionId') $protocol = $request->getProtocol(); $sessionId = ($sessionId === 'current') - ? Auth::tokenVerify($user->getAttribute('tokens'), Auth::TOKEN_TYPE_LOGIN, Auth::$secret) + ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) : $sessionId; - $tokens = $user->getAttribute('tokens', []); + $sessions = $user->getAttribute('sessions', []); - foreach ($tokens as $token) { /* @var $token Document */ - if (($sessionId == $token->getId()) && Auth::TOKEN_TYPE_LOGIN == $token->getAttribute('type')) { - if (!$projectDB->deleteDocument($token->getId())) { + foreach ($sessions as $session) { /** @var Document $session */ + if (($sessionId == $session->getId())) { + if (!$projectDB->deleteDocument($session->getId())) { throw new Exception('Failed to remove token from DB', 500); } @@ -1075,10 +1072,10 @@ App::delete('/v1/account/sessions/:sessionId') ; } - $token->setAttribute('current', false); + $session->setAttribute('current', false); - if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too - $token->setAttribute('current', true); + if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + $session->setAttribute('current', true); $response ->addCookie(Auth::$cookieName.'_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) @@ -1087,7 +1084,7 @@ App::delete('/v1/account/sessions/:sessionId') } $events - ->setParam('payload', $response->output($token, Response::MODEL_SESSION)) + ->setParam('payload', $response->output($session, Response::MODEL_SESSION)) ; return $response->noContent(); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index a674eacf0d..775d9b47a3 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -595,11 +595,10 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status') $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; $secret = Auth::tokenGenerator(); $session = new Document(array_merge([ - '$collection' => Database::SYSTEM_COLLECTION_TOKENS, + '$collection' => Database::SYSTEM_COLLECTION_SESSIONS, '$permissions' => ['read' => ['user:'.$user->getId()], 'write' => ['user:'.$user->getId()]], 'userId' => $user->getId(), - 'type' => Auth::TOKEN_TYPE_LOGIN, - 'provider' => Auth::TOKEN_PROVIDER_EMAIL, + 'provider' => Auth::SESSION_PROVIDER_EMAIL, 'providerUid' => $user->getAttribute('email'), 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'expire' => $expiry, @@ -608,7 +607,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status') 'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--', ], $detector->getOS(), $detector->getClient(), $detector->getDevice())); - $user->setAttribute('tokens', $session, Document::SET_TYPE_APPEND); + $user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND); Authorization::setRole('user:'.$userId); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index efb0041cee..b8e79ccb95 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -196,21 +196,18 @@ App::get('/v1/users/:userId/sessions') throw new Exception('User not found', 404); } - $tokens = $user->getAttribute('tokens', []); - $sessions = []; + $sessions = $user->getAttribute('sessions', []); $countries = $locale->getText('countries'); - foreach ($tokens as $token) { /* @var $token Document */ - if (Auth::TOKEN_TYPE_LOGIN != $token->getAttribute('type')) { - continue; - } + foreach ($sessions as $key => $session) { + /** @var Document $session */ - $token->setAttribute('countryName', (isset($countries[$token->getAttribute('contryCode')])) - ? $countries[$token->getAttribute('contryCode')] + $session->setAttribute('countryName', (isset($countries[$session->getAttribute('contryCode')])) + ? $countries[$session->getAttribute('contryCode')] : $locale->getText('locale.country.unknown')); - $token->setAttribute('current', false); + $session->setAttribute('current', false); - $sessions[] = $token; + $sessions[$key] = $session; } $response->dynamic(new Document([ diff --git a/app/init.php b/app/init.php index 7cf2f9f0fb..372a3857b0 100644 --- a/app/init.php +++ b/app/init.php @@ -419,7 +419,7 @@ App::setResource('user', function($mode, $project, $console, $request, $response if (empty($user->getId()) // Check a document has been found in the DB || Database::SYSTEM_COLLECTION_USERS !== $user->getCollection() // Validate returned document is really a user document - || !Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret)) { // Validate user has valid login token + || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret)) { // Validate user has valid login token $user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]); } diff --git a/src/Appwrite/Auth/Auth.php b/src/Appwrite/Auth/Auth.php index b9da8cde7d..18a2837f5a 100644 --- a/src/Appwrite/Auth/Auth.php +++ b/src/Appwrite/Auth/Auth.php @@ -28,7 +28,7 @@ class Auth /** * Token Types. */ - const TOKEN_TYPE_LOGIN = 1; + const TOKEN_TYPE_LOGIN = 1; // Deprecated const TOKEN_TYPE_VERIFICATION = 2; const TOKEN_TYPE_RECOVERY = 3; const TOKEN_TYPE_INVITE = 4; @@ -36,8 +36,8 @@ class Auth /** * Session Providers. */ - const TOKEN_PROVIDER_EMAIL = 'email'; - const TOKEN_PROVIDER_ANONYMOUS = 'anonymous'; + const SESSION_PROVIDER_EMAIL = 'email'; + const SESSION_PROVIDER_ANONYMOUS = 'anonymous'; /** * Token Expiration times. @@ -213,6 +213,30 @@ class Auth return false; } + /** + * Verify session and check that its not expired. + * + * @param array $sessions + * @param string $secret + * + * @return bool|string + */ + public static function sessionVerify(array $sessions, string $secret) + { + foreach ($sessions as $session) { /** @var Document $session */ + if ($session->isSet('secret') && + $session->isSet('expire') && + $session->isSet('provider') && + $session->isSet('providerUid') && + $session->getAttribute('secret') === self::hash($secret) && + $session->getAttribute('expire') >= \time()) { + return (string)$session->getId(); + } + } + + return false; + } + /** * Is Previligged User? * diff --git a/src/Appwrite/Database/Database.php b/src/Appwrite/Database/Database.php index 4154214b82..d0defdec03 100644 --- a/src/Appwrite/Database/Database.php +++ b/src/Appwrite/Database/Database.php @@ -27,6 +27,7 @@ class Database // Auth, Account and Users (private to user) const SYSTEM_COLLECTION_USERS = 'users'; + const SYSTEM_COLLECTION_SESSIONS = 'sessions'; const SYSTEM_COLLECTION_TOKENS = 'tokens'; // Teams (shared among team members) diff --git a/tests/unit/Auth/AuthTest.php b/tests/unit/Auth/AuthTest.php index 5860d1efb9..6a9b8ab3b0 100644 --- a/tests/unit/Auth/AuthTest.php +++ b/tests/unit/Auth/AuthTest.php @@ -62,41 +62,55 @@ class AuthTest extends TestCase $this->assertEquals(\mb_strlen(Auth::tokenGenerator(5)), 10); } - public function testTokenVerify() + public function testSessionVerify() { $secret = 'secret1'; $hash = Auth::hash($secret); $tokens1 = [ new Document([ '$id' => 'token1', - 'type' => Auth::TOKEN_TYPE_LOGIN, 'expire' => time() + 60 * 60 * 24, 'secret' => $hash, + 'provider' => Auth::SESSION_PROVIDER_EMAIL, + 'providerUid' => 'test@example.com', ]), new Document([ '$id' => 'token2', - 'type' => Auth::TOKEN_TYPE_LOGIN, 'expire' => time() - 60 * 60 * 24, 'secret' => 'secret2', + 'provider' => Auth::SESSION_PROVIDER_EMAIL, + 'providerUid' => 'test@example.com', ]), ]; $tokens2 = [ new Document([ // Correct secret and type time, wrong expire time '$id' => 'token1', - 'type' => Auth::TOKEN_TYPE_LOGIN, 'expire' => time() - 60 * 60 * 24, 'secret' => $hash, + 'provider' => Auth::SESSION_PROVIDER_EMAIL, + 'providerUid' => 'test@example.com', ]), new Document([ '$id' => 'token2', - 'type' => Auth::TOKEN_TYPE_LOGIN, 'expire' => time() - 60 * 60 * 24, 'secret' => 'secret2', + 'provider' => Auth::SESSION_PROVIDER_EMAIL, + 'providerUid' => 'test@example.com', ]), ]; - $tokens3 = [ // Correct secret and expire time, wrong type + $this->assertEquals(Auth::sessionVerify($tokens1, $secret), 'token1'); + $this->assertEquals(Auth::sessionVerify($tokens1, 'false-secret'), false); + $this->assertEquals(Auth::sessionVerify($tokens2, $secret), false); + $this->assertEquals(Auth::sessionVerify($tokens2, 'false-secret'), false); + } + + public function testTokenVerify() + { + $secret = 'secret1'; + $hash = Auth::hash($secret); + $tokens1 = [ new Document([ '$id' => 'token1', 'type' => Auth::TOKEN_TYPE_RECOVERY, @@ -105,20 +119,51 @@ class AuthTest extends TestCase ]), new Document([ '$id' => 'token2', - 'type' => Auth::TOKEN_TYPE_LOGIN, + 'type' => Auth::TOKEN_TYPE_RECOVERY, 'expire' => time() - 60 * 60 * 24, 'secret' => 'secret2', ]), ]; - $this->assertEquals(Auth::tokenVerify($tokens1, Auth::TOKEN_TYPE_LOGIN, $secret), 'token1'); - $this->assertEquals(Auth::tokenVerify($tokens1, Auth::TOKEN_TYPE_LOGIN, 'false-secret'), false); - $this->assertEquals(Auth::tokenVerify($tokens2, Auth::TOKEN_TYPE_LOGIN, $secret), false); - $this->assertEquals(Auth::tokenVerify($tokens2, Auth::TOKEN_TYPE_LOGIN, 'false-secret'), false); - $this->assertEquals(Auth::tokenVerify($tokens3, Auth::TOKEN_TYPE_LOGIN, $secret), false); - $this->assertEquals(Auth::tokenVerify($tokens3, Auth::TOKEN_TYPE_LOGIN, 'false-secret'), false); + $tokens2 = [ + new Document([ // Correct secret and type time, wrong expire time + '$id' => 'token1', + 'type' => Auth::TOKEN_TYPE_RECOVERY, + 'expire' => time() - 60 * 60 * 24, + 'secret' => $hash, + ]), + new Document([ + '$id' => 'token2', + 'type' => Auth::TOKEN_TYPE_RECOVERY, + 'expire' => time() - 60 * 60 * 24, + 'secret' => 'secret2', + ]), + ]; + + $tokens3 = [ // Correct secret and expire time, wrong type + new Document([ + '$id' => 'token1', + 'type' => Auth::TOKEN_TYPE_INVITE, + 'expire' => time() + 60 * 60 * 24, + 'secret' => $hash, + ]), + new Document([ + '$id' => 'token2', + 'type' => Auth::TOKEN_TYPE_RECOVERY, + 'expire' => time() - 60 * 60 * 24, + 'secret' => 'secret2', + ]), + ]; + + $this->assertEquals(Auth::tokenVerify($tokens1, Auth::TOKEN_TYPE_RECOVERY, $secret), 'token1'); + $this->assertEquals(Auth::tokenVerify($tokens1, Auth::TOKEN_TYPE_RECOVERY, 'false-secret'), false); + $this->assertEquals(Auth::tokenVerify($tokens2, Auth::TOKEN_TYPE_RECOVERY, $secret), false); + $this->assertEquals(Auth::tokenVerify($tokens2, Auth::TOKEN_TYPE_RECOVERY, 'false-secret'), false); + $this->assertEquals(Auth::tokenVerify($tokens3, Auth::TOKEN_TYPE_RECOVERY, $secret), false); + $this->assertEquals(Auth::tokenVerify($tokens3, Auth::TOKEN_TYPE_RECOVERY, 'false-secret'), false); } + public function testIsPreviliggedUser() { $this->assertEquals(false, Auth::isPreviliggedUser([])); From 8bc484dfca8d0721c668ec398e1021ad4dbddbf7 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Fri, 19 Feb 2021 14:59:36 +0100 Subject: [PATCH 015/195] fix leftovers --- app/config/collections.php | 2 +- app/controllers/api/account.php | 22 +++++++++++-------- app/controllers/api/teams.php | 1 + app/controllers/api/users.php | 18 +++++++++------ app/workers/deletes.php | 10 ++++++++- .../Utopia/Response/Model/Session.php | 18 +++++++++++++++ 6 files changed, 53 insertions(+), 18 deletions(-) diff --git a/app/config/collections.php b/app/config/collections.php index 92e687cd17..d88bb849aa 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -307,7 +307,7 @@ $collections = [ '$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS, '$id' => Database::SYSTEM_COLLECTION_SESSIONS, '$permissions' => ['read' => ['*']], - 'name' => 'Sessions', + 'name' => 'Session', 'structure' => true, 'rules' => [ [ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index a81e38288a..d37e6f0476 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1054,7 +1054,9 @@ App::delete('/v1/account/sessions/:sessionId') $sessions = $user->getAttribute('sessions', []); - foreach ($sessions as $session) { /** @var Document $session */ + foreach ($sessions as $session) { + /** @var Document $session */ + if (($sessionId == $session->getId())) { if (!$projectDB->deleteDocument($session->getId())) { throw new Exception('Failed to remove token from DB', 500); @@ -1121,10 +1123,12 @@ App::delete('/v1/account/sessions') /** @var Appwrite\Event\Event $events */ $protocol = $request->getProtocol(); - $tokens = $user->getAttribute('tokens', []); + $sessions = $user->getAttribute('sessions', []); - foreach ($tokens as $token) { /* @var $token Document */ - if (!$projectDB->deleteDocument($token->getId())) { + foreach ($sessions as $session) { + /** @var Document $session */ + + if (!$projectDB->deleteDocument($session->getId())) { throw new Exception('Failed to remove token from DB', 500); } @@ -1140,10 +1144,10 @@ App::delete('/v1/account/sessions') ; } - $token->setAttribute('current', false); + $session->setAttribute('current', false); - if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too - $token->setAttribute('current', true); + if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + $session->setAttribute('current', true); $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')) @@ -1153,8 +1157,8 @@ App::delete('/v1/account/sessions') $events ->setParam('payload', $response->output(new Document([ - 'sum' => count($tokens), - 'sessions' => $tokens + 'sum' => count($sessions), + 'sessions' => $sessions ]), Response::MODEL_SESSION_LIST)) ; diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 775d9b47a3..f75f072e76 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -327,6 +327,7 @@ App::post('/v1/teams/:teamId/memberships') 'registration' => \time(), 'reset' => false, 'name' => $name, + 'sessions' => [], 'tokens' => [], ], ['email' => $email]); } catch (Duplicate $th) { diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index b8e79ccb95..4766d0a5f9 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -430,11 +430,13 @@ App::delete('/v1/users/:userId/sessions/:sessionId') throw new Exception('User not found', 404); } - $tokens = $user->getAttribute('tokens', []); + $sessions = $user->getAttribute('sessions', []); - foreach ($tokens as $token) { /* @var $token Document */ - if ($sessionId == $token->getId()) { - if (!$projectDB->deleteDocument($token->getId())) { + foreach ($sessions as $session) { + /** @var Document $session */ + + if ($sessionId == $session->getId()) { + if (!$projectDB->deleteDocument($session->getId())) { throw new Exception('Failed to remove token from DB', 500); } @@ -474,10 +476,12 @@ App::delete('/v1/users/:userId/sessions') throw new Exception('User not found', 404); } - $tokens = $user->getAttribute('tokens', []); + $sessions = $user->getAttribute('sessions', []); - foreach ($tokens as $token) { /* @var $token Document */ - if (!$projectDB->deleteDocument($token->getId())) { + foreach ($sessions as $session) { + /** @var Document $session */ + + if (!$projectDB->deleteDocument($session->getId())) { throw new Exception('Failed to remove token from DB', 500); } } diff --git a/app/workers/deletes.php b/app/workers/deletes.php index 426dcf1ed6..cda60b78e5 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -112,13 +112,21 @@ class DeletesV1 protected function deleteUser(Document $document, $projectId) { $tokens = $document->getAttribute('tokens', []); - + foreach ($tokens as $token) { if (!$this->getProjectDB($projectId)->deleteDocument($token->getId())) { throw new Exception('Failed to remove token from DB'); } } + $sessions = $document->getAttribute('sessions', []); + + foreach ($sessions as $session) { + if (!$this->getProjectDB($projectId)->deleteDocument($session->getId())) { + throw new Exception('Failed to remove session from DB'); + } + } + // Delete Memberships $this->deleteByGroup([ '$collection='.Database::SYSTEM_COLLECTION_MEMBERSHIPS, diff --git a/src/Appwrite/Utopia/Response/Model/Session.php b/src/Appwrite/Utopia/Response/Model/Session.php index 4bc23ab79a..7db7fdee36 100644 --- a/src/Appwrite/Utopia/Response/Model/Session.php +++ b/src/Appwrite/Utopia/Response/Model/Session.php @@ -28,6 +28,24 @@ class Session extends Model 'default' => 0, 'example' => 1592981250, ]) + ->addRule('provider', [ + 'type' => self::TYPE_STRING, + 'description' => 'Session Provider.', + 'default' => 0, + 'example' => 1592981250, + ]) + ->addRule('providerUid', [ + 'type' => self::TYPE_STRING, + 'description' => 'Session Provider User ID.', + 'default' => 0, + 'example' => 1592981250, + ]) + ->addRule('providerToken', [ + 'type' => self::TYPE_STRING, + 'description' => 'Session Provider Token.', + 'default' => 0, + 'example' => 1592981250, + ]) ->addRule('ip', [ 'type' => self::TYPE_STRING, 'description' => 'IP in use when the session was created.', From 70666ddf0a0c65895057cda5fa7333ba61e5dc13 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Fri, 19 Feb 2021 15:41:17 +0100 Subject: [PATCH 016/195] add current to sessions collection --- app/config/collections.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/config/collections.php b/app/config/collections.php index d88bb849aa..6abac72290 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -373,6 +373,15 @@ $collections = [ 'required' => true, 'array' => false, ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Current', + 'key' => 'current', + 'type' => Database::SYSTEM_VAR_TYPE_BOOLEAN, + 'default' => false, + 'required' => true, + 'array' => false, + ], [ '$collection' => Database::SYSTEM_COLLECTION_RULES, 'label' => 'User Agent', From 19b85d7ae69bc3d597ce46fa9795f55e32999eda Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Fri, 19 Feb 2021 15:53:52 +0100 Subject: [PATCH 017/195] revert stupid change --- app/config/collections.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/app/config/collections.php b/app/config/collections.php index 6abac72290..d88bb849aa 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -373,15 +373,6 @@ $collections = [ 'required' => true, 'array' => false, ], - [ - '$collection' => Database::SYSTEM_COLLECTION_RULES, - 'label' => 'Current', - 'key' => 'current', - 'type' => Database::SYSTEM_VAR_TYPE_BOOLEAN, - 'default' => false, - 'required' => true, - 'array' => false, - ], [ '$collection' => Database::SYSTEM_COLLECTION_RULES, 'label' => 'User Agent', From 6dbc3966b2fd44cdb20aa73f1c1a1e647bb5060b Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Mon, 22 Feb 2021 12:27:31 +0100 Subject: [PATCH 018/195] adds migration and migration tests --- src/Appwrite/Migration/Version/V07.php | 70 +++++++++++++++++++++++ tests/unit/Migration/MigrationV07Test.php | 69 ++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 src/Appwrite/Migration/Version/V07.php create mode 100644 tests/unit/Migration/MigrationV07Test.php diff --git a/src/Appwrite/Migration/Version/V07.php b/src/Appwrite/Migration/Version/V07.php new file mode 100644 index 0000000000..0b97a83f80 --- /dev/null +++ b/src/Appwrite/Migration/Version/V07.php @@ -0,0 +1,70 @@ +db; + $project = $this->project; + Console::log('Migrating project: ' . $project->getAttribute('name') . ' (' . $project->getId() . ')'); + + $this->forEachDocument([$this, 'fixDocument']); + } + + protected function fixDocument(Document $document) + { + $providers = Config::getParam('providers'); + + switch ($document->getAttribute('$collection')) { + case Database::SYSTEM_COLLECTION_USERS: + foreach ($providers as $key => $provider) { + /** + * Remove deprecated OAuth2 properties in the Users Documents. + */ + if (!empty($document->getAttribute('oauth2' . \ucfirst($key)))) { + $document->removeAttribute('oauth2' . \ucfirst($key)); + } + + if (!empty($document->getAttribute('oauth2' . \ucfirst($key) . 'AccessToken'))) { + $document->removeAttribute('oauth2' . \ucfirst($key) . 'AccessToken'); + } + + /** + * Invalidate all Login Tokens, since they can't be migrated to the new structure. + * Reason for it is the missing distinction between E-Mail and OAuth2 tokens. + */ + $tokens = array_filter($document->getAttribute('tokens', []), function($token) { + return ($token->getAttribute('type') != Auth::TOKEN_TYPE_LOGIN); + }); + + $document->setAttribute('tokens', array_values($tokens)); + } + break; + } + + foreach ($document as &$attr) { // Handle child documents + if ($attr instanceof Document) { + $attr = $this->fixDocument($attr); + } + + if (\is_array($attr)) { + foreach ($attr as &$child) { + if ($child instanceof Document) { + $child = $this->fixDocument($child); + } + } + } + } + + return $document; + } +} diff --git a/tests/unit/Migration/MigrationV07Test.php b/tests/unit/Migration/MigrationV07Test.php new file mode 100644 index 0000000000..ca73c3229d --- /dev/null +++ b/tests/unit/Migration/MigrationV07Test.php @@ -0,0 +1,69 @@ +pdo = new \PDO('sqlite::memory:'); + $this->migration = new V07($this->pdo); + $reflector = new ReflectionClass('Appwrite\Migration\Version\V07'); + $this->method = $reflector->getMethod('fixDocument'); + $this->method->setAccessible(true); + } + + public function testMigration() + { + $document = $this->fixDocument(new Document([ + '$id' => 'unique', + '$collection' => Database::SYSTEM_COLLECTION_USERS, + 'oauth2Github' => 123, + 'oauth2GithubAccessToken' => 456, + 'tokens' => [ + new Document([ + '$collection' => Database::SYSTEM_COLLECTION_TOKENS, + 'userId' => 'unique', + 'type' => Auth::TOKEN_TYPE_LOGIN, + 'secret' => 'login', + ]), + new Document([ + '$collection' => Database::SYSTEM_COLLECTION_TOKENS, + 'userId' => 'unique', + 'type' => Auth::TOKEN_TYPE_INVITE, + 'secret' => 'invite', + ]), + new Document([ + '$collection' => Database::SYSTEM_COLLECTION_TOKENS, + 'userId' => 'unique', + 'type' => Auth::TOKEN_TYPE_RECOVERY, + 'secret' => 'recovery', + ]), + new Document([ + '$collection' => Database::SYSTEM_COLLECTION_TOKENS, + 'userId' => 'unique', + 'type' => Auth::TOKEN_TYPE_VERIFICATION, + 'secret' => 'verification', + ]), + ] + ])); + + $this->assertEquals($document->getAttribute('oauth2Github', null), null); + $this->assertEquals($document->getAttribute('oauth2GithubAccessToken', null), null); + + $this->assertCount(3, $document->getAttribute('tokens', [])); + $this->assertEquals(Auth::TOKEN_TYPE_INVITE, $document->getAttribute('tokens', [])[0]['type']); + $this->assertEquals(Auth::TOKEN_TYPE_RECOVERY, $document->getAttribute('tokens', [])[1]['type']); + $this->assertEquals(Auth::TOKEN_TYPE_VERIFICATION, $document->getAttribute('tokens', [])[2]['type']); + + } +} From 7aab693d66264b526751c419810d5ea47c06b7de Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Mon, 22 Feb 2021 13:03:34 +0100 Subject: [PATCH 019/195] remove duplicate secret prop from session collection --- app/config/collections.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/app/config/collections.php b/app/config/collections.php index d88bb849aa..130c57bd7c 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -319,15 +319,6 @@ $collections = [ 'required' => true, 'array' => false, ], - [ - '$collection' => Database::SYSTEM_COLLECTION_RULES, - 'label' => 'Secret', - 'key' => 'secret', - 'type' => Database::SYSTEM_VAR_TYPE_TEXT, - 'default' => '', - 'required' => true, - 'array' => false, - ], [ '$collection' => Database::SYSTEM_COLLECTION_RULES, 'label' => 'Provider', From e388cb462b069c2b8f40a6454aadb71f27ae87ab Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Mon, 22 Feb 2021 13:44:47 +0100 Subject: [PATCH 020/195] check duplicate collection rules --- tests/unit/General/CollectionsTest.php | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tests/unit/General/CollectionsTest.php diff --git a/tests/unit/General/CollectionsTest.php b/tests/unit/General/CollectionsTest.php new file mode 100644 index 0000000000..bf7aea7c0c --- /dev/null +++ b/tests/unit/General/CollectionsTest.php @@ -0,0 +1,36 @@ +collections = require('app/config/collections.php'); + } + + public function tearDown(): void + { + } + + public function testDuplicateRules() + { + foreach ($this->collections as $collection) { + if ($collection['rules']) { + foreach ($collection['rules'] as $check) { + $occurences = 0; + foreach ($collection['rules'] as $rule) { + if ($rule['key'] == $check['key']) { + $occurences++; + } + } + $this->assertEquals(1, $occurences); + } + } + } + } +} From e829f8f7bcbb478f311e61f71e393f96874f6d33 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 23 Feb 2021 23:43:05 +0100 Subject: [PATCH 021/195] add env var and console prevention --- .env | 1 + CHANGES.md | 1 + app/config/variables.php | 8 ++++++++ app/controllers/api/account.php | 10 ++++++++-- app/views/install/compose.phtml | 1 + docker-compose.yml | 1 + tests/e2e/Services/Account/AccountCustomClientTest.php | 5 +++-- 7 files changed, 23 insertions(+), 4 deletions(-) diff --git a/.env b/.env index 581af6d978..0f8c2e196d 100644 --- a/.env +++ b/.env @@ -39,3 +39,4 @@ _APP_MAINTENANCE_RETENTION_EXECUTION=1209600 _APP_MAINTENANCE_RETENTION_ABUSE=86400 _APP_MAINTENANCE_RETENTION_AUDIT=1209600 _APP_USAGE_STATS=enabled +_APP_LOGIN_ANONYMOUS=enabled diff --git a/CHANGES.md b/CHANGES.md index 5b10ab43c4..a822b68e70 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ ## Features - Added Anonymous Login ([RFC-010](https://github.com/appwrite/rfc/blob/main/010-anonymous-login.md)) +- Added new Environment Variable to enable or disable Anonymous Login # Version 0.7.0 diff --git a/app/config/variables.php b/app/config/variables.php index e842283c2c..62d9d302ac 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -119,6 +119,14 @@ return [ 'required' => false, 'question' => '', ], + [ + 'name' => '_APP_LOGIN_ANONYMOUS', + 'description' => 'This variable allows you to enable anonymous login.', + 'introduction' => '0.8.0', + 'default' => 'enabled', + 'required' => false, + 'question' => '', + ], ], ], [ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index d90118d906..6bd6dcdd95 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -591,21 +591,27 @@ App::post('/v1/account/sessions/anonymous') ->inject('response') ->inject('locale') ->inject('user') + ->inject('project') ->inject('projectDB') ->inject('geodb') ->inject('audits') - ->action(function ($request, $response, $locale, $user, $projectDB, $geodb, $audits) { + ->action(function ($request, $response, $locale, $user, $project, $projectDB, $geodb, $audits) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Locale\Locale $locale */ /** @var Appwrite\Database\Document $user */ + /** @var Appwrite\Database\Document $project */ /** @var Appwrite\Database\Database $projectDB */ /** @var MaxMind\Db\Reader $geodb */ /** @var Appwrite\Event\Event $audits */ $protocol = $request->getProtocol(); - if ($user->getId()) { + if(App::getEnv('_APP_LOGIN_ANONYMOUS', 'enabled') !== 'enabled') { + throw new Exception('Anonymous login is disabled.', 412); + } + + if ($user->getId() || 'console' === $project->getId()) { throw new Exception('Failed to create anonymous user.', 401); } diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index b98642fb94..daee965c5e 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -89,6 +89,7 @@ services: - _APP_FUNCTIONS_MEMORY - _APP_FUNCTIONS_MEMORY_SWAP - _APP_FUNCTIONS_ENVS + - _APP_LOGIN_ANONYMOUS appwrite-worker-usage: image: appwrite/appwrite: diff --git a/docker-compose.yml b/docker-compose.yml index c682ac839e..3b2260a20d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -109,6 +109,7 @@ services: - _APP_FUNCTIONS_MEMORY - _APP_FUNCTIONS_MEMORY_SWAP - _APP_FUNCTIONS_ENVS + - _APP_LOGIN_ANONYMOUS appwrite-worker-usage: entrypoint: worker-usage diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index cf1af8749a..e1dedd6be9 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -6,6 +6,7 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\SideClient; +use Utopia\App; class AccountCustomClientTest extends Scope { @@ -238,7 +239,7 @@ class AccountCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ]); - $this->assertEquals($response['headers']['status-code'], 201); + $this->assertEquals(201, $response['headers']['status-code']); $session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$this->getProject()['$id']]; @@ -252,7 +253,7 @@ class AccountCustomClientTest extends Scope 'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session, ]); - $this->assertEquals($response['headers']['status-code'], 401); + $this->assertEquals(401, $response['headers']['status-code']); return $session; } From 945d7947a2f84e32b37cad685cd59c2990a88aea Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 3 Mar 2021 11:08:05 +0200 Subject: [PATCH 022/195] Updated SDKs --- app/config/platforms.php | 12 +- app/tasks/sdks.php | 10 + composer.json | 2 +- composer.lock | 194 +++++++++--------- .../examples/account/create-o-auth2session.md | 3 +- .../examples/account/create-recovery.md | 3 +- .../examples/account/create-session.md | 3 +- .../examples/account/create-verification.md | 3 +- .../client-flutter/examples/account/create.md | 3 +- .../examples/account/delete-session.md | 3 +- .../examples/account/delete-sessions.md | 3 +- .../client-flutter/examples/account/delete.md | 3 +- .../examples/account/get-logs.md | 3 +- .../examples/account/get-prefs.md | 3 +- .../examples/account/get-sessions.md | 3 +- .../client-flutter/examples/account/get.md | 3 +- .../examples/account/update-email.md | 3 +- .../examples/account/update-name.md | 3 +- .../examples/account/update-password.md | 3 +- .../examples/account/update-prefs.md | 3 +- .../examples/account/update-recovery.md | 3 +- .../examples/account/update-verification.md | 3 +- .../examples/avatars/get-browser.md | 18 +- .../examples/avatars/get-credit-card.md | 18 +- .../examples/avatars/get-favicon.md | 18 +- .../examples/avatars/get-flag.md | 18 +- .../examples/avatars/get-image.md | 18 +- .../examples/avatars/get-initials.md | 18 +- .../examples/avatars/get-q-r.md | 18 +- .../examples/database/create-document.md | 3 +- .../examples/database/delete-document.md | 3 +- .../examples/database/get-document.md | 3 +- .../examples/database/list-documents.md | 3 +- .../examples/database/update-document.md | 3 +- .../examples/functions/create-execution.md | 3 +- .../examples/functions/get-execution.md | 3 +- .../examples/functions/list-executions.md | 3 +- .../examples/locale/get-continents.md | 3 +- .../examples/locale/get-countries-e-u.md | 3 +- .../examples/locale/get-countries-phones.md | 3 +- .../examples/locale/get-countries.md | 3 +- .../examples/locale/get-currencies.md | 3 +- .../examples/locale/get-languages.md | 3 +- .../client-flutter/examples/locale/get.md | 3 +- .../examples/storage/create-file.md | 3 +- .../examples/storage/delete-file.md | 3 +- .../examples/storage/get-file-download.md | 18 +- .../examples/storage/get-file-preview.md | 18 +- .../examples/storage/get-file-view.md | 18 +- .../examples/storage/get-file.md | 3 +- .../examples/storage/list-files.md | 3 +- .../examples/storage/update-file.md | 3 +- .../examples/teams/create-membership.md | 3 +- .../client-flutter/examples/teams/create.md | 3 +- .../examples/teams/delete-membership.md | 3 +- .../client-flutter/examples/teams/delete.md | 3 +- .../examples/teams/get-memberships.md | 3 +- .../client-flutter/examples/teams/get.md | 3 +- .../client-flutter/examples/teams/list.md | 3 +- .../teams/update-membership-status.md | 3 +- .../client-flutter/examples/teams/update.md | 3 +- .../examples/avatars/get-browser.md | 9 +- .../examples/avatars/get-credit-card.md | 9 +- .../examples/avatars/get-favicon.md | 9 +- .../server-dart/examples/avatars/get-flag.md | 9 +- .../server-dart/examples/avatars/get-image.md | 9 +- .../examples/avatars/get-initials.md | 9 +- .../server-dart/examples/avatars/get-q-r.md | 9 +- .../examples/storage/get-file-download.md | 9 +- .../examples/storage/get-file-preview.md | 9 +- .../examples/storage/get-file-view.md | 9 +- docs/sdks/dart/CHANGELOG.md | 10 + docs/sdks/flutter/CHANGELOG.md | 10 + 73 files changed, 381 insertions(+), 268 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 399d52e74b..4d8e5506fe 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -32,7 +32,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '0.3.0', + 'version' => '0.4.0-dev.1', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -165,7 +165,7 @@ return [ [ 'key' => 'deno', 'name' => 'Deno', - 'version' => '0.1.0', + 'version' => '0.1.1', 'url' => 'https://github.com/appwrite/sdk-for-deno', 'package' => 'https://deno.land/x/appwrite', 'enabled' => true, @@ -199,7 +199,7 @@ return [ [ 'key' => 'python', 'name' => 'Python', - 'version' => '0.1.0', + 'version' => '0.1.1', 'url' => 'https://github.com/appwrite/sdk-for-python', 'package' => 'https://pypi.org/project/appwrite/', 'enabled' => true, @@ -216,7 +216,7 @@ return [ [ 'key' => 'ruby', 'name' => 'Ruby', - 'version' => '2.0.0', + 'version' => '2.0.2', 'url' => 'https://github.com/appwrite/sdk-for-ruby', 'package' => 'https://rubygems.org/gems/appwrite', 'enabled' => true, @@ -284,7 +284,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '0.2.0', + 'version' => '0.3.0', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, @@ -301,7 +301,7 @@ return [ [ 'key' => 'cli', 'name' => 'Command Line', - 'version' => '0.5.0', + 'version' => '0.6.0', 'url' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://github.com/appwrite/sdk-for-cli', 'enabled' => true, diff --git a/app/tasks/sdks.php b/app/tasks/sdks.php index 6b17104f6c..f8d4ed3976 100644 --- a/app/tasks/sdks.php +++ b/app/tasks/sdks.php @@ -96,6 +96,16 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND $config = new CLI(); $config->setComposerVendor('appwrite'); $config->setComposerPackage('cli'); + $config->setExecutableName('appwrite'); + $config->setExecutableName('appwrite'); + $config->setLogo(" + _ _ _ ___ __ _____ + /_\ _ __ _ ____ ___ __(_) |_ ___ / __\ / / \_ \ + //_\\| '_ \| '_ \ \ /\ / / '__| | __/ _ \ / / / / / /\/ + / _ \ |_) | |_) \ V V /| | | | || __/ / /___/ /___/\/ /_ + \_/ \_/ .__/| .__/ \_/\_/ |_| |_|\__\___| \____/\____/\____/ + |_| |_| + "); break; case 'php': $config = new PHP(); diff --git a/composer.json b/composer.json index f82d5c3503..cc4cdf9098 100644 --- a/composer.json +++ b/composer.json @@ -59,7 +59,7 @@ "adhocore/jwt": "1.1.0" }, "require-dev": { - "appwrite/sdk-generator": "0.5.5", + "appwrite/sdk-generator": "0.6.1", "phpunit/phpunit": "9.4.2", "swoole/ide-helper": "4.5.5", "vimeo/psalm": "4.1.1" diff --git a/composer.lock b/composer.lock index 075cf1359c..9b252dcd05 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "5893b378d1dcda91aedf77059f4b0efb", + "content-hash": "4b03aefd02908c32c2d1be6127c8c23a", "packages": [ { "name": "adhocore/jwt", @@ -349,7 +349,7 @@ "issues": "https://github.com/domnikl/statsd-php/issues", "source": "https://github.com/domnikl/statsd-php/tree/master" }, - "abandoned": true, + "abandoned": "slickdeals/statsd", "time": "2020-01-03T14:24:58+00:00" }, { @@ -1004,12 +1004,12 @@ "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "dd738d0b4491f32725492cf345f6b501f5922fec" + "reference": "a18c1e692e02b84abbafe4856c3cd7cc6903908c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/dd738d0b4491f32725492cf345f6b501f5922fec", - "reference": "dd738d0b4491f32725492cf345f6b501f5922fec", + "url": "https://api.github.com/repos/php-fig/log/zipball/a18c1e692e02b84abbafe4856c3cd7cc6903908c", + "reference": "a18c1e692e02b84abbafe4856c3cd7cc6903908c", "shasum": "" }, "require": { @@ -1047,7 +1047,7 @@ "support": { "source": "https://github.com/php-fig/log/tree/master" }, - "time": "2020-09-18T06:44:51+00:00" + "time": "2021-03-02T15:02:34+00:00" }, { "name": "ralouphie/getallheaders", @@ -2086,11 +2086,11 @@ }, { "name": "appwrite/sdk-generator", - "version": "0.5.5", + "version": "0.6.1", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator", - "reference": "08d6dc72f83ec99cfc0f7a50cb7fc0be0c029ac1" + "reference": "8a55e7082428583a89ece31df463663c2b2320d8" }, "require": { "ext-curl": "*", @@ -2120,7 +2120,7 @@ } ], "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", - "time": "2021-02-18T16:37:32+00:00" + "time": "2021-03-02T17:02:58+00:00" }, { "name": "composer/package-versions-deprecated", @@ -2498,12 +2498,12 @@ "source": { "type": "git", "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "85e83cacd2ed573238678c6875f8f0d7ec699541" + "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/85e83cacd2ed573238678c6875f8f0d7ec699541", - "reference": "85e83cacd2ed573238678c6875f8f0d7ec699541", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/9d846d1f5cf101deee7a61c8ba7caa0a975cd730", + "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730", "shasum": "" }, "require": { @@ -2545,9 +2545,9 @@ ], "support": { "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.0" + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1" }, - "time": "2020-10-23T13:55:30+00:00" + "time": "2021-02-22T14:02:09+00:00" }, { "name": "matthiasmullie/minify", @@ -2962,16 +2962,16 @@ }, { "name": "phar-io/version", - "version": "3.0.4", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "e4782611070e50613683d2b9a57730e9a3ba5451" + "reference": "bae7c545bef187884426f042434e561ab1ddb182" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/e4782611070e50613683d2b9a57730e9a3ba5451", - "reference": "e4782611070e50613683d2b9a57730e9a3ba5451", + "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", + "reference": "bae7c545bef187884426f042434e561ab1ddb182", "shasum": "" }, "require": { @@ -3007,9 +3007,9 @@ "description": "Library for handling version information and constraints", "support": { "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.0.4" + "source": "https://github.com/phar-io/version/tree/3.1.0" }, - "time": "2020-12-13T23:18:30+00:00" + "time": "2021-02-23T14:00:09+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -3322,12 +3322,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "05fa32de35b15c94838d22482cc59d99860a706f" + "reference": "dae425925709122f7584cadeeb838edcaa491bb1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/05fa32de35b15c94838d22482cc59d99860a706f", - "reference": "05fa32de35b15c94838d22482cc59d99860a706f", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/dae425925709122f7584cadeeb838edcaa491bb1", + "reference": "dae425925709122f7584cadeeb838edcaa491bb1", "shasum": "" }, "require": { @@ -3375,7 +3375,7 @@ "type": "github" } ], - "time": "2021-02-14T06:52:34+00:00" + "time": "2021-02-23T15:48:43+00:00" }, { "name": "phpunit/php-invoker", @@ -3383,12 +3383,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "7bba8d62fc6140730c268d5ff7fbf9c3a54996a8" + "reference": "5ad9e5f5d6ee1a837e1d50bab1017e0daf423b40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/7bba8d62fc6140730c268d5ff7fbf9c3a54996a8", - "reference": "7bba8d62fc6140730c268d5ff7fbf9c3a54996a8", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5ad9e5f5d6ee1a837e1d50bab1017e0daf423b40", + "reference": "5ad9e5f5d6ee1a837e1d50bab1017e0daf423b40", "shasum": "" }, "require": { @@ -3439,7 +3439,7 @@ "type": "github" } ], - "time": "2021-02-14T06:52:42+00:00" + "time": "2021-02-23T15:48:51+00:00" }, { "name": "phpunit/php-text-template", @@ -3447,12 +3447,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "bca9f27936ccd6d7450f16f1ee3f125b755b7905" + "reference": "4ec5a2ac79a19b35d0cf83cce30604f77743067a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/bca9f27936ccd6d7450f16f1ee3f125b755b7905", - "reference": "bca9f27936ccd6d7450f16f1ee3f125b755b7905", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/4ec5a2ac79a19b35d0cf83cce30604f77743067a", + "reference": "4ec5a2ac79a19b35d0cf83cce30604f77743067a", "shasum": "" }, "require": { @@ -3499,7 +3499,7 @@ "type": "github" } ], - "time": "2021-02-14T06:53:15+00:00" + "time": "2021-02-23T15:49:24+00:00" }, { "name": "phpunit/php-timer", @@ -3507,12 +3507,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "e3125d0dc516e7f7ab23d54ddefbce67627fd608" + "reference": "705821b0927b5e69e9e016c84de68dc6195c71b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e3125d0dc516e7f7ab23d54ddefbce67627fd608", - "reference": "e3125d0dc516e7f7ab23d54ddefbce67627fd608", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/705821b0927b5e69e9e016c84de68dc6195c71b9", + "reference": "705821b0927b5e69e9e016c84de68dc6195c71b9", "shasum": "" }, "require": { @@ -3559,7 +3559,7 @@ "type": "github" } ], - "time": "2021-02-14T06:52:50+00:00" + "time": "2021-02-23T15:48:59+00:00" }, { "name": "phpunit/phpunit", @@ -3724,12 +3724,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "5a6fc83d266e0fcbf890d4475bfbb713dbb4d202" + "reference": "3a42d843af4d27ca1155e1d926881af162733655" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/5a6fc83d266e0fcbf890d4475bfbb713dbb4d202", - "reference": "5a6fc83d266e0fcbf890d4475bfbb713dbb4d202", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/3a42d843af4d27ca1155e1d926881af162733655", + "reference": "3a42d843af4d27ca1155e1d926881af162733655", "shasum": "" }, "require": { @@ -3773,7 +3773,7 @@ "type": "github" } ], - "time": "2021-02-14T06:53:40+00:00" + "time": "2021-02-23T15:49:50+00:00" }, { "name": "sebastian/code-unit", @@ -3837,12 +3837,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "96fc758350a824cf96f9e7847ecdf9bb82c87083" + "reference": "5f5db0b35f586eb5bca0581a10bb42dd56575986" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/96fc758350a824cf96f9e7847ecdf9bb82c87083", - "reference": "96fc758350a824cf96f9e7847ecdf9bb82c87083", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5f5db0b35f586eb5bca0581a10bb42dd56575986", + "reference": "5f5db0b35f586eb5bca0581a10bb42dd56575986", "shasum": "" }, "require": { @@ -3885,7 +3885,7 @@ "type": "github" } ], - "time": "2021-02-14T06:51:27+00:00" + "time": "2021-02-23T15:47:39+00:00" }, { "name": "sebastian/comparator", @@ -3893,12 +3893,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "3b943ec66244e5d0a5252708d1c9073ae6d3efc9" + "reference": "dbc5fb421f242a5749845dc8dd0dc8cde2979dd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/3b943ec66244e5d0a5252708d1c9073ae6d3efc9", - "reference": "3b943ec66244e5d0a5252708d1c9073ae6d3efc9", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/dbc5fb421f242a5749845dc8dd0dc8cde2979dd9", + "reference": "dbc5fb421f242a5749845dc8dd0dc8cde2979dd9", "shasum": "" }, "require": { @@ -3960,7 +3960,7 @@ "type": "github" } ], - "time": "2021-02-14T06:51:35+00:00" + "time": "2021-02-23T15:47:47+00:00" }, { "name": "sebastian/complexity", @@ -4025,12 +4025,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "1895a1a29e197f7d31099a320b2a3ae9e428b21d" + "reference": "93e6aa13f3dc5f8327e7fb9756e9655fc4c23e90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/1895a1a29e197f7d31099a320b2a3ae9e428b21d", - "reference": "1895a1a29e197f7d31099a320b2a3ae9e428b21d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/93e6aa13f3dc5f8327e7fb9756e9655fc4c23e90", + "reference": "93e6aa13f3dc5f8327e7fb9756e9655fc4c23e90", "shasum": "" }, "require": { @@ -4084,7 +4084,7 @@ "type": "github" } ], - "time": "2021-02-14T06:51:43+00:00" + "time": "2021-02-23T15:47:55+00:00" }, { "name": "sebastian/environment", @@ -4092,12 +4092,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "7f8f2720df4d03d4368edadac24c3a7950b6cdc5" + "reference": "6e1743b808be9cfd33a716583ccb94b7d4d32e94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/7f8f2720df4d03d4368edadac24c3a7950b6cdc5", - "reference": "7f8f2720df4d03d4368edadac24c3a7950b6cdc5", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e1743b808be9cfd33a716583ccb94b7d4d32e94", + "reference": "6e1743b808be9cfd33a716583ccb94b7d4d32e94", "shasum": "" }, "require": { @@ -4148,7 +4148,7 @@ "type": "github" } ], - "time": "2021-02-14T06:51:52+00:00" + "time": "2021-02-23T15:48:03+00:00" }, { "name": "sebastian/exporter", @@ -4156,12 +4156,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "c6819d6edff3496f28c29a9ed61c564a9fdae27b" + "reference": "eca7281ab29075df68b113a37a83be616b629b12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c6819d6edff3496f28c29a9ed61c564a9fdae27b", - "reference": "c6819d6edff3496f28c29a9ed61c564a9fdae27b", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/eca7281ab29075df68b113a37a83be616b629b12", + "reference": "eca7281ab29075df68b113a37a83be616b629b12", "shasum": "" }, "require": { @@ -4226,7 +4226,7 @@ "type": "github" } ], - "time": "2021-02-14T06:52:00+00:00" + "time": "2021-02-23T15:48:12+00:00" }, { "name": "sebastian/global-state", @@ -4234,12 +4234,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "a912746c9e31610f52b8e6977107e745c758cfd8" + "reference": "0ac702e6d13725242edb9b294c5d20b92fcfb8b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a912746c9e31610f52b8e6977107e745c758cfd8", - "reference": "a912746c9e31610f52b8e6977107e745c758cfd8", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ac702e6d13725242edb9b294c5d20b92fcfb8b4", + "reference": "0ac702e6d13725242edb9b294c5d20b92fcfb8b4", "shasum": "" }, "require": { @@ -4291,7 +4291,7 @@ "type": "github" } ], - "time": "2021-02-14T06:52:09+00:00" + "time": "2021-02-23T15:48:19+00:00" }, { "name": "sebastian/lines-of-code", @@ -4356,12 +4356,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "79f258bf9b9f9f1aff7ec27fa3e0d5d7ef344088" + "reference": "8cc80b4bda00a4c5997c3fc597a34872f3a1007d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/79f258bf9b9f9f1aff7ec27fa3e0d5d7ef344088", - "reference": "79f258bf9b9f9f1aff7ec27fa3e0d5d7ef344088", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/8cc80b4bda00a4c5997c3fc597a34872f3a1007d", + "reference": "8cc80b4bda00a4c5997c3fc597a34872f3a1007d", "shasum": "" }, "require": { @@ -4406,7 +4406,7 @@ "type": "github" } ], - "time": "2021-02-14T06:52:17+00:00" + "time": "2021-02-23T15:48:28+00:00" }, { "name": "sebastian/object-reflector", @@ -4414,12 +4414,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "232add5a51167e359e1dd03334ebffaddfb95795" + "reference": "1d33587c2c3e636936f895e103a9e82dd8102a8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/232add5a51167e359e1dd03334ebffaddfb95795", - "reference": "232add5a51167e359e1dd03334ebffaddfb95795", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/1d33587c2c3e636936f895e103a9e82dd8102a8e", + "reference": "1d33587c2c3e636936f895e103a9e82dd8102a8e", "shasum": "" }, "require": { @@ -4462,7 +4462,7 @@ "type": "github" } ], - "time": "2021-02-14T06:52:26+00:00" + "time": "2021-02-23T15:48:35+00:00" }, { "name": "sebastian/recursion-context", @@ -4470,12 +4470,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "d6cde15be46e8e5cc8671ceb41b63b69dfd7bd5a" + "reference": "43f58a51e8f853aadb228ba818d2be388af7237b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/d6cde15be46e8e5cc8671ceb41b63b69dfd7bd5a", - "reference": "d6cde15be46e8e5cc8671ceb41b63b69dfd7bd5a", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/43f58a51e8f853aadb228ba818d2be388af7237b", + "reference": "43f58a51e8f853aadb228ba818d2be388af7237b", "shasum": "" }, "require": { @@ -4526,7 +4526,7 @@ "type": "github" } ], - "time": "2021-02-14T06:52:58+00:00" + "time": "2021-02-23T15:49:08+00:00" }, { "name": "sebastian/resource-operations", @@ -4590,12 +4590,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "8abc9c1947c9f928da999be28778a0ba48cdf5b4" + "reference": "557863473c1de00e165a288d5b547f1f83652e7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/8abc9c1947c9f928da999be28778a0ba48cdf5b4", - "reference": "8abc9c1947c9f928da999be28778a0ba48cdf5b4", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/557863473c1de00e165a288d5b547f1f83652e7e", + "reference": "557863473c1de00e165a288d5b547f1f83652e7e", "shasum": "" }, "require": { @@ -4639,7 +4639,7 @@ "type": "github" } ], - "time": "2021-02-14T06:53:07+00:00" + "time": "2021-02-23T15:49:16+00:00" }, { "name": "sebastian/version", @@ -4738,12 +4738,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "2a6f75224a537ee506e9fa1e6fc4200ad411ffd9" + "reference": "c08d7d0d458eceb62996d81d3be8d9fbf5564ec4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/2a6f75224a537ee506e9fa1e6fc4200ad411ffd9", - "reference": "2a6f75224a537ee506e9fa1e6fc4200ad411ffd9", + "url": "https://api.github.com/repos/symfony/console/zipball/c08d7d0d458eceb62996d81d3be8d9fbf5564ec4", + "reference": "c08d7d0d458eceb62996d81d3be8d9fbf5564ec4", "shasum": "" }, "require": { @@ -4828,7 +4828,7 @@ "type": "tidelift" } ], - "time": "2021-02-17T15:27:35+00:00" + "time": "2021-02-23T10:10:15+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5328,12 +5328,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "bf99754c6182a126968b1c2709d18548489f27eb" + "reference": "e830e6ceebd6377b019e4c9a523d6f2c27007e4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bf99754c6182a126968b1c2709d18548489f27eb", - "reference": "bf99754c6182a126968b1c2709d18548489f27eb", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e830e6ceebd6377b019e4c9a523d6f2c27007e4a", + "reference": "e830e6ceebd6377b019e4c9a523d6f2c27007e4a", "shasum": "" }, "require": { @@ -5347,7 +5347,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.3-dev" + "dev-main": "2.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -5400,7 +5400,7 @@ "type": "tidelift" } ], - "time": "2021-01-27T16:27:53+00:00" + "time": "2021-02-25T16:38:04+00:00" }, { "name": "symfony/string", @@ -5542,12 +5542,12 @@ "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "429f90a02d3bd4a06787ac9bc48c56c4320b58a0" + "reference": "728c611e8643a5dd44839ffa791e21763b04a694" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/429f90a02d3bd4a06787ac9bc48c56c4320b58a0", - "reference": "429f90a02d3bd4a06787ac9bc48c56c4320b58a0", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/728c611e8643a5dd44839ffa791e21763b04a694", + "reference": "728c611e8643a5dd44839ffa791e21763b04a694", "shasum": "" }, "require": { @@ -5613,7 +5613,7 @@ "type": "tidelift" } ], - "time": "2021-02-08T09:50:07+00:00" + "time": "2021-02-22T11:56:05+00:00" }, { "name": "vimeo/psalm", @@ -5726,12 +5726,12 @@ "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "9c89b265ccc4092d58e66d72af5d343ee77a41ae" + "reference": "4631e2c7d2d7132adac9fd84d4c1a98c10a6e049" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/9c89b265ccc4092d58e66d72af5d343ee77a41ae", - "reference": "9c89b265ccc4092d58e66d72af5d343ee77a41ae", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/4631e2c7d2d7132adac9fd84d4c1a98c10a6e049", + "reference": "4631e2c7d2d7132adac9fd84d4c1a98c10a6e049", "shasum": "" }, "require": { @@ -5777,7 +5777,7 @@ "issues": "https://github.com/webmozarts/assert/issues", "source": "https://github.com/webmozarts/assert/tree/master" }, - "time": "2021-01-18T12:52:36+00:00" + "time": "2021-02-28T20:01:57+00:00" }, { "name": "webmozart/path-util", diff --git a/docs/examples/0.7.0/client-flutter/examples/account/create-o-auth2session.md b/docs/examples/0.7.0/client-flutter/examples/account/create-o-auth2session.md index 0e26dd27af..a100378a00 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/create-o-auth2session.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/create-o-auth2session.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.createOAuth2Session( provider: 'amazon', ); @@ -19,4 +18,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/create-recovery.md b/docs/examples/0.7.0/client-flutter/examples/account/create-recovery.md index 50db45b15d..2a5df1aa49 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/create-recovery.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/create-recovery.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.createRecovery( email: 'email@example.com', url: 'https://example.com', @@ -20,4 +19,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/create-session.md b/docs/examples/0.7.0/client-flutter/examples/account/create-session.md index ef824b4eca..6233665a43 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/create-session.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/create-session.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.createSession( email: 'email@example.com', password: 'password', @@ -20,4 +19,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/create-verification.md b/docs/examples/0.7.0/client-flutter/examples/account/create-verification.md index 6b9b1ace40..18fa98aed2 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/create-verification.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/create-verification.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.createVerification( url: 'https://example.com', ); @@ -19,4 +18,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/create.md b/docs/examples/0.7.0/client-flutter/examples/account/create.md index 384258ae68..33f7169477 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/create.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/create.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.create( email: 'email@example.com', password: 'password', @@ -20,4 +19,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/delete-session.md b/docs/examples/0.7.0/client-flutter/examples/account/delete-session.md index 80a3f505d0..c116ef1188 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/delete-session.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/delete-session.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.deleteSession( sessionId: '[SESSION_ID]', ); @@ -19,4 +18,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/delete-sessions.md b/docs/examples/0.7.0/client-flutter/examples/account/delete-sessions.md index 74e494db25..0d84b9d121 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/delete-sessions.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/delete-sessions.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.deleteSessions(); result @@ -17,4 +16,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/delete.md b/docs/examples/0.7.0/client-flutter/examples/account/delete.md index 2d9d302af0..c08891e787 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/delete.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/delete.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.delete(); result @@ -17,4 +16,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/get-logs.md b/docs/examples/0.7.0/client-flutter/examples/account/get-logs.md index 4240e55eec..82da1f46ef 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/get-logs.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/get-logs.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.getLogs(); result @@ -17,4 +16,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/get-prefs.md b/docs/examples/0.7.0/client-flutter/examples/account/get-prefs.md index d98dcd7290..2d6efd54db 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/get-prefs.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/get-prefs.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.getPrefs(); result @@ -17,4 +16,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/get-sessions.md b/docs/examples/0.7.0/client-flutter/examples/account/get-sessions.md index b5ecd34e45..a44eac144b 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/get-sessions.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/get-sessions.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.getSessions(); result @@ -17,4 +16,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/get.md b/docs/examples/0.7.0/client-flutter/examples/account/get.md index a77fbf3ad5..733d8842ce 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/get.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/get.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.get(); result @@ -17,4 +16,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/update-email.md b/docs/examples/0.7.0/client-flutter/examples/account/update-email.md index 4a1a002db4..30dff9b7c9 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/update-email.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/update-email.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.updateEmail( email: 'email@example.com', password: 'password', @@ -20,4 +19,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/update-name.md b/docs/examples/0.7.0/client-flutter/examples/account/update-name.md index fa432bb34a..01ff7a0b62 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/update-name.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/update-name.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.updateName( name: '[NAME]', ); @@ -19,4 +18,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/update-password.md b/docs/examples/0.7.0/client-flutter/examples/account/update-password.md index 9bd8ad9b77..203dccb2bf 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/update-password.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/update-password.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.updatePassword( password: 'password', oldPassword: 'password', @@ -20,4 +19,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/update-prefs.md b/docs/examples/0.7.0/client-flutter/examples/account/update-prefs.md index 443252f89a..bb7b8f800e 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/update-prefs.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/update-prefs.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.updatePrefs( prefs: {}, ); @@ -19,4 +18,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/update-recovery.md b/docs/examples/0.7.0/client-flutter/examples/account/update-recovery.md index 5140235d49..df9f144600 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/update-recovery.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/update-recovery.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.updateRecovery( userId: '[USER_ID]', secret: '[SECRET]', @@ -22,4 +21,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/account/update-verification.md b/docs/examples/0.7.0/client-flutter/examples/account/update-verification.md index b795cde92d..2bfbaed75c 100644 --- a/docs/examples/0.7.0/client-flutter/examples/account/update-verification.md +++ b/docs/examples/0.7.0/client-flutter/examples/account/update-verification.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = account.updateVerification( userId: '[USER_ID]', secret: '[SECRET]', @@ -20,4 +19,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/avatars/get-browser.md b/docs/examples/0.7.0/client-flutter/examples/avatars/get-browser.md index 464767c4c0..474b4a2097 100644 --- a/docs/examples/0.7.0/client-flutter/examples/avatars/get-browser.md +++ b/docs/examples/0.7.0/client-flutter/examples/avatars/get-browser.md @@ -8,10 +8,18 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; +} - String result = avatars.getBrowser( +//displaying image +FutureBuilder( + future: avatars.getBrowser( code: 'aa', - ); - - print(result); // Resource URL string -} \ No newline at end of file + ), //works for both public file and private file, for private files you need to be logged in + builder: (context, snapshot) { + return snapshot.hasData && snapshot.data != null + ? Image.memory( + snapshot.data.data, + ) + : CircularProgressIndicator(); + }, +); diff --git a/docs/examples/0.7.0/client-flutter/examples/avatars/get-credit-card.md b/docs/examples/0.7.0/client-flutter/examples/avatars/get-credit-card.md index 482c642402..5611b0ba9a 100644 --- a/docs/examples/0.7.0/client-flutter/examples/avatars/get-credit-card.md +++ b/docs/examples/0.7.0/client-flutter/examples/avatars/get-credit-card.md @@ -8,10 +8,18 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; +} - String result = avatars.getCreditCard( +//displaying image +FutureBuilder( + future: avatars.getCreditCard( code: 'amex', - ); - - print(result); // Resource URL string -} \ No newline at end of file + ), //works for both public file and private file, for private files you need to be logged in + builder: (context, snapshot) { + return snapshot.hasData && snapshot.data != null + ? Image.memory( + snapshot.data.data, + ) + : CircularProgressIndicator(); + }, +); diff --git a/docs/examples/0.7.0/client-flutter/examples/avatars/get-favicon.md b/docs/examples/0.7.0/client-flutter/examples/avatars/get-favicon.md index 60397b0af2..5e1aad08ed 100644 --- a/docs/examples/0.7.0/client-flutter/examples/avatars/get-favicon.md +++ b/docs/examples/0.7.0/client-flutter/examples/avatars/get-favicon.md @@ -8,10 +8,18 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; +} - String result = avatars.getFavicon( +//displaying image +FutureBuilder( + future: avatars.getFavicon( url: 'https://example.com', - ); - - print(result); // Resource URL string -} \ No newline at end of file + ), //works for both public file and private file, for private files you need to be logged in + builder: (context, snapshot) { + return snapshot.hasData && snapshot.data != null + ? Image.memory( + snapshot.data.data, + ) + : CircularProgressIndicator(); + }, +); diff --git a/docs/examples/0.7.0/client-flutter/examples/avatars/get-flag.md b/docs/examples/0.7.0/client-flutter/examples/avatars/get-flag.md index a9f7a711d9..f729dccdd7 100644 --- a/docs/examples/0.7.0/client-flutter/examples/avatars/get-flag.md +++ b/docs/examples/0.7.0/client-flutter/examples/avatars/get-flag.md @@ -8,10 +8,18 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; +} - String result = avatars.getFlag( +//displaying image +FutureBuilder( + future: avatars.getFlag( code: 'af', - ); - - print(result); // Resource URL string -} \ No newline at end of file + ), //works for both public file and private file, for private files you need to be logged in + builder: (context, snapshot) { + return snapshot.hasData && snapshot.data != null + ? Image.memory( + snapshot.data.data, + ) + : CircularProgressIndicator(); + }, +); diff --git a/docs/examples/0.7.0/client-flutter/examples/avatars/get-image.md b/docs/examples/0.7.0/client-flutter/examples/avatars/get-image.md index 7cead1cb2f..21ac6f80f0 100644 --- a/docs/examples/0.7.0/client-flutter/examples/avatars/get-image.md +++ b/docs/examples/0.7.0/client-flutter/examples/avatars/get-image.md @@ -8,10 +8,18 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; +} - String result = avatars.getImage( +//displaying image +FutureBuilder( + future: avatars.getImage( url: 'https://example.com', - ); - - print(result); // Resource URL string -} \ No newline at end of file + ), //works for both public file and private file, for private files you need to be logged in + builder: (context, snapshot) { + return snapshot.hasData && snapshot.data != null + ? Image.memory( + snapshot.data.data, + ) + : CircularProgressIndicator(); + }, +); diff --git a/docs/examples/0.7.0/client-flutter/examples/avatars/get-initials.md b/docs/examples/0.7.0/client-flutter/examples/avatars/get-initials.md index b2e70788d6..6ee824559d 100644 --- a/docs/examples/0.7.0/client-flutter/examples/avatars/get-initials.md +++ b/docs/examples/0.7.0/client-flutter/examples/avatars/get-initials.md @@ -8,9 +8,17 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; +} - String result = avatars.getInitials( - ); - - print(result); // Resource URL string -} \ No newline at end of file +//displaying image +FutureBuilder( + future: avatars.getInitials( + ), //works for both public file and private file, for private files you need to be logged in + builder: (context, snapshot) { + return snapshot.hasData && snapshot.data != null + ? Image.memory( + snapshot.data.data, + ) + : CircularProgressIndicator(); + }, +); diff --git a/docs/examples/0.7.0/client-flutter/examples/avatars/get-q-r.md b/docs/examples/0.7.0/client-flutter/examples/avatars/get-q-r.md index a5bcfd2c05..5c828ba98b 100644 --- a/docs/examples/0.7.0/client-flutter/examples/avatars/get-q-r.md +++ b/docs/examples/0.7.0/client-flutter/examples/avatars/get-q-r.md @@ -8,10 +8,18 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; +} - String result = avatars.getQR( +//displaying image +FutureBuilder( + future: avatars.getQR( text: '[TEXT]', - ); - - print(result); // Resource URL string -} \ No newline at end of file + ), //works for both public file and private file, for private files you need to be logged in + builder: (context, snapshot) { + return snapshot.hasData && snapshot.data != null + ? Image.memory( + snapshot.data.data, + ) + : CircularProgressIndicator(); + }, +); diff --git a/docs/examples/0.7.0/client-flutter/examples/database/create-document.md b/docs/examples/0.7.0/client-flutter/examples/database/create-document.md index b7d540bc2d..b2305f6602 100644 --- a/docs/examples/0.7.0/client-flutter/examples/database/create-document.md +++ b/docs/examples/0.7.0/client-flutter/examples/database/create-document.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = database.createDocument( collectionId: '[COLLECTION_ID]', data: {}, @@ -22,4 +21,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/database/delete-document.md b/docs/examples/0.7.0/client-flutter/examples/database/delete-document.md index 40c1cbdfe2..94479a0949 100644 --- a/docs/examples/0.7.0/client-flutter/examples/database/delete-document.md +++ b/docs/examples/0.7.0/client-flutter/examples/database/delete-document.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = database.deleteDocument( collectionId: '[COLLECTION_ID]', documentId: '[DOCUMENT_ID]', @@ -20,4 +19,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/database/get-document.md b/docs/examples/0.7.0/client-flutter/examples/database/get-document.md index 0c98142315..153ad8e0f9 100644 --- a/docs/examples/0.7.0/client-flutter/examples/database/get-document.md +++ b/docs/examples/0.7.0/client-flutter/examples/database/get-document.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = database.getDocument( collectionId: '[COLLECTION_ID]', documentId: '[DOCUMENT_ID]', @@ -20,4 +19,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/database/list-documents.md b/docs/examples/0.7.0/client-flutter/examples/database/list-documents.md index 0685e7f1aa..dc6893a285 100644 --- a/docs/examples/0.7.0/client-flutter/examples/database/list-documents.md +++ b/docs/examples/0.7.0/client-flutter/examples/database/list-documents.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = database.listDocuments( collectionId: '[COLLECTION_ID]', ); @@ -19,4 +18,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/database/update-document.md b/docs/examples/0.7.0/client-flutter/examples/database/update-document.md index 2f37cd767a..cc85817374 100644 --- a/docs/examples/0.7.0/client-flutter/examples/database/update-document.md +++ b/docs/examples/0.7.0/client-flutter/examples/database/update-document.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = database.updateDocument( collectionId: '[COLLECTION_ID]', documentId: '[DOCUMENT_ID]', @@ -23,4 +22,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/functions/create-execution.md b/docs/examples/0.7.0/client-flutter/examples/functions/create-execution.md index af61896d75..70b617ffab 100644 --- a/docs/examples/0.7.0/client-flutter/examples/functions/create-execution.md +++ b/docs/examples/0.7.0/client-flutter/examples/functions/create-execution.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = functions.createExecution( functionId: '[FUNCTION_ID]', ); @@ -19,4 +18,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/functions/get-execution.md b/docs/examples/0.7.0/client-flutter/examples/functions/get-execution.md index 22135bbedd..a10f7933c2 100644 --- a/docs/examples/0.7.0/client-flutter/examples/functions/get-execution.md +++ b/docs/examples/0.7.0/client-flutter/examples/functions/get-execution.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = functions.getExecution( functionId: '[FUNCTION_ID]', executionId: '[EXECUTION_ID]', @@ -20,4 +19,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/functions/list-executions.md b/docs/examples/0.7.0/client-flutter/examples/functions/list-executions.md index fe53f4a82b..3f59aa3f20 100644 --- a/docs/examples/0.7.0/client-flutter/examples/functions/list-executions.md +++ b/docs/examples/0.7.0/client-flutter/examples/functions/list-executions.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = functions.listExecutions( functionId: '[FUNCTION_ID]', ); @@ -19,4 +18,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/locale/get-continents.md b/docs/examples/0.7.0/client-flutter/examples/locale/get-continents.md index 60d4f32c4f..63920e735a 100644 --- a/docs/examples/0.7.0/client-flutter/examples/locale/get-continents.md +++ b/docs/examples/0.7.0/client-flutter/examples/locale/get-continents.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = locale.getContinents(); result @@ -17,4 +16,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/locale/get-countries-e-u.md b/docs/examples/0.7.0/client-flutter/examples/locale/get-countries-e-u.md index 766006fecc..39f3a1d709 100644 --- a/docs/examples/0.7.0/client-flutter/examples/locale/get-countries-e-u.md +++ b/docs/examples/0.7.0/client-flutter/examples/locale/get-countries-e-u.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = locale.getCountriesEU(); result @@ -17,4 +16,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/locale/get-countries-phones.md b/docs/examples/0.7.0/client-flutter/examples/locale/get-countries-phones.md index 786eeb0b70..44ff02603b 100644 --- a/docs/examples/0.7.0/client-flutter/examples/locale/get-countries-phones.md +++ b/docs/examples/0.7.0/client-flutter/examples/locale/get-countries-phones.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = locale.getCountriesPhones(); result @@ -17,4 +16,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/locale/get-countries.md b/docs/examples/0.7.0/client-flutter/examples/locale/get-countries.md index 0570421ef9..97ef85e1e6 100644 --- a/docs/examples/0.7.0/client-flutter/examples/locale/get-countries.md +++ b/docs/examples/0.7.0/client-flutter/examples/locale/get-countries.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = locale.getCountries(); result @@ -17,4 +16,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/locale/get-currencies.md b/docs/examples/0.7.0/client-flutter/examples/locale/get-currencies.md index 7846c3c710..fb27e3d957 100644 --- a/docs/examples/0.7.0/client-flutter/examples/locale/get-currencies.md +++ b/docs/examples/0.7.0/client-flutter/examples/locale/get-currencies.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = locale.getCurrencies(); result @@ -17,4 +16,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/locale/get-languages.md b/docs/examples/0.7.0/client-flutter/examples/locale/get-languages.md index 4f526383fa..badc806861 100644 --- a/docs/examples/0.7.0/client-flutter/examples/locale/get-languages.md +++ b/docs/examples/0.7.0/client-flutter/examples/locale/get-languages.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = locale.getLanguages(); result @@ -17,4 +16,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/locale/get.md b/docs/examples/0.7.0/client-flutter/examples/locale/get.md index 2dbdd47f63..21b1a6cff2 100644 --- a/docs/examples/0.7.0/client-flutter/examples/locale/get.md +++ b/docs/examples/0.7.0/client-flutter/examples/locale/get.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = locale.get(); result @@ -17,4 +16,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/storage/create-file.md b/docs/examples/0.7.0/client-flutter/examples/storage/create-file.md index 9f61be12f4..a49d0b3fd6 100644 --- a/docs/examples/0.7.0/client-flutter/examples/storage/create-file.md +++ b/docs/examples/0.7.0/client-flutter/examples/storage/create-file.md @@ -9,7 +9,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = storage.createFile( file: await MultipartFile.fromFile('./path-to-files/image.jpg', 'image.jpg'), read: [], @@ -22,4 +21,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/storage/delete-file.md b/docs/examples/0.7.0/client-flutter/examples/storage/delete-file.md index 271ed7b2dd..ee9af9fc0e 100644 --- a/docs/examples/0.7.0/client-flutter/examples/storage/delete-file.md +++ b/docs/examples/0.7.0/client-flutter/examples/storage/delete-file.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = storage.deleteFile( fileId: '[FILE_ID]', ); @@ -19,4 +18,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/storage/get-file-download.md b/docs/examples/0.7.0/client-flutter/examples/storage/get-file-download.md index be3385a61b..2c5de98ee8 100644 --- a/docs/examples/0.7.0/client-flutter/examples/storage/get-file-download.md +++ b/docs/examples/0.7.0/client-flutter/examples/storage/get-file-download.md @@ -8,10 +8,18 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; +} - String result = storage.getFileDownload( +//displaying image +FutureBuilder( + future: storage.getFileDownload( fileId: '[FILE_ID]', - ); - - print(result); // Resource URL string -} \ No newline at end of file + ), //works for both public file and private file, for private files you need to be logged in + builder: (context, snapshot) { + return snapshot.hasData && snapshot.data != null + ? Image.memory( + snapshot.data.data, + ) + : CircularProgressIndicator(); + }, +); diff --git a/docs/examples/0.7.0/client-flutter/examples/storage/get-file-preview.md b/docs/examples/0.7.0/client-flutter/examples/storage/get-file-preview.md index 8bd1a47457..0cf4c9c0d0 100644 --- a/docs/examples/0.7.0/client-flutter/examples/storage/get-file-preview.md +++ b/docs/examples/0.7.0/client-flutter/examples/storage/get-file-preview.md @@ -8,10 +8,18 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; +} - String result = storage.getFilePreview( +//displaying image +FutureBuilder( + future: storage.getFilePreview( fileId: '[FILE_ID]', - ); - - print(result); // Resource URL string -} \ No newline at end of file + ), //works for both public file and private file, for private files you need to be logged in + builder: (context, snapshot) { + return snapshot.hasData && snapshot.data != null + ? Image.memory( + snapshot.data.data, + ) + : CircularProgressIndicator(); + }, +); diff --git a/docs/examples/0.7.0/client-flutter/examples/storage/get-file-view.md b/docs/examples/0.7.0/client-flutter/examples/storage/get-file-view.md index 5803fd409f..9b53265ee7 100644 --- a/docs/examples/0.7.0/client-flutter/examples/storage/get-file-view.md +++ b/docs/examples/0.7.0/client-flutter/examples/storage/get-file-view.md @@ -8,10 +8,18 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; +} - String result = storage.getFileView( +//displaying image +FutureBuilder( + future: storage.getFileView( fileId: '[FILE_ID]', - ); - - print(result); // Resource URL string -} \ No newline at end of file + ), //works for both public file and private file, for private files you need to be logged in + builder: (context, snapshot) { + return snapshot.hasData && snapshot.data != null + ? Image.memory( + snapshot.data.data, + ) + : CircularProgressIndicator(); + }, +); diff --git a/docs/examples/0.7.0/client-flutter/examples/storage/get-file.md b/docs/examples/0.7.0/client-flutter/examples/storage/get-file.md index feca667fa9..2f734c6bdc 100644 --- a/docs/examples/0.7.0/client-flutter/examples/storage/get-file.md +++ b/docs/examples/0.7.0/client-flutter/examples/storage/get-file.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = storage.getFile( fileId: '[FILE_ID]', ); @@ -19,4 +18,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/storage/list-files.md b/docs/examples/0.7.0/client-flutter/examples/storage/list-files.md index 1963ec694d..663d416198 100644 --- a/docs/examples/0.7.0/client-flutter/examples/storage/list-files.md +++ b/docs/examples/0.7.0/client-flutter/examples/storage/list-files.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = storage.listFiles( ); @@ -18,4 +17,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/storage/update-file.md b/docs/examples/0.7.0/client-flutter/examples/storage/update-file.md index a5ecbfe987..bc71901245 100644 --- a/docs/examples/0.7.0/client-flutter/examples/storage/update-file.md +++ b/docs/examples/0.7.0/client-flutter/examples/storage/update-file.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = storage.updateFile( fileId: '[FILE_ID]', read: [], @@ -21,4 +20,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/teams/create-membership.md b/docs/examples/0.7.0/client-flutter/examples/teams/create-membership.md index e5e82de7b4..ef5931ea66 100644 --- a/docs/examples/0.7.0/client-flutter/examples/teams/create-membership.md +++ b/docs/examples/0.7.0/client-flutter/examples/teams/create-membership.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = teams.createMembership( teamId: '[TEAM_ID]', email: 'email@example.com', @@ -22,4 +21,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/teams/create.md b/docs/examples/0.7.0/client-flutter/examples/teams/create.md index d11958ae6d..90dea2fb1a 100644 --- a/docs/examples/0.7.0/client-flutter/examples/teams/create.md +++ b/docs/examples/0.7.0/client-flutter/examples/teams/create.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = teams.create( name: '[NAME]', ); @@ -19,4 +18,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/teams/delete-membership.md b/docs/examples/0.7.0/client-flutter/examples/teams/delete-membership.md index 3f57b840fe..6f26421a55 100644 --- a/docs/examples/0.7.0/client-flutter/examples/teams/delete-membership.md +++ b/docs/examples/0.7.0/client-flutter/examples/teams/delete-membership.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = teams.deleteMembership( teamId: '[TEAM_ID]', inviteId: '[INVITE_ID]', @@ -20,4 +19,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/teams/delete.md b/docs/examples/0.7.0/client-flutter/examples/teams/delete.md index 9fb488afec..e55c62b2e3 100644 --- a/docs/examples/0.7.0/client-flutter/examples/teams/delete.md +++ b/docs/examples/0.7.0/client-flutter/examples/teams/delete.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = teams.delete( teamId: '[TEAM_ID]', ); @@ -19,4 +18,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/teams/get-memberships.md b/docs/examples/0.7.0/client-flutter/examples/teams/get-memberships.md index 4edadfa2b4..3116ca86ad 100644 --- a/docs/examples/0.7.0/client-flutter/examples/teams/get-memberships.md +++ b/docs/examples/0.7.0/client-flutter/examples/teams/get-memberships.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = teams.getMemberships( teamId: '[TEAM_ID]', ); @@ -19,4 +18,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/teams/get.md b/docs/examples/0.7.0/client-flutter/examples/teams/get.md index 083565f7a6..25eb2453fc 100644 --- a/docs/examples/0.7.0/client-flutter/examples/teams/get.md +++ b/docs/examples/0.7.0/client-flutter/examples/teams/get.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = teams.get( teamId: '[TEAM_ID]', ); @@ -19,4 +18,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/teams/list.md b/docs/examples/0.7.0/client-flutter/examples/teams/list.md index 665020e928..8657f80fd3 100644 --- a/docs/examples/0.7.0/client-flutter/examples/teams/list.md +++ b/docs/examples/0.7.0/client-flutter/examples/teams/list.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = teams.list( ); @@ -18,4 +17,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/teams/update-membership-status.md b/docs/examples/0.7.0/client-flutter/examples/teams/update-membership-status.md index 616f64e148..c596c904e1 100644 --- a/docs/examples/0.7.0/client-flutter/examples/teams/update-membership-status.md +++ b/docs/examples/0.7.0/client-flutter/examples/teams/update-membership-status.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = teams.updateMembershipStatus( teamId: '[TEAM_ID]', inviteId: '[INVITE_ID]', @@ -22,4 +21,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/client-flutter/examples/teams/update.md b/docs/examples/0.7.0/client-flutter/examples/teams/update.md index 915fb57d38..838639be9b 100644 --- a/docs/examples/0.7.0/client-flutter/examples/teams/update.md +++ b/docs/examples/0.7.0/client-flutter/examples/teams/update.md @@ -8,7 +8,6 @@ void main() { // Init SDK .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint .setProject('5df5acd0d48c2') // Your project ID ; - Future result = teams.update( teamId: '[TEAM_ID]', name: '[NAME]', @@ -20,4 +19,4 @@ void main() { // Init SDK }).catchError((error) { print(error.response); }); -} \ No newline at end of file +} diff --git a/docs/examples/0.7.0/server-dart/examples/avatars/get-browser.md b/docs/examples/0.7.0/server-dart/examples/avatars/get-browser.md index 87d2062fc4..c924ae43c8 100644 --- a/docs/examples/0.7.0/server-dart/examples/avatars/get-browser.md +++ b/docs/examples/0.7.0/server-dart/examples/avatars/get-browser.md @@ -10,9 +10,14 @@ void main() { // Init SDK .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; - String result = avatars.getBrowser( + Future result = avatars.getBrowser( code: 'aa', ); - print(result); // Resource URL string + result + .then((response) { + print(response); + }).catchError((error) { + print(error.response); + }); } \ No newline at end of file diff --git a/docs/examples/0.7.0/server-dart/examples/avatars/get-credit-card.md b/docs/examples/0.7.0/server-dart/examples/avatars/get-credit-card.md index 8fe5f1d5d8..5d01d690dd 100644 --- a/docs/examples/0.7.0/server-dart/examples/avatars/get-credit-card.md +++ b/docs/examples/0.7.0/server-dart/examples/avatars/get-credit-card.md @@ -10,9 +10,14 @@ void main() { // Init SDK .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; - String result = avatars.getCreditCard( + Future result = avatars.getCreditCard( code: 'amex', ); - print(result); // Resource URL string + result + .then((response) { + print(response); + }).catchError((error) { + print(error.response); + }); } \ No newline at end of file diff --git a/docs/examples/0.7.0/server-dart/examples/avatars/get-favicon.md b/docs/examples/0.7.0/server-dart/examples/avatars/get-favicon.md index 6a249dc70f..3308f5c59a 100644 --- a/docs/examples/0.7.0/server-dart/examples/avatars/get-favicon.md +++ b/docs/examples/0.7.0/server-dart/examples/avatars/get-favicon.md @@ -10,9 +10,14 @@ void main() { // Init SDK .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; - String result = avatars.getFavicon( + Future result = avatars.getFavicon( url: 'https://example.com', ); - print(result); // Resource URL string + result + .then((response) { + print(response); + }).catchError((error) { + print(error.response); + }); } \ No newline at end of file diff --git a/docs/examples/0.7.0/server-dart/examples/avatars/get-flag.md b/docs/examples/0.7.0/server-dart/examples/avatars/get-flag.md index f997b668da..5da4aaf219 100644 --- a/docs/examples/0.7.0/server-dart/examples/avatars/get-flag.md +++ b/docs/examples/0.7.0/server-dart/examples/avatars/get-flag.md @@ -10,9 +10,14 @@ void main() { // Init SDK .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; - String result = avatars.getFlag( + Future result = avatars.getFlag( code: 'af', ); - print(result); // Resource URL string + result + .then((response) { + print(response); + }).catchError((error) { + print(error.response); + }); } \ No newline at end of file diff --git a/docs/examples/0.7.0/server-dart/examples/avatars/get-image.md b/docs/examples/0.7.0/server-dart/examples/avatars/get-image.md index 9f31d8f159..196c6dedb5 100644 --- a/docs/examples/0.7.0/server-dart/examples/avatars/get-image.md +++ b/docs/examples/0.7.0/server-dart/examples/avatars/get-image.md @@ -10,9 +10,14 @@ void main() { // Init SDK .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; - String result = avatars.getImage( + Future result = avatars.getImage( url: 'https://example.com', ); - print(result); // Resource URL string + result + .then((response) { + print(response); + }).catchError((error) { + print(error.response); + }); } \ No newline at end of file diff --git a/docs/examples/0.7.0/server-dart/examples/avatars/get-initials.md b/docs/examples/0.7.0/server-dart/examples/avatars/get-initials.md index 73788d2170..c40a54b712 100644 --- a/docs/examples/0.7.0/server-dart/examples/avatars/get-initials.md +++ b/docs/examples/0.7.0/server-dart/examples/avatars/get-initials.md @@ -10,8 +10,13 @@ void main() { // Init SDK .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; - String result = avatars.getInitials( + Future result = avatars.getInitials( ); - print(result); // Resource URL string + result + .then((response) { + print(response); + }).catchError((error) { + print(error.response); + }); } \ No newline at end of file diff --git a/docs/examples/0.7.0/server-dart/examples/avatars/get-q-r.md b/docs/examples/0.7.0/server-dart/examples/avatars/get-q-r.md index eb8b46c743..bef31b345f 100644 --- a/docs/examples/0.7.0/server-dart/examples/avatars/get-q-r.md +++ b/docs/examples/0.7.0/server-dart/examples/avatars/get-q-r.md @@ -10,9 +10,14 @@ void main() { // Init SDK .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; - String result = avatars.getQR( + Future result = avatars.getQR( text: '[TEXT]', ); - print(result); // Resource URL string + result + .then((response) { + print(response); + }).catchError((error) { + print(error.response); + }); } \ No newline at end of file diff --git a/docs/examples/0.7.0/server-dart/examples/storage/get-file-download.md b/docs/examples/0.7.0/server-dart/examples/storage/get-file-download.md index 2eceaab1f5..1601fccd94 100644 --- a/docs/examples/0.7.0/server-dart/examples/storage/get-file-download.md +++ b/docs/examples/0.7.0/server-dart/examples/storage/get-file-download.md @@ -10,9 +10,14 @@ void main() { // Init SDK .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; - String result = storage.getFileDownload( + Future result = storage.getFileDownload( fileId: '[FILE_ID]', ); - print(result); // Resource URL string + result + .then((response) { + print(response); + }).catchError((error) { + print(error.response); + }); } \ No newline at end of file diff --git a/docs/examples/0.7.0/server-dart/examples/storage/get-file-preview.md b/docs/examples/0.7.0/server-dart/examples/storage/get-file-preview.md index 2b564d81b7..2c88ef408e 100644 --- a/docs/examples/0.7.0/server-dart/examples/storage/get-file-preview.md +++ b/docs/examples/0.7.0/server-dart/examples/storage/get-file-preview.md @@ -10,9 +10,14 @@ void main() { // Init SDK .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; - String result = storage.getFilePreview( + Future result = storage.getFilePreview( fileId: '[FILE_ID]', ); - print(result); // Resource URL string + result + .then((response) { + print(response); + }).catchError((error) { + print(error.response); + }); } \ No newline at end of file diff --git a/docs/examples/0.7.0/server-dart/examples/storage/get-file-view.md b/docs/examples/0.7.0/server-dart/examples/storage/get-file-view.md index 030fb40f38..27fa91bb05 100644 --- a/docs/examples/0.7.0/server-dart/examples/storage/get-file-view.md +++ b/docs/examples/0.7.0/server-dart/examples/storage/get-file-view.md @@ -10,9 +10,14 @@ void main() { // Init SDK .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; - String result = storage.getFileView( + Future result = storage.getFileView( fileId: '[FILE_ID]', ); - print(result); // Resource URL string + result + .then((response) { + print(response); + }).catchError((error) { + print(error.response); + }); } \ No newline at end of file diff --git a/docs/sdks/dart/CHANGELOG.md b/docs/sdks/dart/CHANGELOG.md index e8d0ca1f73..8926d9077c 100644 --- a/docs/sdks/dart/CHANGELOG.md +++ b/docs/sdks/dart/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.3.0 + +- Improved code quality +- Added a custom Appwrite exception +- Enabled access to private storage file + +## 0.2.0 + +- Upgraded to work with Appwrite 0.7 + ## 0.1.0 - First release \ No newline at end of file diff --git a/docs/sdks/flutter/CHANGELOG.md b/docs/sdks/flutter/CHANGELOG.md index 0e853c7ebe..5f55c15e90 100644 --- a/docs/sdks/flutter/CHANGELOG.md +++ b/docs/sdks/flutter/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.4.0-dev.1 + +- Improved code quality +- Enabled access to private storage file +- Added easier integration for preview images and the Image widget + +## 0.3.0 + +- Upgraded to work with Appwrite 0.7 + ## 0.3.0-dev.2 - Fix for an error when using a self-signed certificate for Web From 486ae9d96e90e2dbabd8e4ac3e1a8424411d078d Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 3 Mar 2021 07:48:39 -0500 Subject: [PATCH 023/195] Updated domnikl/statsd to slickdeals/statsd --- CHANGES.md | 1 + composer.json | 4 +- composer.lock | 110 +++++++++++++++++++++++++------------------------- 3 files changed, 57 insertions(+), 58 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 11f67a9ecd..8670aa1d7e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,7 @@ - Upgraded influxdb/influxdb-php lib to version 1.15.2 - Upgraded phpmailer/phpmailer lib to version 6.3.0 - Upgraded adhocore/jwt lib to version 1.1.2 +- Upgraded domnikl/statsd to slickdeals/statsd version 3.0 ## Bug Fixes diff --git a/composer.json b/composer.json index b4370e7c2f..99abf7605e 100644 --- a/composer.json +++ b/composer.json @@ -53,11 +53,11 @@ "resque/php-resque": "1.3.6", "matomo/device-detector": "4.1.0", "dragonmantank/cron-expression": "3.1.0", - "domnikl/statsd": "3.0.2", "influxdb/influxdb-php": "1.15.2", "phpmailer/phpmailer": "6.3.0", "chillerlan/php-qrcode": "4.3.0", - "adhocore/jwt": "1.1.2" + "adhocore/jwt": "1.1.2", + "slickdeals/statsd": "~3.0" }, "require-dev": { "appwrite/sdk-generator": "0.5.5", diff --git a/composer.lock b/composer.lock index 85b0932c93..aff6fefab8 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "4f58de92fb64af44d915387895472881", + "content-hash": "c89bd786aae720e161185007212f6594", "packages": [ { "name": "adhocore/jwt", @@ -297,61 +297,6 @@ }, "time": "2020-11-06T16:09:14+00:00" }, - { - "name": "domnikl/statsd", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/domnikl/statsd-php.git", - "reference": "393c6565efbfb23c8296ae3099a62fb6366c6ce3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/domnikl/statsd-php/zipball/393c6565efbfb23c8296ae3099a62fb6366c6ce3", - "reference": "393c6565efbfb23c8296ae3099a62fb6366c6ce3", - "shasum": "" - }, - "require": { - "php": ">= 7.2" - }, - "require-dev": { - "flyeralarm/php-code-validator": "^2.2", - "phpunit/phpunit": "~8.0", - "vimeo/psalm": "^3.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Domnikl\\Statsd\\": "src/", - "Domnikl\\Test\\Statsd\\": "tests/unit" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dominik Liebler", - "email": "liebler.dominik@gmail.com" - } - ], - "description": "a PHP client for statsd", - "homepage": "https://domnikl.github.com/statsd-php", - "keywords": [ - "Metrics", - "monitoring", - "statistics", - "statsd", - "udp" - ], - "support": { - "issues": "https://github.com/domnikl/statsd-php/issues", - "source": "https://github.com/domnikl/statsd-php/tree/master" - }, - "abandoned": true, - "time": "2020-01-03T14:24:58+00:00" - }, { "name": "dragonmantank/cron-expression", "version": "v3.1.0", @@ -1192,6 +1137,59 @@ }, "time": "2020-04-16T16:39:50+00:00" }, + { + "name": "slickdeals/statsd", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/Slickdeals/statsd-php.git", + "reference": "393c6565efbfb23c8296ae3099a62fb6366c6ce3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Slickdeals/statsd-php/zipball/393c6565efbfb23c8296ae3099a62fb6366c6ce3", + "reference": "393c6565efbfb23c8296ae3099a62fb6366c6ce3", + "shasum": "" + }, + "require": { + "php": ">= 7.2" + }, + "require-dev": { + "flyeralarm/php-code-validator": "^2.2", + "phpunit/phpunit": "~8.0", + "vimeo/psalm": "^3.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Domnikl\\Statsd\\": "src/", + "Domnikl\\Test\\Statsd\\": "tests/unit" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dominik Liebler", + "email": "liebler.dominik@gmail.com" + } + ], + "description": "a PHP client for statsd", + "homepage": "https://domnikl.github.com/statsd-php", + "keywords": [ + "Metrics", + "monitoring", + "statistics", + "statsd", + "udp" + ], + "support": { + "source": "https://github.com/Slickdeals/statsd-php/tree/3.0.2" + }, + "time": "2020-01-03T14:24:58+00:00" + }, { "name": "symfony/polyfill-ctype", "version": "dev-main", From 192ba00e3fe0020e1f0a2580f2f481c8b8999b86 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Thu, 4 Mar 2021 12:18:50 -0500 Subject: [PATCH 024/195] Add event for users.update.prefs --- app/config/events.php | 5 +++++ app/controllers/api/users.php | 1 + 2 files changed, 6 insertions(+) diff --git a/app/config/events.php b/app/config/events.php index 2bb3862fdf..4893f6f188 100644 --- a/app/config/events.php +++ b/app/config/events.php @@ -117,6 +117,11 @@ return [ 'model' => Response::MODEL_USER, 'note' => 'version >= 0.7', ], + 'users.update.prefs' => [ + 'description' => 'This event triggers when a user preference is updated from the users API.', + 'model' => Response::MODEL_USER, + 'note' => 'version >= 0.7', + ], 'users.update.status' => [ 'description' => 'This event triggers when a user status is updated from the users API.', 'model' => Response::MODEL_USER, diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index efb0041cee..0af3ec03a1 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -373,6 +373,7 @@ App::patch('/v1/users/:userId/status') App::patch('/v1/users/:userId/prefs') ->desc('Update User Preferences') ->groups(['api', 'users']) + ->label('event', 'users.update.prefs') ->label('scope', 'users.write') ->label('sdk.platform', [APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'users') From 440a67e6d2c7eaa75066bd22e1ade47cf3364024 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Thu, 4 Mar 2021 13:47:02 -0500 Subject: [PATCH 025/195] Add e2e test for users.update.prefs --- .../Webhooks/WebhooksCustomServerTest.php | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php index 360dee2cb3..5869bddbbe 100644 --- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php +++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php @@ -186,7 +186,46 @@ class WebhooksCustomServerTest extends Scope */ return ['userId' => $user['body']['$id'], 'name' => $user['body']['name'], 'email' => $user['body']['email']]; } - + + /** + * @depends testCreateUser + */ + public function testUpdateUserPrefs(array $data):array + { + /** + * Test for SUCCESS + */ + $user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/prefs', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'prefs' => ['test' => true], + ]); + + $this->assertEquals($user['headers']['status-code'], 200); + $this->assertNotEmpty($user['body']['prefs']); + + $webhook = $this->getLastRequest(); + + $this->assertEquals($webhook['method'], 'POST'); + $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->assertEquals($webhook['headers']['X-Appwrite-Webhook-Event'], 'users.update.prefs'); + $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']); + $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide())); + $this->assertNotEmpty($webhook['data']['$id']); + $this->assertEquals($webhook['data']['name'], $data['name']); + $this->assertIsInt($webhook['data']['registration']); + $this->assertEquals($webhook['data']['status'], 0); + $this->assertEquals($webhook['data']['email'], $data['email']); + $this->assertEquals($webhook['data']['emailVerification'], false); + $this->assertEquals($webhook['data']['prefs'], ["test" => true]); + + return $data; + } + /** * @depends testCreateUser */ @@ -261,4 +300,4 @@ class WebhooksCustomServerTest extends Scope return $data; } -} \ No newline at end of file +} From b665833323bb6838707e94f3797e74fe5be230fd Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 4 Mar 2021 23:26:09 +0200 Subject: [PATCH 026/195] Updated SDKs --- app/config/platforms.php | 4 ++-- composer.json | 2 +- composer.lock | 16 ++++++++-------- docs/sdks/dart/CHANGELOG.md | 4 ++++ docs/sdks/flutter/CHANGELOG.md | 4 ++++ 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 4d8e5506fe..4e4bde824a 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -32,7 +32,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '0.4.0-dev.1', + 'version' => '0.4.0-dev.2', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -284,7 +284,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '0.3.0', + 'version' => '0.3.1', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, diff --git a/composer.json b/composer.json index cc4cdf9098..f84268963c 100644 --- a/composer.json +++ b/composer.json @@ -59,7 +59,7 @@ "adhocore/jwt": "1.1.0" }, "require-dev": { - "appwrite/sdk-generator": "0.6.1", + "appwrite/sdk-generator": "0.6.2", "phpunit/phpunit": "9.4.2", "swoole/ide-helper": "4.5.5", "vimeo/psalm": "4.1.1" diff --git a/composer.lock b/composer.lock index 9b252dcd05..e7b549f246 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "4b03aefd02908c32c2d1be6127c8c23a", + "content-hash": "eebe34132bbd166e74c848453fe2068e", "packages": [ { "name": "adhocore/jwt", @@ -574,12 +574,12 @@ "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "f47ece9e6e8ce74e3be04bef47f46061dc18c095" + "reference": "2f3e4f6cf8fd4aad7624c90a94f0ab38fde25976" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/f47ece9e6e8ce74e3be04bef47f46061dc18c095", - "reference": "f47ece9e6e8ce74e3be04bef47f46061dc18c095", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/2f3e4f6cf8fd4aad7624c90a94f0ab38fde25976", + "reference": "2f3e4f6cf8fd4aad7624c90a94f0ab38fde25976", "shasum": "" }, "require": { @@ -641,7 +641,7 @@ "issues": "https://github.com/guzzle/psr7/issues", "source": "https://github.com/guzzle/psr7/tree/1.x" }, - "time": "2020-12-08T11:45:39+00:00" + "time": "2021-03-02T18:57:24+00:00" }, { "name": "influxdb/influxdb-php", @@ -2086,11 +2086,11 @@ }, { "name": "appwrite/sdk-generator", - "version": "0.6.1", + "version": "0.6.2", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator", - "reference": "8a55e7082428583a89ece31df463663c2b2320d8" + "reference": "d96376295c9265a054634d21dcdbfc02b832c677" }, "require": { "ext-curl": "*", @@ -2120,7 +2120,7 @@ } ], "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", - "time": "2021-03-02T17:02:58+00:00" + "time": "2021-03-03T12:53:32+00:00" }, { "name": "composer/package-versions-deprecated", diff --git a/docs/sdks/dart/CHANGELOG.md b/docs/sdks/dart/CHANGELOG.md index 8926d9077c..4cdd477aba 100644 --- a/docs/sdks/dart/CHANGELOG.md +++ b/docs/sdks/dart/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.1 + +- Minor fixes for custom exceptions + ## 0.3.0 - Improved code quality diff --git a/docs/sdks/flutter/CHANGELOG.md b/docs/sdks/flutter/CHANGELOG.md index 5f55c15e90..8798963fc8 100644 --- a/docs/sdks/flutter/CHANGELOG.md +++ b/docs/sdks/flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.0-dev.2 + +- Minor fixes for custom exceptions + ## 0.4.0-dev.1 - Improved code quality From 9403b9b95f95cdae828b15fad1d8223b9ed3a5c4 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 4 Mar 2021 23:32:03 +0200 Subject: [PATCH 027/195] Added new routes to mock errors --- app/controllers/mock.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/app/controllers/mock.php b/app/controllers/mock.php index 4c9f3faf9f..493d62d6be 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -280,6 +280,32 @@ App::get('/v1/mock/tests/general/empty') $response->noContent(); }); +App::get('/v1/mock/tests/general/400-error') + ->desc('Mock a an 400 failed request') + ->groups(['mock']) + ->label('scope', 'public') + ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) + ->label('sdk.namespace', 'general') + ->label('sdk.method', 'error400') + ->label('sdk.description', 'Mock an 400 error') + ->label('sdk.mock', true) + ->action(function () { + throw new Exception('Mock 400 error', 400); + }); + +App::get('/v1/mock/tests/general/500-error') + ->desc('Mock a an 500 failed request') + ->groups(['mock']) + ->label('scope', 'public') + ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) + ->label('sdk.namespace', 'general') + ->label('sdk.method', 'error500') + ->label('sdk.description', 'Mock an 500 error') + ->label('sdk.mock', true) + ->action(function () { + throw new Exception('Mock 500 error', 500); + }); + App::get('/v1/mock/tests/general/oauth2') ->desc('Mock an OAuth2 login route') ->groups(['mock']) From 30b04baca8792cfa63d6def236988b4fab10aee7 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Fri, 5 Mar 2021 07:30:34 +0200 Subject: [PATCH 028/195] Added response types --- app/controllers/api/account.php | 4 ++-- app/controllers/mock.php | 36 ++++++++++++++++++++------------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index e8ae76312b..e7f36af332 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -253,8 +253,8 @@ App::get('/v1/account/sessions/oauth2/:provider') ->label('sdk.namespace', 'account') ->label('sdk.method', 'createOAuth2Session') ->label('sdk.description', '/docs/references/account/create-session-oauth2.md') - ->label('sdk.response.code', 301) - ->label('sdk.response.type', 'text/html') + ->label('sdk.response.code', Response::STATUS_CODE_MOVED_PERMANENTLY) + ->label('sdk.response.type', Response::CONTENT_TYPE_HTML) ->label('sdk.methodType', 'webAuth') ->label('abuse-limit', 50) ->label('abuse-key', 'ip:{ip}') diff --git a/app/controllers/mock.php b/app/controllers/mock.php index 493d62d6be..aad24bf9aa 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -2,8 +2,8 @@ global $utopia, $request, $response; +use Appwrite\Utopia\Response; use Utopia\App; -use Utopia\Response; use Utopia\Validator\Numeric; use Utopia\Validator\Text; use Utopia\Validator\ArrayList; @@ -161,7 +161,7 @@ App::delete('/v1/mock/tests/bar') }); App::post('/v1/mock/tests/general/upload') - ->desc('Mock a post request for SDK tests') + ->desc('Upload File') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) @@ -203,7 +203,7 @@ App::post('/v1/mock/tests/general/upload') }); App::get('/v1/mock/tests/general/redirect') - ->desc('Mock a post request for SDK tests') + ->desc('Redirect') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) @@ -219,7 +219,7 @@ App::get('/v1/mock/tests/general/redirect') }); App::get('/v1/mock/tests/general/redirect/done') - ->desc('Mock a post request for SDK tests') + ->desc('Redirect Target') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) @@ -231,13 +231,13 @@ App::get('/v1/mock/tests/general/redirect/done') }); App::get('/v1/mock/tests/general/set-cookie') - ->desc('Mock a cookie request for SDK tests') + ->desc('Set Cookie') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'setCookie') - ->label('sdk.description', 'Mock a set cookie request for SDK tests') + ->label('sdk.description', 'Mock a set cookie request.') ->label('sdk.mock', true) ->inject('response') ->action(function ($response) { @@ -247,13 +247,13 @@ App::get('/v1/mock/tests/general/set-cookie') }); App::get('/v1/mock/tests/general/get-cookie') - ->desc('Mock a cookie request for SDK tests') + ->desc('Get Cookie') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'getCookie') - ->label('sdk.description', 'Mock a get cookie request for SDK tests') + ->label('sdk.description', 'Mock a cookie response.') ->label('sdk.mock', true) ->inject('request') ->action(function ($request) { @@ -265,13 +265,15 @@ App::get('/v1/mock/tests/general/get-cookie') }); App::get('/v1/mock/tests/general/empty') - ->desc('Mock a post request for SDK tests') + ->desc('Empty Response') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'empty') - ->label('sdk.description', 'Mock a redirected request for SDK tests') + ->label('sdk.description', 'Mock a an empty response body.') + ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) + ->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.mock', true) ->inject('response') ->action(function ($response) { @@ -281,26 +283,32 @@ App::get('/v1/mock/tests/general/empty') }); App::get('/v1/mock/tests/general/400-error') - ->desc('Mock a an 400 failed request') + ->desc('400 Error') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'error400') - ->label('sdk.description', 'Mock an 400 error') + ->label('sdk.description', 'Mock a an 400 failed request.') + ->label('sdk.response.code', Response::STATUS_CODE_BAD_REQUEST) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_ERROR) ->label('sdk.mock', true) ->action(function () { throw new Exception('Mock 400 error', 400); }); App::get('/v1/mock/tests/general/500-error') - ->desc('Mock a an 500 failed request') + ->desc('500 Error') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'error500') - ->label('sdk.description', 'Mock an 500 error') + ->label('sdk.description', 'Mock a an 500 failed request.') + ->label('sdk.response.code', Response::STATUS_CODE_INTERNAL_SERVER_ERROR) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_ERROR) ->label('sdk.mock', true) ->action(function () { throw new Exception('Mock 500 error', 500); From 9552a8dc80d7070345c62dcba7cdb72fe6e9e728 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Fri, 5 Mar 2021 12:21:41 +0545 Subject: [PATCH 029/195] add response formate to access control allowed headers --- app/controllers/general.php | 4 ++-- tests/e2e/General/HTTPTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 614c400048..fe00160388 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -117,7 +117,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo ->addHeader('Server', 'Appwrite') ->addHeader('X-Content-Type-Options', 'nosniff') ->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE') - ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-SDK-Version, Cache-Control, Expires, Pragma') + ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-SDK-Version, Cache-Control, Expires, Pragma') ->addHeader('Access-Control-Expose-Headers', 'X-Fallback-Cookies') ->addHeader('Access-Control-Allow-Origin', $refDomain) ->addHeader('Access-Control-Allow-Credentials', 'true') @@ -237,7 +237,7 @@ App::options(function ($request, $response) { $response ->addHeader('Server', 'Appwrite') ->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE') - ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-SDK-Version, Cache-Control, Expires, Pragma, X-Fallback-Cookies') + ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-SDK-Version, Cache-Control, Expires, Pragma, X-Fallback-Cookies') ->addHeader('Access-Control-Expose-Headers', 'X-Fallback-Cookies') ->addHeader('Access-Control-Allow-Origin', $origin) ->addHeader('Access-Control-Allow-Credentials', 'true') diff --git a/tests/e2e/General/HTTPTest.php b/tests/e2e/General/HTTPTest.php index e33c46e3a2..24e586cbc2 100644 --- a/tests/e2e/General/HTTPTest.php +++ b/tests/e2e/General/HTTPTest.php @@ -26,7 +26,7 @@ class HTTPTest extends Scope $this->assertEquals(204, $response['headers']['status-code']); $this->assertEquals('Appwrite', $response['headers']['server']); $this->assertEquals('GET, POST, PUT, PATCH, DELETE', $response['headers']['access-control-allow-methods']); - $this->assertEquals('Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-SDK-Version, Cache-Control, Expires, Pragma, X-Fallback-Cookies', $response['headers']['access-control-allow-headers']); + $this->assertEquals('Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-SDK-Version, Cache-Control, Expires, Pragma, X-Fallback-Cookies', $response['headers']['access-control-allow-headers']); $this->assertEquals('X-Fallback-Cookies', $response['headers']['access-control-expose-headers']); $this->assertEquals('http://localhost', $response['headers']['access-control-allow-origin']); $this->assertEquals('true', $response['headers']['access-control-allow-credentials']); From c31c7736a14008b02e7177a20a1277f7a196529b Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Fri, 5 Mar 2021 08:40:29 +0200 Subject: [PATCH 030/195] Updated Mock API --- app/controllers/mock.php | 107 ++++++++++++++------ src/Appwrite/Utopia/Response.php | 6 ++ src/Appwrite/Utopia/Response/Model/Mock.php | 41 ++++++++ 3 files changed, 124 insertions(+), 30 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Model/Mock.php diff --git a/app/controllers/mock.php b/app/controllers/mock.php index aad24bf9aa..7e65e2d936 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -2,6 +2,7 @@ global $utopia, $request, $response; +use Appwrite\Database\Document; use Appwrite\Utopia\Response; use Utopia\App; use Utopia\Validator\Numeric; @@ -11,13 +12,16 @@ use Utopia\Validator\Host; use Utopia\Storage\Validator\File; App::get('/v1/mock/tests/foo') - ->desc('Mock a get request for SDK tests') + ->desc('Get Foo') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'foo') ->label('sdk.method', 'get') - ->label('sdk.description', 'Mock a get request for SDK tests') + ->label('sdk.description', 'Mock a get request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -26,13 +30,16 @@ App::get('/v1/mock/tests/foo') }); App::post('/v1/mock/tests/foo') - ->desc('Mock a post request for SDK tests') + ->desc('Post Foo') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'foo') ->label('sdk.method', 'post') - ->label('sdk.description', 'Mock a post request for SDK tests') + ->label('sdk.description', 'Mock a post request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -41,13 +48,16 @@ App::post('/v1/mock/tests/foo') }); App::patch('/v1/mock/tests/foo') - ->desc('Mock a patch request for SDK tests') + ->desc('Patch Foo') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'foo') ->label('sdk.method', 'patch') - ->label('sdk.description', 'Mock a get request for SDK tests') + ->label('sdk.description', 'Mock a patch request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -56,13 +66,16 @@ App::patch('/v1/mock/tests/foo') }); App::put('/v1/mock/tests/foo') - ->desc('Mock a put request for SDK tests') + ->desc('Put Foo') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'foo') ->label('sdk.method', 'put') - ->label('sdk.description', 'Mock a put request for SDK tests') + ->label('sdk.description', 'Mock a put request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -71,13 +84,16 @@ App::put('/v1/mock/tests/foo') }); App::delete('/v1/mock/tests/foo') - ->desc('Mock a delete request for SDK tests') + ->desc('Delete Foo') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'foo') ->label('sdk.method', 'delete') - ->label('sdk.description', 'Mock a delete request for SDK tests') + ->label('sdk.description', 'Mock a delete request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -86,13 +102,16 @@ App::delete('/v1/mock/tests/foo') }); App::get('/v1/mock/tests/bar') - ->desc('Mock a get request for SDK tests') + ->desc('Get Bar') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'bar') ->label('sdk.method', 'get') - ->label('sdk.description', 'Mock a get request for SDK tests') + ->label('sdk.description', 'Mock a get request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -101,13 +120,16 @@ App::get('/v1/mock/tests/bar') }); App::post('/v1/mock/tests/bar') - ->desc('Mock a post request for SDK tests') + ->desc('Post Bar') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'bar') ->label('sdk.method', 'post') - ->label('sdk.description', 'Mock a post request for SDK tests') + ->label('sdk.description', 'Mock a post request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -116,13 +138,16 @@ App::post('/v1/mock/tests/bar') }); App::patch('/v1/mock/tests/bar') - ->desc('Mock a patch request for SDK tests') + ->desc('Patch Bar') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'bar') ->label('sdk.method', 'patch') - ->label('sdk.description', 'Mock a get request for SDK tests') + ->label('sdk.description', 'Mock a patch request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -131,13 +156,16 @@ App::patch('/v1/mock/tests/bar') }); App::put('/v1/mock/tests/bar') - ->desc('Mock a put request for SDK tests') + ->desc('Put Bar') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'bar') ->label('sdk.method', 'put') - ->label('sdk.description', 'Mock a put request for SDK tests') + ->label('sdk.description', 'Mock a put request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -146,13 +174,16 @@ App::put('/v1/mock/tests/bar') }); App::delete('/v1/mock/tests/bar') - ->desc('Mock a delete request for SDK tests') + ->desc('Delete Bar') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'bar') ->label('sdk.method', 'delete') - ->label('sdk.description', 'Mock a delete request for SDK tests') + ->label('sdk.description', 'Mock a delete request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -167,8 +198,11 @@ App::post('/v1/mock/tests/general/upload') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'upload') - ->label('sdk.description', 'Mock a delete request for SDK tests') + ->label('sdk.description', 'Mock a file upload request.') ->label('sdk.request.type', 'multipart/form-data') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -209,7 +243,9 @@ App::get('/v1/mock/tests/general/redirect') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'redirect') - ->label('sdk.description', 'Mock a redirect request for SDK tests') + ->label('sdk.description', 'Mock a redirect request.') + ->label('sdk.response.code', Response::STATUS_CODE_MOVED_PERMANENTLY) + ->label('sdk.response.type', Response::CONTENT_TYPE_HTML) ->label('sdk.mock', true) ->inject('response') ->action(function ($response) { @@ -219,13 +255,16 @@ App::get('/v1/mock/tests/general/redirect') }); App::get('/v1/mock/tests/general/redirect/done') - ->desc('Redirect Target') + ->desc('Redirected') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'redirected') - ->label('sdk.description', 'Mock a redirected request for SDK tests') + ->label('sdk.description', 'Mock a redirected request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->action(function () { }); @@ -238,6 +277,9 @@ App::get('/v1/mock/tests/general/set-cookie') ->label('sdk.namespace', 'general') ->label('sdk.method', 'setCookie') ->label('sdk.description', 'Mock a set cookie request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->inject('response') ->action(function ($response) { @@ -254,6 +296,9 @@ App::get('/v1/mock/tests/general/get-cookie') ->label('sdk.namespace', 'general') ->label('sdk.method', 'getCookie') ->label('sdk.description', 'Mock a cookie response.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->inject('request') ->action(function ($request) { @@ -271,7 +316,7 @@ App::get('/v1/mock/tests/general/empty') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'empty') - ->label('sdk.description', 'Mock a an empty response body.') + ->label('sdk.description', 'Mock a an empty response.') ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.mock', true) @@ -315,7 +360,7 @@ App::get('/v1/mock/tests/general/500-error') }); App::get('/v1/mock/tests/general/oauth2') - ->desc('Mock an OAuth2 login route') + ->desc('OAuth Login') ->groups(['mock']) ->label('scope', 'public') ->label('docs', false) @@ -332,7 +377,7 @@ App::get('/v1/mock/tests/general/oauth2') }); App::get('/v1/mock/tests/general/oauth2/token') - ->desc('Mock an OAuth2 login route') + ->desc('OAuth2 Token') ->groups(['mock']) ->label('scope', 'public') ->label('docs', false) @@ -361,7 +406,7 @@ App::get('/v1/mock/tests/general/oauth2/token') }); App::get('/v1/mock/tests/general/oauth2/user') - ->desc('Mock an OAuth2 user route') + ->desc('OAuth2 User') ->groups(['mock']) ->label('scope', 'public') ->label('docs', false) @@ -382,8 +427,9 @@ App::get('/v1/mock/tests/general/oauth2/user') }); App::get('/v1/mock/tests/general/oauth2/success') - ->label('scope', 'public') + ->desc('OAuth2 Success') ->groups(['mock']) + ->label('scope', 'public') ->label('docs', false) ->inject('response') ->action(function ($response) { @@ -395,6 +441,7 @@ App::get('/v1/mock/tests/general/oauth2/success') }); App::get('/v1/mock/tests/general/oauth2/failure') + ->desc('OAuth2 Failure') ->groups(['mock']) ->label('scope', 'public') ->label('docs', false) @@ -431,5 +478,5 @@ App::shutdown(function($utopia, $response, $request) { throw new Exception('Failed to save resutls', 500); } - $response->json(['result' => $route->getMethod() . ':' . $route->getURL() . ':passed']); + $response->dynamic(new Document(['result' => $route->getMethod() . ':' . $route->getURL() . ':passed']), Response::MODEL_MOCK); }, ['utopia', 'response', 'request'], 'mock'); \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index b2203918d5..e7f7e4e84e 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -39,6 +39,7 @@ use Appwrite\Utopia\Response\Model\Tag; use Appwrite\Utopia\Response\Model\Task; use Appwrite\Utopia\Response\Model\Token; use Appwrite\Utopia\Response\Model\Webhook; +use Appwrite\Utopia\Response\Model\Mock; // Keep last use stdClass; /** @@ -115,6 +116,9 @@ class Response extends SwooleResponse const MODEL_PLATFORM_LIST = 'platformList'; const MODEL_DOMAIN = 'domain'; const MODEL_DOMAIN_LIST = 'domainList'; + + // Tests (keep last) + const MODEL_MOCK = 'mock'; /** * @var Filter @@ -191,6 +195,8 @@ class Response extends SwooleResponse ->setModel(new Phone()) // Verification // Recovery + // Tests (keep last) + ->setModel(new Mock()) ; parent::__construct($response); diff --git a/src/Appwrite/Utopia/Response/Model/Mock.php b/src/Appwrite/Utopia/Response/Model/Mock.php new file mode 100644 index 0000000000..dade6e00d9 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/Mock.php @@ -0,0 +1,41 @@ +addRule('result', [ + 'type' => self::TYPE_STRING, + 'description' => 'Result message.', + 'default' => '', + 'example' => 'Success', + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'Mock'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_MOCK; + } +} \ No newline at end of file From 38c2c53d2d5f5b641bfa184f1d90f881afece9cb Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Fri, 5 Mar 2021 15:04:40 +0545 Subject: [PATCH 031/195] upgate changes --- CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 490007bcd6..f83de6bcb4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,9 @@ +# Version 0.7.1 + +## Bug Fixs + +- Fixed a bug, that Response format header was not added in the access-control-allow-header list. + # Version 0.7.0 ## Features From 0546a0e428483c85574d84125adcd5d58a292f1c Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 5 Mar 2021 13:36:30 -0500 Subject: [PATCH 032/195] Return correct response model --- app/config/events.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config/events.php b/app/config/events.php index 4893f6f188..601b502163 100644 --- a/app/config/events.php +++ b/app/config/events.php @@ -119,7 +119,7 @@ return [ ], 'users.update.prefs' => [ 'description' => 'This event triggers when a user preference is updated from the users API.', - 'model' => Response::MODEL_USER, + 'model' => Response::MODEL_ANY, 'note' => 'version >= 0.7', ], 'users.update.status' => [ From cc532dbc593638eadba4d7cfc1959002394a35ab Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sun, 7 Mar 2021 10:47:49 +0200 Subject: [PATCH 033/195] Getting ready for 0.7.1 release --- CHANGES.md | 2 +- README.md | 6 +++--- app/init.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8670aa1d7e..78de748d4b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ - Anonymous login -# Version 0.7.1 (Not Released Yet) +# Version 0.7.1 ## Features diff --git a/README.md b/README.md index 950c5a3010..3d4bd75733 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:0.7.0 + appwrite/appwrite:0.7.1 ``` ### Windows @@ -65,7 +65,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:0.7.0 + appwrite/appwrite:0.7.1 ``` #### PowerShell @@ -75,7 +75,7 @@ docker run -it --rm , --volume /var/run/docker.sock:/var/run/docker.sock , --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw , --entrypoint="install" , - appwrite/appwrite:0.7.0 + appwrite/appwrite:0.7.1 ``` Once the Docker installation completes, go to http://localhost to access the Appwrite console from your browser. Please note that on non-linux native hosts, the server might take a few minutes to start after installation completes. diff --git a/app/init.php b/app/init.php index a70ed1b88e..effa61ad56 100644 --- a/app/init.php +++ b/app/init.php @@ -40,7 +40,7 @@ const APP_MODE_DEFAULT = 'default'; const APP_MODE_ADMIN = 'admin'; const APP_PAGING_LIMIT = 12; const APP_CACHE_BUSTER = 144; -const APP_VERSION_STABLE = '0.7.0'; +const APP_VERSION_STABLE = '0.7.1'; const APP_STORAGE_UPLOADS = '/storage/uploads'; const APP_STORAGE_FUNCTIONS = '/storage/functions'; const APP_STORAGE_CACHE = '/storage/cache'; From 56f5c73126d2d9c02278040819be8b30764d602f Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Sun, 7 Mar 2021 08:09:45 -0500 Subject: [PATCH 034/195] Update tests --- .../Webhooks/WebhooksCustomServerTest.php | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php index 5869bddbbe..9b32b75609 100644 --- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php +++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php @@ -199,11 +199,10 @@ class WebhooksCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'prefs' => ['test' => true], + 'prefs' => ['a' => 'b'] ]); $this->assertEquals($user['headers']['status-code'], 200); - $this->assertNotEmpty($user['body']['prefs']); $webhook = $this->getLastRequest(); @@ -215,13 +214,7 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide())); - $this->assertNotEmpty($webhook['data']['$id']); - $this->assertEquals($webhook['data']['name'], $data['name']); - $this->assertIsInt($webhook['data']['registration']); - $this->assertEquals($webhook['data']['status'], 0); - $this->assertEquals($webhook['data']['email'], $data['email']); - $this->assertEquals($webhook['data']['emailVerification'], false); - $this->assertEquals($webhook['data']['prefs'], ["test" => true]); + $this->assertEquals($webhook['data']['a'], 'b'); return $data; } @@ -260,7 +253,7 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['data']['status'], 2); $this->assertEquals($webhook['data']['email'], $data['email']); $this->assertEquals($webhook['data']['emailVerification'], false); - $this->assertEquals($webhook['data']['prefs'], []); + $this->assertEquals($webhook['data']['prefs']['a'], 'b'); return $data; } @@ -296,7 +289,7 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['data']['status'], 2); $this->assertEquals($webhook['data']['email'], $data['email']); $this->assertEquals($webhook['data']['emailVerification'], false); - $this->assertEquals($webhook['data']['prefs'], []); + $this->assertEquals($webhook['data']['prefs']['a'], 'b'); return $data; } From 023b662b102bc1b4ce3aba91d27f58a579fb333b Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Mon, 8 Mar 2021 11:55:33 +0100 Subject: [PATCH 035/195] set default security email to certs@appwrite.io --- app/config/variables.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/config/variables.php b/app/config/variables.php index 14b29bc451..369a860fe2 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -107,8 +107,8 @@ return [ 'name' => '_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', 'description' => 'This is the email address used to issue SSL certificates for custom domains or the user agent in your webhooks payload.', 'introduction' => '0.7.0', - 'default' => '', - 'required' => true, + 'default' => 'certs@appwrite.io', + 'required' => false, 'question' => '', ], [ From 9fdfdbdc4b4ba4958a83870bcabc4e22cf1ef770 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 8 Mar 2021 17:00:39 +0545 Subject: [PATCH 036/195] getting started in the SDKs --- app/tasks/sdks.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/tasks/sdks.php b/app/tasks/sdks.php index f8d4ed3976..be99b7d15d 100644 --- a/app/tasks/sdks.php +++ b/app/tasks/sdks.php @@ -67,6 +67,8 @@ $cli $target = \realpath(__DIR__.'/..').'/sdks/git/'.$language['key'].'/'; $readme = \realpath(__DIR__ . '/../../docs/sdks/'.$language['key'].'/README.md'); $readme = ($readme) ? \file_get_contents($readme) : ''; + $gettingStarted = \realpath(__DIR__ . '/../../docs/sdks/'.$language['key'].'/GETTING_STARTED.md'); + $gettingStarted = ($gettingStarted) ? \file_get_contents($gettingStarted) : ''; $examples = \realpath(__DIR__ . '/../../docs/sdks/'.$language['key'].'/EXAMPLES.md'); $examples = ($examples) ? \file_get_contents($examples) : ''; $changelog = \realpath(__DIR__ . '/../../docs/sdks/'.$language['key'].'/CHANGELOG.md'); @@ -188,6 +190,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ->setShareVia('appwrite_io') ->setWarning($warning) ->setReadme($readme) + ->setGettingStarted($gettingStarted) ->setChangelog($changelog) ->setExamples($examples) ; From b087d8ffc3343d74a5daa36ab827c1875b30b6bc Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 8 Mar 2021 17:04:18 +0545 Subject: [PATCH 037/195] dart getting started section --- composer.json | 2 +- composer.lock | 97 ++++++++++++++----------------- docs/sdks/dart/GETTING_STARTED.md | 31 ++++++++++ 3 files changed, 76 insertions(+), 54 deletions(-) create mode 100644 docs/sdks/dart/GETTING_STARTED.md diff --git a/composer.json b/composer.json index 1e8954e049..39e8a0416d 100644 --- a/composer.json +++ b/composer.json @@ -60,7 +60,7 @@ "slickdeals/statsd": "~3.0" }, "require-dev": { - "appwrite/sdk-generator": "0.6.2", + "appwrite/sdk-generator": "dev-master", "phpunit/phpunit": "9.4.2", "swoole/ide-helper": "4.5.5", "vimeo/psalm": "4.1.1" diff --git a/composer.lock b/composer.lock index 41cf579ab4..691ed7386f 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "442d6d8b6c76ef7ae5ea26e500af6479", + "content-hash": "a328295b2b11cf89bbae0f3a8d9143ee", "packages": [ { "name": "adhocore/jwt", @@ -466,12 +466,12 @@ "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "ddfeedfff2a52661429437da0702979f708e6ac6" + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/ddfeedfff2a52661429437da0702979f708e6ac6", - "reference": "ddfeedfff2a52661429437da0702979f708e6ac6", + "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", "shasum": "" }, "require": { @@ -512,9 +512,9 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/master" + "source": "https://github.com/guzzle/promises/tree/1.4.1" }, - "time": "2020-10-19T16:50:15+00:00" + "time": "2021-03-07T09:25:29+00:00" }, { "name": "guzzlehttp/psr7", @@ -522,12 +522,12 @@ "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "2f3e4f6cf8fd4aad7624c90a94f0ab38fde25976" + "reference": "64245c4a03d43ddb638c4ff0b2d330c2af726ec5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/2f3e4f6cf8fd4aad7624c90a94f0ab38fde25976", - "reference": "2f3e4f6cf8fd4aad7624c90a94f0ab38fde25976", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/64245c4a03d43ddb638c4ff0b2d330c2af726ec5", + "reference": "64245c4a03d43ddb638c4ff0b2d330c2af726ec5", "shasum": "" }, "require": { @@ -589,7 +589,7 @@ "issues": "https://github.com/guzzle/psr7/issues", "source": "https://github.com/guzzle/psr7/tree/1.x" }, - "time": "2021-03-02T18:57:24+00:00" + "time": "2021-03-07T16:17:45+00:00" }, { "name": "influxdb/influxdb-php", @@ -2063,20 +2063,20 @@ }, { "name": "webmozart/assert", - "version": "dev-master", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "4631e2c7d2d7132adac9fd84d4c1a98c10a6e049" + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/4631e2c7d2d7132adac9fd84d4c1a98c10a6e049", - "reference": "4631e2c7d2d7132adac9fd84d4c1a98c10a6e049", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", + "php": "^5.3.3 || ^7.0 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -2084,15 +2084,9 @@ "vimeo/psalm": "<3.9.1" }, "require-dev": { - "phpunit/phpunit": "^8.5.13" + "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, - "default-branch": true, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -2116,9 +2110,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/master" + "source": "https://github.com/webmozarts/assert/tree/1.9.1" }, - "time": "2021-02-28T20:01:57+00:00" + "time": "2020-07-08T17:02:28+00:00" } ], "packages-dev": [ @@ -2292,11 +2286,11 @@ }, { "name": "appwrite/sdk-generator", - "version": "0.6.2", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator", - "reference": "d96376295c9265a054634d21dcdbfc02b832c677" + "reference": "583248c57c5bcbd9c74f8312cc7fc3ab6cda51a3" }, "require": { "ext-curl": "*", @@ -2309,6 +2303,7 @@ "require-dev": { "phpunit/phpunit": "^7.0" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -2326,7 +2321,7 @@ } ], "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", - "time": "2021-03-03T12:53:32+00:00" + "time": "2021-03-07T08:45:05+00:00" }, { "name": "composer/package-versions-deprecated", @@ -3277,12 +3272,12 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "e3324ecbde7319b0bbcf0fd7ca4af19469c38da9" + "reference": "f8d350d8514ff60b5993dd0121c62299480c989c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e3324ecbde7319b0bbcf0fd7ca4af19469c38da9", - "reference": "e3324ecbde7319b0bbcf0fd7ca4af19469c38da9", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/f8d350d8514ff60b5993dd0121c62299480c989c", + "reference": "f8d350d8514ff60b5993dd0121c62299480c989c", "shasum": "" }, "require": { @@ -3326,7 +3321,7 @@ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" }, - "time": "2020-11-18T14:27:38+00:00" + "time": "2021-03-07T11:12:25+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -3872,28 +3867,22 @@ }, { "name": "psr/container", - "version": "dev-master", + "version": "1.1.x-dev", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "381524e8568e07f31d504a945b88556548c8c42e" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/381524e8568e07f31d504a945b88556548c8c42e", - "reference": "381524e8568e07f31d504a945b88556548c8c42e", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { "php": ">=7.2.0" }, - "default-branch": true, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -3920,9 +3909,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/master" + "source": "https://github.com/php-fig/container/tree/1.1.x" }, - "time": "2020-10-13T07:07:53+00:00" + "time": "2021-03-05T17:36:06+00:00" }, { "name": "sebastian/cli-parser", @@ -4944,12 +4933,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c08d7d0d458eceb62996d81d3be8d9fbf5564ec4" + "reference": "92e1b7c4381c3c51e327b99c7ff067411e291783" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c08d7d0d458eceb62996d81d3be8d9fbf5564ec4", - "reference": "c08d7d0d458eceb62996d81d3be8d9fbf5564ec4", + "url": "https://api.github.com/repos/symfony/console/zipball/92e1b7c4381c3c51e327b99c7ff067411e291783", + "reference": "92e1b7c4381c3c51e327b99c7ff067411e291783", "shasum": "" }, "require": { @@ -5034,7 +5023,7 @@ "type": "tidelift" } ], - "time": "2021-02-23T10:10:15+00:00" + "time": "2021-03-06T13:50:37+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -5454,17 +5443,17 @@ "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "e830e6ceebd6377b019e4c9a523d6f2c27007e4a" + "reference": "96cd360b9f03a22a30cf5354e630c557bd3aac33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e830e6ceebd6377b019e4c9a523d6f2c27007e4a", - "reference": "e830e6ceebd6377b019e4c9a523d6f2c27007e4a", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/96cd360b9f03a22a30cf5354e630c557bd3aac33", + "reference": "96cd360b9f03a22a30cf5354e630c557bd3aac33", "shasum": "" }, "require": { "php": ">=7.2.5", - "psr/container": "^1.0" + "psr/container": "^1.1" }, "suggest": { "symfony/service-implementation": "" @@ -5526,7 +5515,7 @@ "type": "tidelift" } ], - "time": "2021-02-25T16:38:04+00:00" + "time": "2021-03-05T22:51:52+00:00" }, { "name": "symfony/string", @@ -5900,7 +5889,9 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": [], + "stability-flags": { + "appwrite/sdk-generator": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/docs/sdks/dart/GETTING_STARTED.md b/docs/sdks/dart/GETTING_STARTED.md new file mode 100644 index 0000000000..02923733c3 --- /dev/null +++ b/docs/sdks/dart/GETTING_STARTED.md @@ -0,0 +1,31 @@ +## Getting Started + +[Add Appwrite](https://github.com/appwrite/sdk-for-dart) SDK to your project’s **pubspec.yaml** dependencies. + +```yaml +dependencies: + Dart_appwrite: ^0.1.0 +``` + +Then get the dependencies by running +```bash +dart pub get +``` + +### Initialize & Make API Request +Once you add the dependencies, its extremely easy to get started with the SDK; All you need to do is import the package in your code, set your Appwrite credentials, and start making API calls. Below is a simple example: + +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +void main() async { + Client client = Client(); + .setEndpoint( + 'http://[HOSTNAME_OR_IP]/v1') // Make sure your endpoint is accessible + .setProject('5ff3379a01d25') // Your project ID + .setKey('cd868c7af8bdc893b4...93b7535db89') + Users users = Users(client); + final response = await users.create(email: ‘email@example.com’,password: ‘password’, name: ‘name’); + print(response.data); +} +``` \ No newline at end of file From 68b1511672ee22c0a79c97996e483ec540d87a52 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 8 Mar 2021 10:16:28 -0500 Subject: [PATCH 038/195] Chain tests together --- tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php index 9b32b75609..dbd1709802 100644 --- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php +++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php @@ -203,7 +203,6 @@ class WebhooksCustomServerTest extends Scope ]); $this->assertEquals($user['headers']['status-code'], 200); - $webhook = $this->getLastRequest(); $this->assertEquals($webhook['method'], 'POST'); @@ -220,7 +219,7 @@ class WebhooksCustomServerTest extends Scope } /** - * @depends testCreateUser + * @depends testUpdateUserPrefs */ public function testUpdateUserStatus(array $data):array { From 5eaced933dfd779269582c34c4b9510c43c30629 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 8 Mar 2021 14:58:16 -0500 Subject: [PATCH 039/195] Use uppercase countryCode as index for $countries assoc array --- app/controllers/api/account.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index e8ae76312b..cab25eb746 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -236,9 +236,11 @@ App::post('/v1/account/sessions') ->setStatusCode(Response::STATUS_CODE_CREATED) ; + $countries = $locale->getText('countries'); + $session ->setAttribute('current', true) - ->setAttribute('countryName', (isset($countries[$session->getAttribute('countryCode')])) ? $countries[$session->getAttribute('countryCode')] : $locale->getText('locale.country.unknown')) + ->setAttribute('countryName', (isset($countries[strtoupper($session->getAttribute('countryCode'))])) ? $countries[strtoupper($session->getAttribute('countryCode'))] : $locale->getText('locale.country.unknown')) ; $response->dynamic($session, Response::MODEL_SESSION); @@ -679,8 +681,8 @@ App::get('/v1/account/sessions') continue; } - $token->setAttribute('countryName', (isset($countries[$token->getAttribute('contryCode')])) - ? $countries[$token->getAttribute('contryCode')] + $token->setAttribute('countryName', (isset($countries[strtoupper($token->getAttribute('countryCode'))])) + ? $countries[strtoupper($token->getAttribute('countryCode'))] : $locale->getText('locale.country.unknown')); $token->setAttribute('current', ($current == $token->getId()) ? true : false); From 0d75dd362343e2635e07800b1e9eacb0c142003d Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 8 Mar 2021 15:48:45 -0500 Subject: [PATCH 040/195] Prove user prefs update before webhook --- tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php index dbd1709802..d2d5549134 100644 --- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php +++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php @@ -203,6 +203,8 @@ class WebhooksCustomServerTest extends Scope ]); $this->assertEquals($user['headers']['status-code'], 200); + $this->assertEquals($user['body']['a'], 'b'); + $webhook = $this->getLastRequest(); $this->assertEquals($webhook['method'], 'POST'); From c87a1165f68aef363c4bbb26478c94c2458305b7 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 9 Mar 2021 06:00:19 -0500 Subject: [PATCH 041/195] Update changelog --- CHANGES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 6261981848..d0cd072bc4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,7 +6,7 @@ ## Features -- Better error logs on appwrite cretificates worker +- Better error logs on appwrite certificates worker - Added option for Redis authentication - Force adding a security email on setup - SMTP is now disabled by default, no dummy SMTP is included in setup @@ -28,6 +28,7 @@ - Updated missing storage env vars - Fixed a bug, that Response format header was not added in the access-control-allow-header list. +- Fixed a bug where countryName is unknown on sessions (#933) ## Security From ed447986eaf3f17724146f0771ba6a184ec6fe2f Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 9 Mar 2021 06:03:42 -0500 Subject: [PATCH 042/195] Use uppercase countryCode --- app/controllers/api/users.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index efb0041cee..dbff5a0191 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -205,8 +205,8 @@ App::get('/v1/users/:userId/sessions') continue; } - $token->setAttribute('countryName', (isset($countries[$token->getAttribute('contryCode')])) - ? $countries[$token->getAttribute('contryCode')] + $token->setAttribute('countryName', (isset($countries[strtoupper($token->getAttribute('contryCode'))])) + ? $countries[strtoupper($token->getAttribute('contryCode'))] : $locale->getText('locale.country.unknown')); $token->setAttribute('current', false); From 7e12b82d75bd47c7f3342abd98787d921472ea52 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 9 Mar 2021 09:21:37 -0500 Subject: [PATCH 043/195] Use updated package paths --- app/preload.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/preload.php b/app/preload.php index f73aaea61a..7c8ae00938 100644 --- a/app/preload.php +++ b/app/preload.php @@ -28,10 +28,9 @@ foreach ([ realpath(__DIR__ . '/../vendor/felixfbecker'), realpath(__DIR__ . '/../vendor/twig/twig'), realpath(__DIR__ . '/../vendor/guzzlehttp/guzzle'), - realpath(__DIR__ . '/../vendor/domnikl'), - realpath(__DIR__ . '/../vendor/domnikl'), + realpath(__DIR__ . '/../vendor/slickdeals'), realpath(__DIR__ . '/../vendor/psr/log'), - realpath(__DIR__ . '/../vendor/piwik'), + realpath(__DIR__ . '/../vendor/matomo'), realpath(__DIR__ . '/../vendor/symfony'), ] as $key => $value) { if($value !== false) { From 815358173cfdee440f83c0f169fade91ea3c6b7a Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 9 Mar 2021 20:09:47 +0200 Subject: [PATCH 044/195] Updated SDK-Generator --- composer.json | 2 +- composer.lock | 92 ++++++++++++++++++++++----------------------------- 2 files changed, 41 insertions(+), 53 deletions(-) diff --git a/composer.json b/composer.json index f84268963c..d6e0e3ea87 100644 --- a/composer.json +++ b/composer.json @@ -59,7 +59,7 @@ "adhocore/jwt": "1.1.0" }, "require-dev": { - "appwrite/sdk-generator": "0.6.2", + "appwrite/sdk-generator": "0.6.3", "phpunit/phpunit": "9.4.2", "swoole/ide-helper": "4.5.5", "vimeo/psalm": "4.1.1" diff --git a/composer.lock b/composer.lock index e7b549f246..9753cff07e 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "eebe34132bbd166e74c848453fe2068e", + "content-hash": "2b47762c56fd4622b111787819f22a9f", "packages": [ { "name": "adhocore/jwt", @@ -518,12 +518,12 @@ "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "ddfeedfff2a52661429437da0702979f708e6ac6" + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/ddfeedfff2a52661429437da0702979f708e6ac6", - "reference": "ddfeedfff2a52661429437da0702979f708e6ac6", + "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", "shasum": "" }, "require": { @@ -564,9 +564,9 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/master" + "source": "https://github.com/guzzle/promises/tree/1.4.1" }, - "time": "2020-10-19T16:50:15+00:00" + "time": "2021-03-07T09:25:29+00:00" }, { "name": "guzzlehttp/psr7", @@ -574,12 +574,12 @@ "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "2f3e4f6cf8fd4aad7624c90a94f0ab38fde25976" + "reference": "d7fe0a0eabc266c3dcf2f20aa12121044ff196a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/2f3e4f6cf8fd4aad7624c90a94f0ab38fde25976", - "reference": "2f3e4f6cf8fd4aad7624c90a94f0ab38fde25976", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/d7fe0a0eabc266c3dcf2f20aa12121044ff196a4", + "reference": "d7fe0a0eabc266c3dcf2f20aa12121044ff196a4", "shasum": "" }, "require": { @@ -641,7 +641,7 @@ "issues": "https://github.com/guzzle/psr7/issues", "source": "https://github.com/guzzle/psr7/tree/1.x" }, - "time": "2021-03-02T18:57:24+00:00" + "time": "2021-03-09T14:42:40+00:00" }, { "name": "influxdb/influxdb-php", @@ -2086,11 +2086,11 @@ }, { "name": "appwrite/sdk-generator", - "version": "0.6.2", + "version": "0.6.3", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator", - "reference": "d96376295c9265a054634d21dcdbfc02b832c677" + "reference": "583248c57c5bcbd9c74f8312cc7fc3ab6cda51a3" }, "require": { "ext-curl": "*", @@ -2120,7 +2120,7 @@ } ], "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", - "time": "2021-03-03T12:53:32+00:00" + "time": "2021-03-07T08:45:05+00:00" }, { "name": "composer/package-versions-deprecated", @@ -3071,12 +3071,12 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "e3324ecbde7319b0bbcf0fd7ca4af19469c38da9" + "reference": "f8d350d8514ff60b5993dd0121c62299480c989c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e3324ecbde7319b0bbcf0fd7ca4af19469c38da9", - "reference": "e3324ecbde7319b0bbcf0fd7ca4af19469c38da9", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/f8d350d8514ff60b5993dd0121c62299480c989c", + "reference": "f8d350d8514ff60b5993dd0121c62299480c989c", "shasum": "" }, "require": { @@ -3120,7 +3120,7 @@ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" }, - "time": "2020-11-18T14:27:38+00:00" + "time": "2021-03-07T11:12:25+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -3666,28 +3666,22 @@ }, { "name": "psr/container", - "version": "dev-master", + "version": "1.1.x-dev", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "381524e8568e07f31d504a945b88556548c8c42e" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/381524e8568e07f31d504a945b88556548c8c42e", - "reference": "381524e8568e07f31d504a945b88556548c8c42e", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { "php": ">=7.2.0" }, - "default-branch": true, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -3714,9 +3708,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/master" + "source": "https://github.com/php-fig/container/tree/1.1.x" }, - "time": "2020-10-13T07:07:53+00:00" + "time": "2021-03-05T17:36:06+00:00" }, { "name": "sebastian/cli-parser", @@ -4738,12 +4732,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c08d7d0d458eceb62996d81d3be8d9fbf5564ec4" + "reference": "4e102e4de39852a1dcd3b2169d263b88afee7fff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c08d7d0d458eceb62996d81d3be8d9fbf5564ec4", - "reference": "c08d7d0d458eceb62996d81d3be8d9fbf5564ec4", + "url": "https://api.github.com/repos/symfony/console/zipball/4e102e4de39852a1dcd3b2169d263b88afee7fff", + "reference": "4e102e4de39852a1dcd3b2169d263b88afee7fff", "shasum": "" }, "require": { @@ -4828,7 +4822,7 @@ "type": "tidelift" } ], - "time": "2021-02-23T10:10:15+00:00" + "time": "2021-03-08T21:52:55+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5328,17 +5322,17 @@ "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "e830e6ceebd6377b019e4c9a523d6f2c27007e4a" + "reference": "96cd360b9f03a22a30cf5354e630c557bd3aac33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e830e6ceebd6377b019e4c9a523d6f2c27007e4a", - "reference": "e830e6ceebd6377b019e4c9a523d6f2c27007e4a", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/96cd360b9f03a22a30cf5354e630c557bd3aac33", + "reference": "96cd360b9f03a22a30cf5354e630c557bd3aac33", "shasum": "" }, "require": { "php": ">=7.2.5", - "psr/container": "^1.0" + "psr/container": "^1.1" }, "suggest": { "symfony/service-implementation": "" @@ -5400,7 +5394,7 @@ "type": "tidelift" } ], - "time": "2021-02-25T16:38:04+00:00" + "time": "2021-03-05T22:51:52+00:00" }, { "name": "symfony/string", @@ -5722,20 +5716,20 @@ }, { "name": "webmozart/assert", - "version": "dev-master", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "4631e2c7d2d7132adac9fd84d4c1a98c10a6e049" + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/4631e2c7d2d7132adac9fd84d4c1a98c10a6e049", - "reference": "4631e2c7d2d7132adac9fd84d4c1a98c10a6e049", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", + "php": "^5.3.3 || ^7.0 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -5743,15 +5737,9 @@ "vimeo/psalm": "<3.9.1" }, "require-dev": { - "phpunit/phpunit": "^8.5.13" + "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, - "default-branch": true, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -5775,9 +5763,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/master" + "source": "https://github.com/webmozarts/assert/tree/1.9.1" }, - "time": "2021-02-28T20:01:57+00:00" + "time": "2020-07-08T17:02:28+00:00" }, { "name": "webmozart/path-util", From 59984fa6c04c97d708bb36ee30734f2f37ff5bc7 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 9 Mar 2021 14:58:03 -0500 Subject: [PATCH 045/195] Pass custom function $data to execution --- app/controllers/api/functions.php | 4 +++- app/workers/functions.php | 11 +++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 7a6b11bcda..d2d5520161 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -672,11 +672,12 @@ App::post('/v1/functions/:functionId/executions') ->label('abuse-limit', 60) ->label('abuse-time', 60) ->param('functionId', '', new UID(), 'Function unique ID.') + ->param('data', '', new Text(8192), 'String of custom data to send to function.', true) // ->param('async', 1, new Range(0, 1), 'Execute code asynchronously. Pass 1 for true, 0 for false. Default value is 1.', true) ->inject('response') ->inject('project') ->inject('projectDB') - ->action(function ($functionId, /*$async,*/ $response, $project, $projectDB) { + ->action(function ($functionId, $data, /*$async,*/ $response, $project, $projectDB) { /** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Database\Document $project */ /** @var Appwrite\Database\Database $projectDB */ @@ -736,6 +737,7 @@ App::post('/v1/functions/:functionId/executions') 'functionId' => $function->getId(), 'executionId' => $execution->getId(), 'trigger' => 'http', + 'data' => $data, ]); $response diff --git a/app/workers/functions.php b/app/workers/functions.php index 87e3a86245..98eb126ffc 100644 --- a/app/workers/functions.php +++ b/app/workers/functions.php @@ -148,6 +148,7 @@ class FunctionsV1 $event = $this->args['event'] ?? ''; $scheduleOriginal = $this->args['scheduleOriginal'] ?? ''; $payload = (!empty($this->args['payload'])) ? json_encode($this->args['payload']) : ''; + $data = $this->args['data'] ?? ''; $database = new Database(); $database->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register)); @@ -195,7 +196,7 @@ class FunctionsV1 Console::success('Triggered function: '.$event); - $this->execute('event', $projectId, '', $database, $function, $event, $payload); + $this->execute('event', $projectId, '', $database, $function, $event, $payload, $data); } } break; @@ -251,7 +252,7 @@ class FunctionsV1 'scheduleOriginal' => $function->getAttribute('schedule', ''), ]); // Async task rescheduale - $this->execute($trigger, $projectId, $executionId, $database, $function); + $this->execute($trigger, $projectId, $executionId, $database, $function, /*$event*/'', /*$payload*/'', $data); break; @@ -264,7 +265,7 @@ class FunctionsV1 throw new Exception('Function not found ('.$functionId.')'); } - $this->execute($trigger, $projectId, $executionId, $database, $function); + $this->execute($trigger, $projectId, $executionId, $database, $function, /*$event*/'', /*$payload*/'', $data); break; default: @@ -283,10 +284,11 @@ class FunctionsV1 * @param Database $function * @param string $event * @param string $payload + * @param string $data * * @return void */ - public function execute(string $trigger, string $projectId, string $executionId, Database $database, Document $function, string $event = '', string $payload = ''): void + public function execute(string $trigger, string $projectId, string $executionId, Database $database, Document $function, string $event = '', string $payload = '', string $data = ''): void { global $list; @@ -341,6 +343,7 @@ class FunctionsV1 'APPWRITE_FUNCTION_ENV_VERSION' => $environment['version'], 'APPWRITE_FUNCTION_EVENT' => $event, 'APPWRITE_FUNCTION_EVENT_PAYLOAD' => $payload, + 'APPWRITE_FUNCTION_DATA' => $data, ]); \array_walk($vars, function (&$value, $key) { From e157ac95844031933d1f935353bafb7196825046 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 10 Mar 2021 07:21:25 +0545 Subject: [PATCH 046/195] updates --- docs/sdks/dart/GETTING_STARTED.md | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/docs/sdks/dart/GETTING_STARTED.md b/docs/sdks/dart/GETTING_STARTED.md index 02923733c3..02a3aa631b 100644 --- a/docs/sdks/dart/GETTING_STARTED.md +++ b/docs/sdks/dart/GETTING_STARTED.md @@ -1,17 +1,5 @@ ## Getting Started -[Add Appwrite](https://github.com/appwrite/sdk-for-dart) SDK to your project’s **pubspec.yaml** dependencies. - -```yaml -dependencies: - Dart_appwrite: ^0.1.0 -``` - -Then get the dependencies by running -```bash -dart pub get -``` - ### Initialize & Make API Request Once you add the dependencies, its extremely easy to get started with the SDK; All you need to do is import the package in your code, set your Appwrite credentials, and start making API calls. Below is a simple example: @@ -24,7 +12,9 @@ void main() async { 'http://[HOSTNAME_OR_IP]/v1') // Make sure your endpoint is accessible .setProject('5ff3379a01d25') // Your project ID .setKey('cd868c7af8bdc893b4...93b7535db89') + Users users = Users(client); + final response = await users.create(email: ‘email@example.com’,password: ‘password’, name: ‘name’); print(response.data); } From 13c7ded9dbec689c49a852c7e9e4ef77de24a148 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 10 Mar 2021 12:29:28 +0545 Subject: [PATCH 047/195] Flutter getting started --- docs/sdks/flutter/GETTING_STARTED.md | 112 +++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 docs/sdks/flutter/GETTING_STARTED.md diff --git a/docs/sdks/flutter/GETTING_STARTED.md b/docs/sdks/flutter/GETTING_STARTED.md new file mode 100644 index 0000000000..bd9b635786 --- /dev/null +++ b/docs/sdks/flutter/GETTING_STARTED.md @@ -0,0 +1,112 @@ +## Getting Started for Flutter + +### Create Your First Appwrite Project +Go to your new Appwrite console, and once inside, click the (plus) icon in the top navigation header or on the **'Create Project'** button on your console homepage. Choose a name for your project and click create to get started. + +### Add your Flutter Platform +To init your SDK and start interacting with Appwrite services, you need to add a new Flutter platform to your project. To add a new platform, go to your Appwrite console, choose the project you created in the step before, and click the 'Add Platform' button. + +From the options, choose to add a new **Flutter** platform and add your app credentials. Appwrite Flutter SDK currently supports building apps for both iOS and Android. + +If you are building your Flutter application for multiple devices, you have to follow this process for each different device. + +### iOS +For **iOS** add your app name and Bundle ID, You can find your Bundle Identifier in the General tab for your app's primary target in Xcode. + +### Android +For **Android** add your app name and package name, Your package name is generally the applicationId in your app-level build.gradle file. By registering your new app platform, you are allowing your app to communicate with the Appwrite API. + +#### iOS + +The Appwrite SDK uses ASWebAuthenticationSession on iOS 12+ and SFAuthenticationSession on iOS 11 to allow OAuth authentication. You have to change your iOS Deployment Target in Xcode to be iOS >= 11 to be able to build your app on an emulator or a real device. + +1. In Xcode, open Runner.xcworkspace in your app's ios folder. +2. To view your app's settings, select the Runner project in the Xcode project navigator. Then, in the main view sidebar, select the Runner target. +3. Select the General tab. +4. In Deployment Info, 'Target' select iOS 11.0 + +### Android +In order to capture the Appwrite OAuth callback url, the following activity needs to be added to your AndroidManifest.xml. Be sure to relpace the **[PROJECT_ID]** string with your actual Appwrite project ID. You can find your Appwrite project ID in you project settings screen in your Appwrite console. + +``` + + + + + + + + + + + + +``` + +#### Web +Appwrite 0.7, and the Appwrite Flutter SDK 0.3.0 have added support for Flutter Web. To build web apps that integrate with Appwrite successfully, all you have to do is add a web platform on your Appwrite project's dashboard and list the domain your website will use to allow communication to the Appwrite API.

+ +### Flutter Web Cross-Domain Communication & Cookies +

While running Flutter Web, make sure your Appwrite server and your Flutter client are using the same top-level domain and the same protocol (HTTP or HTTPS) to communicate. When trying to communicate between different domains or protocols, you may receive HTTP status error 401 because some modern browsers block cross-site or insecure cookies for enhanced privacy. In production, Appwrite allows you set multiple custom-domains for each project.

+ +### Init your SDK + +

Initialize your SDK code with your project ID, which can be found in your project settings page. + +``` +import 'package:appwrite/appwrite.dart'; +Client client = Client(); + + +client +.setEndpoint('https://localhost/v1') // Your Appwrite Endpoint +.setProject('5e8cf4f46b5e8') // Your project ID +.setSelfSigned() // Remove in production +; +``` + +

Before starting to send any API calls to your new Appwrite instance, make sure your Android or iOS emulators has network access to the Appwrite server hostname or IP address.

When trying to connect to Appwrite from an emulator or a mobile device, localhost is the hostname for the device or emulator and not your local Appwrite instance. You should replace localhost with your private IP as the Appwrite endpoint's hostname. You can also use a service like ngrok to proxy the Appwrite API.

+ +### Make Your First Request + +

Once your SDK object is set, access any of the Appwrite services and choose any request to send. Full documentation for any service method you would like to use can be found in your SDK documentation or in the API References section. + +``` +// Register User +Account account = Account(client); +Response user = await account +.create( +email: 'me@appwrite.io', +password: 'password', +name: 'My Name' +); +``` + +### Full Example + +``` +import 'package:appwrite/appwrite.dart'; +Client client = Client(); + + +client +.setEndpoint('https://localhost/v1') // Your Appwrite Endpoint +.setProject('5e8cf4f46b5e8') // Your project ID +.setSelfSigned() // Remove in production +; + + +// Register User +Account account = Account(client); + + +Response user = await account +.create( +email: 'me@appwrite.io', +password: 'password', +name: 'My Name' +); +``` + +### Next Steps +Appwrite has many services and tools to help improve your app and speed up your development. The best way to learn how you can take advantage of them is to explore the different API references docs. \ No newline at end of file From 3f96ff85b8e7771c0f175fd6e8da8a587f1e8ac2 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 10 Mar 2021 12:38:51 +0545 Subject: [PATCH 048/195] deno getting started --- docs/sdks/deno/GETTING_STARTED.md | 53 +++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 docs/sdks/deno/GETTING_STARTED.md diff --git a/docs/sdks/deno/GETTING_STARTED.md b/docs/sdks/deno/GETTING_STARTED.md new file mode 100644 index 0000000000..c9865ea381 --- /dev/null +++ b/docs/sdks/deno/GETTING_STARTED.md @@ -0,0 +1,53 @@ +## Getting Started + +### Init your SDK +Initialize your SDK code with your project ID which can be found in your project settings page and your new API secret Key from previous phase. + +```typescript +let client = new sdk.Client(); + +client + .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint + .setProject('5df5acd0d48c2') // Your project ID + .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key +; + +``` + +### Make your first request + +Once your SDK object is set, create any of the Appwrite service project objects and choose any request to send. Full documentation for any service method you would like to use can be found in your SDK documentation or in the API References section. + +```typescript +let users = new sdk.Users(client); + +let promise = users.create('email@example.com', 'password'); + +promise.then(function (response) { + console.log(response); +}, function (error) { + console.log(error); +}); +``` + +### Full Example +```typescript +import * as sdk from "https://deno.land/x/appwrite/mod.ts"; + +let client = new sdk.Client(); +let users = new sdk.Users(client); + +client + .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint + .setProject('5df5acd0d48c2') // Your project ID + .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key +; + +let promise = users.create('email@example.com', 'password'); + +promise.then(function (response) { + console.log(response); +}, function (error) { + console.log(error); +}); +``` \ No newline at end of file From 691792fb99fca03fda292a97009069b2595be784 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 10 Mar 2021 13:52:30 +0200 Subject: [PATCH 049/195] Updated the Dart SDK --- app/config/platforms.php | 2 +- composer.lock | 128 +++++++++++++++++++-------------------- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 4e4bde824a..38e4f3d3b6 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -284,7 +284,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '0.3.1', + 'version' => '0.4.0', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, diff --git a/composer.lock b/composer.lock index 9753cff07e..4390dffc2d 100644 --- a/composer.lock +++ b/composer.lock @@ -3322,12 +3322,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "dae425925709122f7584cadeeb838edcaa491bb1" + "reference": "330949c62cbc3e44120990701c949e59a4f3e141" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/dae425925709122f7584cadeeb838edcaa491bb1", - "reference": "dae425925709122f7584cadeeb838edcaa491bb1", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/330949c62cbc3e44120990701c949e59a4f3e141", + "reference": "330949c62cbc3e44120990701c949e59a4f3e141", "shasum": "" }, "require": { @@ -3375,7 +3375,7 @@ "type": "github" } ], - "time": "2021-02-23T15:48:43+00:00" + "time": "2021-03-10T06:29:10+00:00" }, { "name": "phpunit/php-invoker", @@ -3383,12 +3383,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5ad9e5f5d6ee1a837e1d50bab1017e0daf423b40" + "reference": "fe3276f5cd81d19a8e8ef90a32855545f7aae7cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5ad9e5f5d6ee1a837e1d50bab1017e0daf423b40", - "reference": "5ad9e5f5d6ee1a837e1d50bab1017e0daf423b40", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/fe3276f5cd81d19a8e8ef90a32855545f7aae7cb", + "reference": "fe3276f5cd81d19a8e8ef90a32855545f7aae7cb", "shasum": "" }, "require": { @@ -3439,7 +3439,7 @@ "type": "github" } ], - "time": "2021-02-23T15:48:51+00:00" + "time": "2021-03-10T06:29:18+00:00" }, { "name": "phpunit/php-text-template", @@ -3447,12 +3447,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "4ec5a2ac79a19b35d0cf83cce30604f77743067a" + "reference": "11d864dc75b7f73d1e03361bff717894587f3987" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/4ec5a2ac79a19b35d0cf83cce30604f77743067a", - "reference": "4ec5a2ac79a19b35d0cf83cce30604f77743067a", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/11d864dc75b7f73d1e03361bff717894587f3987", + "reference": "11d864dc75b7f73d1e03361bff717894587f3987", "shasum": "" }, "require": { @@ -3499,7 +3499,7 @@ "type": "github" } ], - "time": "2021-02-23T15:49:24+00:00" + "time": "2021-03-10T06:29:48+00:00" }, { "name": "phpunit/php-timer", @@ -3507,12 +3507,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "705821b0927b5e69e9e016c84de68dc6195c71b9" + "reference": "95242c4aa540e9b3655c7edbe8f76d55ac237b7b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/705821b0927b5e69e9e016c84de68dc6195c71b9", - "reference": "705821b0927b5e69e9e016c84de68dc6195c71b9", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/95242c4aa540e9b3655c7edbe8f76d55ac237b7b", + "reference": "95242c4aa540e9b3655c7edbe8f76d55ac237b7b", "shasum": "" }, "require": { @@ -3559,7 +3559,7 @@ "type": "github" } ], - "time": "2021-02-23T15:48:59+00:00" + "time": "2021-03-10T06:29:26+00:00" }, { "name": "phpunit/phpunit", @@ -3718,12 +3718,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "3a42d843af4d27ca1155e1d926881af162733655" + "reference": "c8472024d13a267ba49f4c1e194a01cba5b094f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/3a42d843af4d27ca1155e1d926881af162733655", - "reference": "3a42d843af4d27ca1155e1d926881af162733655", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c8472024d13a267ba49f4c1e194a01cba5b094f5", + "reference": "c8472024d13a267ba49f4c1e194a01cba5b094f5", "shasum": "" }, "require": { @@ -3767,7 +3767,7 @@ "type": "github" } ], - "time": "2021-02-23T15:49:50+00:00" + "time": "2021-03-10T06:30:16+00:00" }, { "name": "sebastian/code-unit", @@ -3831,12 +3831,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5f5db0b35f586eb5bca0581a10bb42dd56575986" + "reference": "84710fb3a027eb62978539705a0cd00713d474c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5f5db0b35f586eb5bca0581a10bb42dd56575986", - "reference": "5f5db0b35f586eb5bca0581a10bb42dd56575986", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/84710fb3a027eb62978539705a0cd00713d474c8", + "reference": "84710fb3a027eb62978539705a0cd00713d474c8", "shasum": "" }, "require": { @@ -3879,7 +3879,7 @@ "type": "github" } ], - "time": "2021-02-23T15:47:39+00:00" + "time": "2021-03-10T06:28:05+00:00" }, { "name": "sebastian/comparator", @@ -3887,12 +3887,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "dbc5fb421f242a5749845dc8dd0dc8cde2979dd9" + "reference": "5dfac003e3be0ca24000cee2a2e19ba2f21aa8f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/dbc5fb421f242a5749845dc8dd0dc8cde2979dd9", - "reference": "dbc5fb421f242a5749845dc8dd0dc8cde2979dd9", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5dfac003e3be0ca24000cee2a2e19ba2f21aa8f8", + "reference": "5dfac003e3be0ca24000cee2a2e19ba2f21aa8f8", "shasum": "" }, "require": { @@ -3954,7 +3954,7 @@ "type": "github" } ], - "time": "2021-02-23T15:47:47+00:00" + "time": "2021-03-10T06:28:15+00:00" }, { "name": "sebastian/complexity", @@ -4019,12 +4019,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "93e6aa13f3dc5f8327e7fb9756e9655fc4c23e90" + "reference": "08ab1620f0f35c41e50d847433193da76d33151e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/93e6aa13f3dc5f8327e7fb9756e9655fc4c23e90", - "reference": "93e6aa13f3dc5f8327e7fb9756e9655fc4c23e90", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/08ab1620f0f35c41e50d847433193da76d33151e", + "reference": "08ab1620f0f35c41e50d847433193da76d33151e", "shasum": "" }, "require": { @@ -4078,7 +4078,7 @@ "type": "github" } ], - "time": "2021-02-23T15:47:55+00:00" + "time": "2021-03-10T06:28:23+00:00" }, { "name": "sebastian/environment", @@ -4086,12 +4086,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "6e1743b808be9cfd33a716583ccb94b7d4d32e94" + "reference": "e34aa76b02666b7f12417f2000b6d4fbb9c2016c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e1743b808be9cfd33a716583ccb94b7d4d32e94", - "reference": "6e1743b808be9cfd33a716583ccb94b7d4d32e94", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/e34aa76b02666b7f12417f2000b6d4fbb9c2016c", + "reference": "e34aa76b02666b7f12417f2000b6d4fbb9c2016c", "shasum": "" }, "require": { @@ -4142,7 +4142,7 @@ "type": "github" } ], - "time": "2021-02-23T15:48:03+00:00" + "time": "2021-03-10T06:28:31+00:00" }, { "name": "sebastian/exporter", @@ -4150,12 +4150,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "eca7281ab29075df68b113a37a83be616b629b12" + "reference": "889b30136f9f8a6c0c4d71954b772ac8b8d7feab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/eca7281ab29075df68b113a37a83be616b629b12", - "reference": "eca7281ab29075df68b113a37a83be616b629b12", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/889b30136f9f8a6c0c4d71954b772ac8b8d7feab", + "reference": "889b30136f9f8a6c0c4d71954b772ac8b8d7feab", "shasum": "" }, "require": { @@ -4220,7 +4220,7 @@ "type": "github" } ], - "time": "2021-02-23T15:48:12+00:00" + "time": "2021-03-10T06:28:38+00:00" }, { "name": "sebastian/global-state", @@ -4228,12 +4228,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ac702e6d13725242edb9b294c5d20b92fcfb8b4" + "reference": "8a1428d5351ea5dae3aa386d3b321499ac23adea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ac702e6d13725242edb9b294c5d20b92fcfb8b4", - "reference": "0ac702e6d13725242edb9b294c5d20b92fcfb8b4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/8a1428d5351ea5dae3aa386d3b321499ac23adea", + "reference": "8a1428d5351ea5dae3aa386d3b321499ac23adea", "shasum": "" }, "require": { @@ -4285,7 +4285,7 @@ "type": "github" } ], - "time": "2021-02-23T15:48:19+00:00" + "time": "2021-03-10T06:28:46+00:00" }, { "name": "sebastian/lines-of-code", @@ -4350,12 +4350,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "8cc80b4bda00a4c5997c3fc597a34872f3a1007d" + "reference": "b218fb1d63287edb7613b61122890f39e82ae8c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/8cc80b4bda00a4c5997c3fc597a34872f3a1007d", - "reference": "8cc80b4bda00a4c5997c3fc597a34872f3a1007d", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/b218fb1d63287edb7613b61122890f39e82ae8c2", + "reference": "b218fb1d63287edb7613b61122890f39e82ae8c2", "shasum": "" }, "require": { @@ -4400,7 +4400,7 @@ "type": "github" } ], - "time": "2021-02-23T15:48:28+00:00" + "time": "2021-03-10T06:28:54+00:00" }, { "name": "sebastian/object-reflector", @@ -4408,12 +4408,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "1d33587c2c3e636936f895e103a9e82dd8102a8e" + "reference": "cbf30bc9ed44451f5301480f668cd4fcf6bb225a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/1d33587c2c3e636936f895e103a9e82dd8102a8e", - "reference": "1d33587c2c3e636936f895e103a9e82dd8102a8e", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/cbf30bc9ed44451f5301480f668cd4fcf6bb225a", + "reference": "cbf30bc9ed44451f5301480f668cd4fcf6bb225a", "shasum": "" }, "require": { @@ -4456,7 +4456,7 @@ "type": "github" } ], - "time": "2021-02-23T15:48:35+00:00" + "time": "2021-03-10T06:29:02+00:00" }, { "name": "sebastian/recursion-context", @@ -4464,12 +4464,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "43f58a51e8f853aadb228ba818d2be388af7237b" + "reference": "c3333538e25ec932d0cbdce77b6ac846757b809d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/43f58a51e8f853aadb228ba818d2be388af7237b", - "reference": "43f58a51e8f853aadb228ba818d2be388af7237b", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/c3333538e25ec932d0cbdce77b6ac846757b809d", + "reference": "c3333538e25ec932d0cbdce77b6ac846757b809d", "shasum": "" }, "require": { @@ -4520,7 +4520,7 @@ "type": "github" } ], - "time": "2021-02-23T15:49:08+00:00" + "time": "2021-03-10T06:29:33+00:00" }, { "name": "sebastian/resource-operations", @@ -4584,12 +4584,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "557863473c1de00e165a288d5b547f1f83652e7e" + "reference": "1bba184dccb563769fab9bd69c623c1a353dec98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/557863473c1de00e165a288d5b547f1f83652e7e", - "reference": "557863473c1de00e165a288d5b547f1f83652e7e", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/1bba184dccb563769fab9bd69c623c1a353dec98", + "reference": "1bba184dccb563769fab9bd69c623c1a353dec98", "shasum": "" }, "require": { @@ -4633,7 +4633,7 @@ "type": "github" } ], - "time": "2021-02-23T15:49:16+00:00" + "time": "2021-03-10T06:29:41+00:00" }, { "name": "sebastian/version", @@ -5536,12 +5536,12 @@ "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "728c611e8643a5dd44839ffa791e21763b04a694" + "reference": "37e48403c21e06f63bc27d7ccd997fbb72b0ae2a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/728c611e8643a5dd44839ffa791e21763b04a694", - "reference": "728c611e8643a5dd44839ffa791e21763b04a694", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/37e48403c21e06f63bc27d7ccd997fbb72b0ae2a", + "reference": "37e48403c21e06f63bc27d7ccd997fbb72b0ae2a", "shasum": "" }, "require": { @@ -5607,7 +5607,7 @@ "type": "tidelift" } ], - "time": "2021-02-22T11:56:05+00:00" + "time": "2021-03-10T10:07:14+00:00" }, { "name": "vimeo/psalm", From 4e8d42b95002ea2e012abc74e7576777dc704c45 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 10 Mar 2021 14:40:24 +0200 Subject: [PATCH 050/195] Show Dart SDK on homepage --- app/config/platforms.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 38e4f3d3b6..ab7a30fdb1 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -289,7 +289,7 @@ return [ 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, 'beta' => true, - 'dev' => true, + 'dev' => false, 'hidden' => false, 'family' => APP_PLATFORM_SERVER, 'prism' => 'dart', From da8984f66e0389df7b966cfb794c666303855035 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 10 Mar 2021 11:58:46 -0500 Subject: [PATCH 051/195] Pass userId and JWT to function as env vars --- app/controllers/api/functions.php | 25 +++++++++++++++++++++++-- app/workers/functions.php | 12 ++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index d2d5520161..b3243ab93c 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -677,10 +677,12 @@ App::post('/v1/functions/:functionId/executions') ->inject('response') ->inject('project') ->inject('projectDB') - ->action(function ($functionId, $data, /*$async,*/ $response, $project, $projectDB) { + ->inject('user') + ->action(function ($functionId, $data, /*$async,*/ $response, $project, $projectDB, $user) { /** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Database\Document $project */ /** @var Appwrite\Database\Database $projectDB */ + /** @var Appwrite\Database\Document $user */ Authorization::disable(); @@ -731,13 +733,32 @@ App::post('/v1/functions/:functionId/executions') if (false === $execution) { throw new Exception('Failed saving execution to DB', 500); } - + + if (!empty($user->getId())) { // If userId exists, generate a JWT for function + + $tokens = $user->getAttribute('tokens', []); + $session = new Document(); + $jwt = ''; + + foreach ($tokens as $token) { /** @var Appwrite\Database\Document $token */ + if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + $session = $token; + } + } + + if(!$session->isEmpty()) { + $jwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. + } + } + Resque::enqueue('v1-functions', 'FunctionsV1', [ 'projectId' => $project->getId(), 'functionId' => $function->getId(), 'executionId' => $execution->getId(), 'trigger' => 'http', 'data' => $data, + 'userId' => $user->getId(), + 'jwt' => $jwt, ]); $response diff --git a/app/workers/functions.php b/app/workers/functions.php index 98eb126ffc..932c4cd589 100644 --- a/app/workers/functions.php +++ b/app/workers/functions.php @@ -149,6 +149,8 @@ class FunctionsV1 $scheduleOriginal = $this->args['scheduleOriginal'] ?? ''; $payload = (!empty($this->args['payload'])) ? json_encode($this->args['payload']) : ''; $data = $this->args['data'] ?? ''; + $userId = $this->args['userId'] ?? ''; + $jwt = $this->args['jwt'] ?? ''; $database = new Database(); $database->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register)); @@ -196,7 +198,7 @@ class FunctionsV1 Console::success('Triggered function: '.$event); - $this->execute('event', $projectId, '', $database, $function, $event, $payload, $data); + $this->execute('event', $projectId, '', $database, $function, $event, $payload, $data, $userId, $jwt); } } break; @@ -252,7 +254,7 @@ class FunctionsV1 'scheduleOriginal' => $function->getAttribute('schedule', ''), ]); // Async task rescheduale - $this->execute($trigger, $projectId, $executionId, $database, $function, /*$event*/'', /*$payload*/'', $data); + $this->execute($trigger, $projectId, $executionId, $database, $function, /*$event*/'', /*$payload*/'', $data, $userId, $jwt); break; @@ -265,7 +267,7 @@ class FunctionsV1 throw new Exception('Function not found ('.$functionId.')'); } - $this->execute($trigger, $projectId, $executionId, $database, $function, /*$event*/'', /*$payload*/'', $data); + $this->execute($trigger, $projectId, $executionId, $database, $function, /*$event*/'', /*$payload*/'', $data, $userId, $jwt); break; default: @@ -288,7 +290,7 @@ class FunctionsV1 * * @return void */ - public function execute(string $trigger, string $projectId, string $executionId, Database $database, Document $function, string $event = '', string $payload = '', string $data = ''): void + public function execute(string $trigger, string $projectId, string $executionId, Database $database, Document $function, string $event = '', string $payload = '', string $data = '', string $userId = '', string $jwt = ''): void { global $list; @@ -344,6 +346,8 @@ class FunctionsV1 'APPWRITE_FUNCTION_EVENT' => $event, 'APPWRITE_FUNCTION_EVENT_PAYLOAD' => $payload, 'APPWRITE_FUNCTION_DATA' => $data, + 'APPWRITE_FUNCTION_USERID' => $userId, + 'APPWRITE_FUNCTION_JWT' => $jwt, ]); \array_walk($vars, function (&$value, $key) { From a2bf6269bf2498a10f2871bb909ea4ea3ad83e47 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 10 Mar 2021 12:43:59 -0500 Subject: [PATCH 052/195] Create script for testing execution custom data --- tests/resources/functions/package-php-fn.sh | 12 ++++ tests/resources/functions/php-fn.tar.gz | Bin 0 -> 24556 bytes .../resources/functions/php-fn/composer.json | 18 +++++ .../resources/functions/php-fn/composer.lock | 64 ++++++++++++++++++ tests/resources/functions/php-fn/index.php | 31 +++++++++ 5 files changed, 125 insertions(+) create mode 100755 tests/resources/functions/package-php-fn.sh create mode 100644 tests/resources/functions/php-fn.tar.gz create mode 100644 tests/resources/functions/php-fn/composer.json create mode 100644 tests/resources/functions/php-fn/composer.lock create mode 100644 tests/resources/functions/php-fn/index.php diff --git a/tests/resources/functions/package-php-fn.sh b/tests/resources/functions/package-php-fn.sh new file mode 100755 index 0000000000..bbb4920cbc --- /dev/null +++ b/tests/resources/functions/package-php-fn.sh @@ -0,0 +1,12 @@ + +echo 'PHP Packaging...' + +cp -r $(pwd)/tests/resources/functions/php-fn $(pwd)/tests/resources/functions/packages/php-fn + +docker run --rm -v $(pwd)/tests/resources/functions/packages/php-fn:/app -w /app composer:2.0 composer install --ignore-platform-reqs + +docker run --rm -v $(pwd)/tests/resources/functions/packages/php-fn:/app -w /app appwrite/env-php-8.0:1.0.0 tar -zcvf code.tar.gz . + +mv $(pwd)/tests/resources/functions/packages/php-fn/code.tar.gz $(pwd)/tests/resources/functions/php-fn.tar.gz + +rm -r $(pwd)/tests/resources/functions/packages/php-fn diff --git a/tests/resources/functions/php-fn.tar.gz b/tests/resources/functions/php-fn.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..55dd10889178d4301761a9520337be0c5e2afff5 GIT binary patch literal 24556 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 literal 0 HcmV?d00001 diff --git a/tests/resources/functions/php-fn/composer.json b/tests/resources/functions/php-fn/composer.json new file mode 100644 index 0000000000..e3c6db23e9 --- /dev/null +++ b/tests/resources/functions/php-fn/composer.json @@ -0,0 +1,18 @@ +{ + "name": "appwrite/cloud-function-demo", + "description": "Demo cloud function script", + "type": "library", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "require": { + "php": ">=7.4.0", + "ext-curl": "*", + "ext-json": "*", + "appwrite/appwrite": "1.1.*" + } +} diff --git a/tests/resources/functions/php-fn/composer.lock b/tests/resources/functions/php-fn/composer.lock new file mode 100644 index 0000000000..758c73c3f9 --- /dev/null +++ b/tests/resources/functions/php-fn/composer.lock @@ -0,0 +1,64 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "afdff6a172e6c44aee11f1562175f81a", + "packages": [ + { + "name": "appwrite/appwrite", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/appwrite/sdk-for-php.git", + "reference": "98b327d3fd18a72f4582019916afd735a0e9e0e7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/98b327d3fd18a72f4582019916afd735a0e9e0e7", + "reference": "98b327d3fd18a72f4582019916afd735a0e9e0e7", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "php": ">=7.1.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "Appwrite\\": "src/Appwrite" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Appwrite is an open-source backend as a service server that abstract and simplify complex and repetitive development tasks", + "support": { + "email": "team@localhost.test", + "issues": "https://github.com/appwrite/sdk-for-php/issues", + "source": "https://github.com/appwrite/sdk-for-php/tree/1.1.2", + "url": "https://appwrite.io/support" + }, + "time": "2020-08-15T18:24:32+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.4.0", + "ext-curl": "*", + "ext-json": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.0.0" +} diff --git a/tests/resources/functions/php-fn/index.php b/tests/resources/functions/php-fn/index.php new file mode 100644 index 0000000000..6d4bad1e5a --- /dev/null +++ b/tests/resources/functions/php-fn/index.php @@ -0,0 +1,31 @@ +setEndpoint($_ENV['APPWRITE_ENDPOINT']) // Your API Endpoint + ->setProject($_ENV['APPWRITE_PROJECT']) // Your project ID + ->setKey($_ENV['APPWRITE_SECRET']) // Your secret API key +; + +$storage = new Storage($client); + +// $result = $storage->getFile($_ENV['APPWRITE_FILEID']); + +echo $_ENV['APPWRITE_FUNCTION_ID']."\n"; +echo $_ENV['APPWRITE_FUNCTION_NAME']."\n"; +echo $_ENV['APPWRITE_FUNCTION_TAG']."\n"; +echo $_ENV['APPWRITE_FUNCTION_TRIGGER']."\n"; +echo $_ENV['APPWRITE_FUNCTION_ENV_NAME']."\n"; +echo $_ENV['APPWRITE_FUNCTION_ENV_VERSION']."\n"; +// echo $result['$id']; +echo $_ENV['APPWRITE_FUNCTION_EVENT']."\n"; +echo $_ENV['APPWRITE_FUNCTION_EVENT_PAYLOAD']."\n"; +echo 'data:'.$_ENV['APPWRITE_FUNCTION_DATA']."\n"; +echo 'userId:'.$_ENV['APPWRITE_FUNCTION_USERID']."\n"; +echo 'jwt:'.$_ENV['APPWRITE_FUNCTION_JWT']."\n"; From f56a63c611b94d669059182db398418f7e643038 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 10 Mar 2021 12:48:05 -0500 Subject: [PATCH 053/195] Make JWT for each execution --- app/controllers/api/functions.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index b3243ab93c..23273f6ba9 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1,5 +1,7 @@ isEmpty()) { - $jwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. + $newjwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. + $jwt = $newjwt->encode([ + 'userId' => $user->getId(), + 'sessionId' => $session->getId(), + ]); } } From 33fb2e3edd7b1cb801e70b4ffbe96c83cb0a2af4 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 10 Mar 2021 13:05:43 -0500 Subject: [PATCH 054/195] Test for custom data in execution --- .../Functions/FunctionsCustomServerTest.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 9dded8fdd7..943e43cfbc 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -343,6 +343,37 @@ class FunctionsCustomServerTest extends Scope /** * @depends testCreateExecution */ + public function testCreateCustomExecution($data):array + { + /** + * Test for SUCCESS + */ + $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$data['functionId'].'/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => 'foobar', + ]); + + print_r($execution); + $executionId = $execution['body']['$id'] ?? ''; + + $this->assertEquals(201, $execution['headers']['status-code']); + $this->assertNotEmpty($execution['body']['$id']); + $this->assertNotEmpty($execution['body']['functionId']); + $this->assertIsInt($execution['body']['dateCreated']); + $this->assertEquals($data['functionId'], $execution['body']['functionId']); + $this->assertEquals('waiting', $execution['body']['status']); + $this->assertEquals(0, $execution['body']['exitCode']); + $this->assertEquals('', $execution['body']['stdout']); + $this->assertEquals('', $execution['body']['stderr']); + $this->assertEquals(0, $execution['body']['time']); + $this->assertStringContainsString('foobar', $execution['body']['stdout']); + + } + /** + * @depends testCreateCustomExecution + */ public function testListExecutions(array $data):array { /** From 8373db80ef6af38e4cedc962d2137857a45f58f0 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 10 Mar 2021 14:42:36 -0500 Subject: [PATCH 055/195] Comment unneeded function calls --- tests/resources/functions/php-fn.tar.gz | Bin 24556 -> 24556 bytes tests/resources/functions/php-fn/index.php | 14 +++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/resources/functions/php-fn.tar.gz b/tests/resources/functions/php-fn.tar.gz index 55dd10889178d4301761a9520337be0c5e2afff5..02aff814504b80b727fc1777f54abd485eaee4bc 100644 GIT binary patch delta 22843 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 delta 22823 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 z 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 VSzl2BMw6>wIS4jJt}g&c0|1MZtAqdm diff --git a/tests/resources/functions/php-fn/index.php b/tests/resources/functions/php-fn/index.php index 6d4bad1e5a..8490d2b1c6 100644 --- a/tests/resources/functions/php-fn/index.php +++ b/tests/resources/functions/php-fn/index.php @@ -5,15 +5,15 @@ include './vendor/autoload.php'; use Appwrite\Client; use Appwrite\Services\Storage; -$client = new Client(); +// $client = new Client(); -$client - ->setEndpoint($_ENV['APPWRITE_ENDPOINT']) // Your API Endpoint - ->setProject($_ENV['APPWRITE_PROJECT']) // Your project ID - ->setKey($_ENV['APPWRITE_SECRET']) // Your secret API key -; +// $client + // ->setEndpoint($_ENV['APPWRITE_ENDPOINT']) // Your API Endpoint + // ->setProject($_ENV['APPWRITE_PROJECT']) // Your project ID + // ->setKey($_ENV['APPWRITE_SECRET']) // Your secret API key +// ; -$storage = new Storage($client); +// $storage = new Storage($client); // $result = $storage->getFile($_ENV['APPWRITE_FILEID']); From 36b8e6defbe934fe45f156848b8ef62e57a43e7b Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 10 Mar 2021 14:43:15 -0500 Subject: [PATCH 056/195] Server test assertions --- .../Functions/FunctionsCustomServerTest.php | 104 ++++++++++++------ 1 file changed, 73 insertions(+), 31 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 943e43cfbc..fbd7ec141b 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -343,37 +343,6 @@ class FunctionsCustomServerTest extends Scope /** * @depends testCreateExecution */ - public function testCreateCustomExecution($data):array - { - /** - * Test for SUCCESS - */ - $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$data['functionId'].'/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'data' => 'foobar', - ]); - - print_r($execution); - $executionId = $execution['body']['$id'] ?? ''; - - $this->assertEquals(201, $execution['headers']['status-code']); - $this->assertNotEmpty($execution['body']['$id']); - $this->assertNotEmpty($execution['body']['functionId']); - $this->assertIsInt($execution['body']['dateCreated']); - $this->assertEquals($data['functionId'], $execution['body']['functionId']); - $this->assertEquals('waiting', $execution['body']['status']); - $this->assertEquals(0, $execution['body']['exitCode']); - $this->assertEquals('', $execution['body']['stdout']); - $this->assertEquals('', $execution['body']['stderr']); - $this->assertEquals(0, $execution['body']['time']); - $this->assertStringContainsString('foobar', $execution['body']['stdout']); - - } - /** - * @depends testCreateCustomExecution - */ public function testListExecutions(array $data):array { /** @@ -791,4 +760,77 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($executions['body']['executions'][0]['stdout'], ''); $this->assertEquals($executions['body']['executions'][0]['stderr'], ''); } + + /** + * @depends testTimeout + */ + public function testCreateCustomExecution() + { + $name = 'php-8.0'; + $code = realpath(__DIR__ . '/../../../resources/functions').'/php-fn.tar.gz'; + $command = 'php index.php'; + $timeout = 2; + + $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Test '.$name, + 'env' => $name, + 'vars' => [], + 'events' => [], + 'schedule' => '', + 'timeout' => $timeout, + ]); + + $functionId = $function['body']['$id'] ?? ''; + + $this->assertEquals(201, $function['headers']['status-code']); + + $tag = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/tags', array_merge([ + 'content-type' => 'multipart/form-data', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'command' => $command, + 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), + ]); + + $tagId = $tag['body']['$id'] ?? ''; + $this->assertEquals(201, $tag['headers']['status-code']); + + $tag = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/tag', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'tag' => $tagId, + ]); + + $this->assertEquals(200, $tag['headers']['status-code']); + + $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => 'foobar', + ]); + + $executionId = $execution['body']['$id'] ?? ''; + + $this->assertEquals(201, $execution['headers']['status-code']); + + sleep(10); + + $executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals($executions['headers']['status-code'], 200); + $this->assertEquals($executions['body']['sum'], 1); + $this->assertIsArray($executions['body']['executions']); + $this->assertCount(1, $executions['body']['executions']); + $this->assertEquals($executions['body']['executions'][0]['$id'], $executionId); + $this->assertEquals($executions['body']['executions'][0]['trigger'], 'http'); + $this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']); + } } From 681ab12f7660ba38f7fbb1ca399b3432ffbd0d84 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 10 Mar 2021 15:25:54 -0500 Subject: [PATCH 057/195] Instantiate jwt outside conditionals --- app/controllers/api/functions.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 23273f6ba9..6c9bd61921 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -735,12 +735,12 @@ App::post('/v1/functions/:functionId/executions') if (false === $execution) { throw new Exception('Failed saving execution to DB', 500); } - + + $jwt = ''; // initialize if (!empty($user->getId())) { // If userId exists, generate a JWT for function $tokens = $user->getAttribute('tokens', []); $session = new Document(); - $jwt = ''; foreach ($tokens as $token) { /** @var Appwrite\Database\Document $token */ if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too @@ -749,8 +749,8 @@ App::post('/v1/functions/:functionId/executions') } if(!$session->isEmpty()) { - $newjwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. - $jwt = $newjwt->encode([ + $jwtObj = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. + $jwt = $jwtObj->encode([ 'userId' => $user->getId(), 'sessionId' => $session->getId(), ]); From 034679930aa7bec023d212495472c8b4f8897abb Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 10 Mar 2021 15:50:20 -0500 Subject: [PATCH 058/195] Add client e2e test --- .../Functions/FunctionsCustomClientTest.php | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 25d3a8237c..74f0a7d80a 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -115,4 +115,78 @@ class FunctionsCustomClientTest extends Scope return []; } -} \ No newline at end of file + + public function testCreateCustomExecution():array + { + /** + * Test for SUCCESS + */ + $function = $this->client->call(Client::METHOD_POST, '/functions', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'name' => 'Test', + 'execute' => ['*'], + 'env' => 'php-7.4', + 'vars' => [ + 'funcKey1' => 'funcValue1', + 'funcKey2' => 'funcValue2', + 'funcKey3' => 'funcValue3', + ], + 'events' => [ + 'account.create', + 'account.delete', + ], + 'schedule' => '* * * * *', + 'timeout' => 10, + ]); + + $this->assertEquals(201, $function['headers']['status-code']); + + $tag = $this->client->call(Client::METHOD_POST, '/functions/'.$function['body']['$id'].'/tags', [ + 'content-type' => 'multipart/form-data', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'command' => 'php function.php', + 'code' => new CURLFile(realpath(__DIR__ . '/../../../resources/functions/php-fn.tar.gz'), 'application/x-gzip', 'php-fx.tar.gz'), //different tarball names intentional + ]); + + $tagId = $tag['body']['$id'] ?? ''; + + $this->assertEquals(201, $tag['headers']['status-code']); + + $function = $this->client->call(Client::METHOD_PATCH, '/functions/'.$function['body']['$id'].'/tag', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'tag' => $tagId, + ]); + + $this->assertEquals(200, $function['headers']['status-code']); + + $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$function['body']['$id'].'/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => 'foobar', + ]); + + $executionId = $execution['body']['$id'] ?? ''; + + $this->assertEquals(201, $execution['headers']['status-code']); + + $execution = $this->client->call(Client::METHOD_GET, '/functions/'.$function['body']['$id'].'/executions/'.$executionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $execution['headers']['status-code']); + $this->assertStringContainsString('foobar', $execution['body']['stdout']); + $this->assertStringContainsString($this->getUser()['$id'], $execution['body']['stdout']); + + return []; + } +} From a70977f27f4205cbf527f47e4c59ba337e6b1552 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 11 Mar 2021 10:27:07 +0545 Subject: [PATCH 059/195] exception and small fixes --- docs/sdks/dart/GETTING_STARTED.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/sdks/dart/GETTING_STARTED.md b/docs/sdks/dart/GETTING_STARTED.md index 02a3aa631b..fb58f0bbcf 100644 --- a/docs/sdks/dart/GETTING_STARTED.md +++ b/docs/sdks/dart/GETTING_STARTED.md @@ -8,14 +8,17 @@ import 'package:dart_appwrite/dart_appwrite.dart'; void main() async { Client client = Client(); - .setEndpoint( - 'http://[HOSTNAME_OR_IP]/v1') // Make sure your endpoint is accessible + .setEndpoint('http://[HOSTNAME_OR_IP]/v1') // Make sure your endpoint is accessible .setProject('5ff3379a01d25') // Your project ID .setKey('cd868c7af8bdc893b4...93b7535db89') Users users = Users(client); - final response = await users.create(email: ‘email@example.com’,password: ‘password’, name: ‘name’); - print(response.data); + try { + final response = await users.create(email: ‘email@example.com’,password: ‘password’, name: ‘name’); + print(response.data); + } on AppwriteException catch(e) { + print(e.message); + } } ``` \ No newline at end of file From af8c52087b9894e1163554ba765faf235ee2e649 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 11 Mar 2021 10:38:40 +0545 Subject: [PATCH 060/195] learn more section dart SDK --- docs/sdks/dart/GETTING_STARTED.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/sdks/dart/GETTING_STARTED.md b/docs/sdks/dart/GETTING_STARTED.md index fb58f0bbcf..78d7c49729 100644 --- a/docs/sdks/dart/GETTING_STARTED.md +++ b/docs/sdks/dart/GETTING_STARTED.md @@ -21,4 +21,10 @@ void main() async { print(e.message); } } -``` \ No newline at end of file +``` + +### Learn more +You can use followng resources to learn more and get help +- [Appwrite Docs](https://appwrite.io/docs) +- [Discord Community](https://appwrite.io/discord) +- [Appwrite Dart Playground](https://github.com/appwrite/playground-for-dart) \ No newline at end of file From ce17d5d8860a313d8efeb0da5a492a1e74725c9d Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 11 Mar 2021 10:39:49 +0545 Subject: [PATCH 061/195] learn more section in Deno SDK --- docs/sdks/deno/GETTING_STARTED.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/sdks/deno/GETTING_STARTED.md b/docs/sdks/deno/GETTING_STARTED.md index c9865ea381..d06f5ff580 100644 --- a/docs/sdks/deno/GETTING_STARTED.md +++ b/docs/sdks/deno/GETTING_STARTED.md @@ -50,4 +50,10 @@ promise.then(function (response) { }, function (error) { console.log(error); }); -``` \ No newline at end of file +``` + +### Learn more +You can use followng resources to learn more and get help +- [Appwrite Docs](https://appwrite.io/docs) +- [Discord Community](https://appwrite.io/discord) +- [Appwrite Deno Playground](https://github.com/appwrite/playground-for-deno) \ No newline at end of file From 90fc752ffeec619566eabee6f4a8dcb64841cb0d Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 11 Mar 2021 10:41:16 +0545 Subject: [PATCH 062/195] flutter sdk learn more section --- docs/sdks/flutter/GETTING_STARTED.md | 29 +++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/sdks/flutter/GETTING_STARTED.md b/docs/sdks/flutter/GETTING_STARTED.md index bd9b635786..c66ec5c095 100644 --- a/docs/sdks/flutter/GETTING_STARTED.md +++ b/docs/sdks/flutter/GETTING_STARTED.md @@ -28,18 +28,18 @@ The Appwrite SDK uses ASWebAuthenticationSession on iOS 12+ and SFAuthentication ### Android In order to capture the Appwrite OAuth callback url, the following activity needs to be added to your AndroidManifest.xml. Be sure to relpace the **[PROJECT_ID]** string with your actual Appwrite project ID. You can find your Appwrite project ID in you project settings screen in your Appwrite console. -``` +```xml - - - - - - - - - - + + + + + + + + + + ``` @@ -108,5 +108,8 @@ name: 'My Name' ); ``` -### Next Steps -Appwrite has many services and tools to help improve your app and speed up your development. The best way to learn how you can take advantage of them is to explore the different API references docs. \ No newline at end of file +### Learn more +You can use followng resources to learn more and get help +- [Appwrite Docs](https://appwrite.io/docs) +- [Discord Community](https://appwrite.io/discord) +- [Appwrite Flutter Playground](https://github.com/appwrite/playground-for-flutter) \ No newline at end of file From 6295bdba86e99268fa6103a52940d4395d21e052 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 11 Mar 2021 13:49:59 +0545 Subject: [PATCH 063/195] remove other files --- docs/sdks/deno/GETTING_STARTED.md | 59 -------------- docs/sdks/flutter/GETTING_STARTED.md | 115 --------------------------- 2 files changed, 174 deletions(-) delete mode 100644 docs/sdks/deno/GETTING_STARTED.md delete mode 100644 docs/sdks/flutter/GETTING_STARTED.md diff --git a/docs/sdks/deno/GETTING_STARTED.md b/docs/sdks/deno/GETTING_STARTED.md deleted file mode 100644 index d06f5ff580..0000000000 --- a/docs/sdks/deno/GETTING_STARTED.md +++ /dev/null @@ -1,59 +0,0 @@ -## Getting Started - -### Init your SDK -Initialize your SDK code with your project ID which can be found in your project settings page and your new API secret Key from previous phase. - -```typescript -let client = new sdk.Client(); - -client - .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint - .setProject('5df5acd0d48c2') // Your project ID - .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key -; - -``` - -### Make your first request - -Once your SDK object is set, create any of the Appwrite service project objects and choose any request to send. Full documentation for any service method you would like to use can be found in your SDK documentation or in the API References section. - -```typescript -let users = new sdk.Users(client); - -let promise = users.create('email@example.com', 'password'); - -promise.then(function (response) { - console.log(response); -}, function (error) { - console.log(error); -}); -``` - -### Full Example -```typescript -import * as sdk from "https://deno.land/x/appwrite/mod.ts"; - -let client = new sdk.Client(); -let users = new sdk.Users(client); - -client - .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint - .setProject('5df5acd0d48c2') // Your project ID - .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key -; - -let promise = users.create('email@example.com', 'password'); - -promise.then(function (response) { - console.log(response); -}, function (error) { - console.log(error); -}); -``` - -### Learn more -You can use followng resources to learn more and get help -- [Appwrite Docs](https://appwrite.io/docs) -- [Discord Community](https://appwrite.io/discord) -- [Appwrite Deno Playground](https://github.com/appwrite/playground-for-deno) \ No newline at end of file diff --git a/docs/sdks/flutter/GETTING_STARTED.md b/docs/sdks/flutter/GETTING_STARTED.md deleted file mode 100644 index c66ec5c095..0000000000 --- a/docs/sdks/flutter/GETTING_STARTED.md +++ /dev/null @@ -1,115 +0,0 @@ -## Getting Started for Flutter - -### Create Your First Appwrite Project -Go to your new Appwrite console, and once inside, click the (plus) icon in the top navigation header or on the **'Create Project'** button on your console homepage. Choose a name for your project and click create to get started. - -### Add your Flutter Platform -To init your SDK and start interacting with Appwrite services, you need to add a new Flutter platform to your project. To add a new platform, go to your Appwrite console, choose the project you created in the step before, and click the 'Add Platform' button. - -From the options, choose to add a new **Flutter** platform and add your app credentials. Appwrite Flutter SDK currently supports building apps for both iOS and Android. - -If you are building your Flutter application for multiple devices, you have to follow this process for each different device. - -### iOS -For **iOS** add your app name and Bundle ID, You can find your Bundle Identifier in the General tab for your app's primary target in Xcode. - -### Android -For **Android** add your app name and package name, Your package name is generally the applicationId in your app-level build.gradle file. By registering your new app platform, you are allowing your app to communicate with the Appwrite API. - -#### iOS - -The Appwrite SDK uses ASWebAuthenticationSession on iOS 12+ and SFAuthenticationSession on iOS 11 to allow OAuth authentication. You have to change your iOS Deployment Target in Xcode to be iOS >= 11 to be able to build your app on an emulator or a real device. - -1. In Xcode, open Runner.xcworkspace in your app's ios folder. -2. To view your app's settings, select the Runner project in the Xcode project navigator. Then, in the main view sidebar, select the Runner target. -3. Select the General tab. -4. In Deployment Info, 'Target' select iOS 11.0 - -### Android -In order to capture the Appwrite OAuth callback url, the following activity needs to be added to your AndroidManifest.xml. Be sure to relpace the **[PROJECT_ID]** string with your actual Appwrite project ID. You can find your Appwrite project ID in you project settings screen in your Appwrite console. - -```xml - - - - - - - - - - - - -``` - -#### Web -Appwrite 0.7, and the Appwrite Flutter SDK 0.3.0 have added support for Flutter Web. To build web apps that integrate with Appwrite successfully, all you have to do is add a web platform on your Appwrite project's dashboard and list the domain your website will use to allow communication to the Appwrite API.

- -### Flutter Web Cross-Domain Communication & Cookies -

While running Flutter Web, make sure your Appwrite server and your Flutter client are using the same top-level domain and the same protocol (HTTP or HTTPS) to communicate. When trying to communicate between different domains or protocols, you may receive HTTP status error 401 because some modern browsers block cross-site or insecure cookies for enhanced privacy. In production, Appwrite allows you set multiple custom-domains for each project.

- -### Init your SDK - -

Initialize your SDK code with your project ID, which can be found in your project settings page. - -``` -import 'package:appwrite/appwrite.dart'; -Client client = Client(); - - -client -.setEndpoint('https://localhost/v1') // Your Appwrite Endpoint -.setProject('5e8cf4f46b5e8') // Your project ID -.setSelfSigned() // Remove in production -; -``` - -

Before starting to send any API calls to your new Appwrite instance, make sure your Android or iOS emulators has network access to the Appwrite server hostname or IP address.

When trying to connect to Appwrite from an emulator or a mobile device, localhost is the hostname for the device or emulator and not your local Appwrite instance. You should replace localhost with your private IP as the Appwrite endpoint's hostname. You can also use a service like ngrok to proxy the Appwrite API.

- -### Make Your First Request - -

Once your SDK object is set, access any of the Appwrite services and choose any request to send. Full documentation for any service method you would like to use can be found in your SDK documentation or in the API References section. - -``` -// Register User -Account account = Account(client); -Response user = await account -.create( -email: 'me@appwrite.io', -password: 'password', -name: 'My Name' -); -``` - -### Full Example - -``` -import 'package:appwrite/appwrite.dart'; -Client client = Client(); - - -client -.setEndpoint('https://localhost/v1') // Your Appwrite Endpoint -.setProject('5e8cf4f46b5e8') // Your project ID -.setSelfSigned() // Remove in production -; - - -// Register User -Account account = Account(client); - - -Response user = await account -.create( -email: 'me@appwrite.io', -password: 'password', -name: 'My Name' -); -``` - -### Learn more -You can use followng resources to learn more and get help -- [Appwrite Docs](https://appwrite.io/docs) -- [Discord Community](https://appwrite.io/discord) -- [Appwrite Flutter Playground](https://github.com/appwrite/playground-for-flutter) \ No newline at end of file From 98d7d13f1611fc76e9be2dd99814be4232641cab Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 11 Mar 2021 14:38:44 +0545 Subject: [PATCH 064/195] remove other files --- docs/sdks/dart/GETTING_STARTED.md | 30 ---------------- docs/sdks/deno/GETTING_STARTED.md | 59 ------------------------------- 2 files changed, 89 deletions(-) delete mode 100644 docs/sdks/dart/GETTING_STARTED.md delete mode 100644 docs/sdks/deno/GETTING_STARTED.md diff --git a/docs/sdks/dart/GETTING_STARTED.md b/docs/sdks/dart/GETTING_STARTED.md deleted file mode 100644 index 78d7c49729..0000000000 --- a/docs/sdks/dart/GETTING_STARTED.md +++ /dev/null @@ -1,30 +0,0 @@ -## Getting Started - -### Initialize & Make API Request -Once you add the dependencies, its extremely easy to get started with the SDK; All you need to do is import the package in your code, set your Appwrite credentials, and start making API calls. Below is a simple example: - -```dart -import 'package:dart_appwrite/dart_appwrite.dart'; - -void main() async { - Client client = Client(); - .setEndpoint('http://[HOSTNAME_OR_IP]/v1') // Make sure your endpoint is accessible - .setProject('5ff3379a01d25') // Your project ID - .setKey('cd868c7af8bdc893b4...93b7535db89') - - Users users = Users(client); - - try { - final response = await users.create(email: ‘email@example.com’,password: ‘password’, name: ‘name’); - print(response.data); - } on AppwriteException catch(e) { - print(e.message); - } -} -``` - -### Learn more -You can use followng resources to learn more and get help -- [Appwrite Docs](https://appwrite.io/docs) -- [Discord Community](https://appwrite.io/discord) -- [Appwrite Dart Playground](https://github.com/appwrite/playground-for-dart) \ No newline at end of file diff --git a/docs/sdks/deno/GETTING_STARTED.md b/docs/sdks/deno/GETTING_STARTED.md deleted file mode 100644 index d06f5ff580..0000000000 --- a/docs/sdks/deno/GETTING_STARTED.md +++ /dev/null @@ -1,59 +0,0 @@ -## Getting Started - -### Init your SDK -Initialize your SDK code with your project ID which can be found in your project settings page and your new API secret Key from previous phase. - -```typescript -let client = new sdk.Client(); - -client - .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint - .setProject('5df5acd0d48c2') // Your project ID - .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key -; - -``` - -### Make your first request - -Once your SDK object is set, create any of the Appwrite service project objects and choose any request to send. Full documentation for any service method you would like to use can be found in your SDK documentation or in the API References section. - -```typescript -let users = new sdk.Users(client); - -let promise = users.create('email@example.com', 'password'); - -promise.then(function (response) { - console.log(response); -}, function (error) { - console.log(error); -}); -``` - -### Full Example -```typescript -import * as sdk from "https://deno.land/x/appwrite/mod.ts"; - -let client = new sdk.Client(); -let users = new sdk.Users(client); - -client - .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint - .setProject('5df5acd0d48c2') // Your project ID - .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key -; - -let promise = users.create('email@example.com', 'password'); - -promise.then(function (response) { - console.log(response); -}, function (error) { - console.log(error); -}); -``` - -### Learn more -You can use followng resources to learn more and get help -- [Appwrite Docs](https://appwrite.io/docs) -- [Discord Community](https://appwrite.io/discord) -- [Appwrite Deno Playground](https://github.com/appwrite/playground-for-deno) \ No newline at end of file From a737b23fa536861db3d5860c486d08b0738dfcc2 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Thu, 11 Mar 2021 07:39:06 -0500 Subject: [PATCH 065/195] Add modal for execution data --- app/views/console/functions/function.phtml | 46 +++++++++++++--------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/app/views/console/functions/function.phtml b/app/views/console/functions/function.phtml index 4ae65f491e..5ad3db3191 100644 --- a/app/views/console/functions/function.phtml +++ b/app/views/console/functions/function.phtml @@ -50,24 +50,9 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);

- -
-   View Logs -
+
+   View Logs +
@@ -575,6 +560,31 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true); +