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
zcmXVXWmp#7_ch(!NQZPuH%O;+mvkvemoRj9OC#MNjlxX{NJw{gH`3hm^8Ehq*Ew_M
z?6dY-YoBY+%n1bc1Ol5#0bI4&W5fO~d>algDD<%M^^}&qJ}$AG{^U#6^SPIe=i7d=
z!Gz+ngD1RDPcKhT&oz-=xxJoTGlMpz63ge&wD`t9;|IQptTxvV#ucgTyPFS+wd%gz
z@#mDy$k+q#hx`Z5EGwNaxw+E?$bEt?;@08EJ5z+dgs6RpX1xQ86M+@LJ8vY9JYV$V
z;uCfE`d1KKvHWuS^DEPB{ttigHzp6<1f(OE1D+iMo;B*ePD@16&Jrb$OSJW0g?je|
zi_uhLI~vPHv?#vAZd%E7Q!M^#mx!VinZk3DWanI(b*C|LxhLs5wh_ld&ZCmNMilEl
z9=~@&I*#9v)}1Rqz64wt-|bBGn9|%l+BQi_`#!lq#@+8s)lOn02Bze@rw?!CLvVZV
zH+}vvy;UqP-pvwSNF-iB}lRv(T0UY^mYrQsO@jy@5qX27(tY=#7oq(g|R%eLro*
z`SV|Ou7?r+n6S+8|I*1i0Yj6I`}>7Rr8_*2ljPiYJMmBAWg=$p7Xr~St4G?{hl%Sa
z(&a|(MdOYUc>9t;*mHDCJBd_|Ko9c6+i6A7Hx7W7G69{G^ji_K6YItY9
z4~^SbC~qPg7Jg?K8gE6Gv~|>F{QWs8;B@4R6A7Q=DnW{2c!2;W$d{LBD4o1fb!0+ul71RzU_yE=JN!jQH_W%1O9-D78!Q1#Ut9!=wb-}x#iF=5
z+akhf5qdTwEVgC-^1vGt4teCjI1^Hv8%8(|lD9=)eo>yeV$(RuZF-10)n)QrO#d!X
zogsffgggVmJP2KBQDR|Xu=(>AhrWd;K}a+ScJat*+%VyDN5{jmp&_Hsnx!Ace{U(-
ztQ36PySx6*a~vYeDpfLQvKQo}!W!4p5lCXEG*B28AN&d@-AAW+47v0sDP{43o4^)DfsDxMqj)m>dR(-s7+xR9xEaJh1lr&Jn^F_2vd4
z)1%AQYwr(HlcWT!>iSsUL|RV~F;CQ(hjrn6vmVaeU_U5Ws2i2x)gQmoGzkC7pRa05
zRpvIByRu#Our)0O1sWI27Ef62c)3LkQ8>TvuOx}8&oTM(rq+_jN7&bAnH&y%k-~#9
z%UJ+M;