![]()
@@ -390,7 +392,10 @@
|
|
-
+
+
+
+
Unknown
|
diff --git a/app/views/layouts/default.phtml b/app/views/layouts/default.phtml
index c6c0de795e..5016ccd8d6 100644
--- a/app/views/layouts/default.phtml
+++ b/app/views/layouts/default.phtml
@@ -10,7 +10,7 @@ $litespeed = $this->getParam('litespeed', true);
$analytics = $this->getParam('analytics', 'UA-26264668-9');
$mode = $this->getParam('mode', '');
$canonical = $this->getParam('canonical', '');
-$image = $this->getParam('image', '/images/logo.png');
+$image = $this->getParam('image', '/images/logo.png');
$locale = $this->getParam('locale', null);
$runtimes = $this->getParam('runtimes', null);
@@ -53,12 +53,17 @@ if(!empty($platforms)) {
+
+
+
+
+
+```
+
+Redirection URL passed to the authentication service must be the same as the URL on which the application is running (schema, host, port if necessary) and the path must point to created HTML file, /auth.html in this case. The callbackUrlScheme parameter of the authenticate() method does not take into account, so it is possible to use a schema for native platforms in the code.
+
#### 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](https://appwrite.io/docs/custom-domains) for each project.
diff --git a/public/images/logo.png b/public/images/logo.png
index 1f2cf818cf..83d48541ca 100644
Binary files a/public/images/logo.png and b/public/images/logo.png differ
diff --git a/public/scripts/dependencies/appwrite.js b/public/scripts/dependencies/appwrite.js
index 347acde123..62be80abf8 100644
--- a/public/scripts/dependencies/appwrite.js
+++ b/public/scripts/dependencies/appwrite.js
@@ -199,7 +199,7 @@
*
* Use this endpoint to allow a new user to register a new account in your
* project. After the user registration completes successfully, you can use
- * the [/account/verification](/docs/client/account#accountCreateVerification)
+ * the [/account/verfication](/docs/client/account#accountCreateVerification)
* route to start verifying the user email address. To allow the new user to
* login to their new account, you need to create a new [account
* session](/docs/client/account#accountCreateSession).
@@ -264,12 +264,14 @@
* Update Account Email
*
* 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.
+ * address, the user confirmation status will get reset. A new confirmation
+ * email is not sent automatically however you can use the send confirmation
+ * email endpoint again to send the confirmation email. 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.
*
+ *
* @param {string} email
* @param {string} password
* @throws {AppwriteException}
@@ -1165,8 +1167,8 @@
* @param {string} collectionId
* @param {string} name
* @param {string} permission
- * @param {string} read
- * @param {string} write
+ * @param {string[]} read
+ * @param {string[]} write
* @throws {AppwriteException}
* @returns {Promise}
*/
@@ -1237,12 +1239,13 @@
* @param {string} collectionId
* @param {string} name
* @param {string} permission
- * @param {string} read
- * @param {string} write
+ * @param {string[]} read
+ * @param {string[]} write
+ * @param {boolean} enabled
* @throws {AppwriteException}
* @returns {Promise}
*/
- updateCollection: (collectionId, name, permission, read, write) => __awaiter(this, void 0, void 0, function* () {
+ updateCollection: (collectionId, name, permission, read, write, enabled) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
@@ -1266,6 +1269,9 @@
if (typeof write !== 'undefined') {
payload['write'] = write;
}
+ if (typeof enabled !== 'undefined') {
+ payload['enabled'] = enabled;
+ }
const uri = new URL(this.config.endpoint + path);
return yield this.call('put', uri, {
'content-type': 'application/json',
@@ -1318,27 +1324,27 @@
*
*
* @param {string} collectionId
- * @param {string} attributeId
+ * @param {string} key
* @param {boolean} required
* @param {boolean} xdefault
* @param {boolean} array
* @throws {AppwriteException}
* @returns {Promise}
*/
- createBooleanAttribute: (collectionId, attributeId, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
+ createBooleanAttribute: (collectionId, key, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
- if (typeof attributeId === 'undefined') {
- throw new AppwriteException('Missing required parameter: "attributeId"');
+ if (typeof key === 'undefined') {
+ throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof required === 'undefined') {
throw new AppwriteException('Missing required parameter: "required"');
}
let path = '/database/collections/{collectionId}/attributes/boolean'.replace('{collectionId}', collectionId);
let payload = {};
- if (typeof attributeId !== 'undefined') {
- payload['attributeId'] = attributeId;
+ if (typeof key !== 'undefined') {
+ payload['key'] = key;
}
if (typeof required !== 'undefined') {
payload['required'] = required;
@@ -1361,27 +1367,27 @@
*
*
* @param {string} collectionId
- * @param {string} attributeId
+ * @param {string} key
* @param {boolean} required
* @param {string} xdefault
* @param {boolean} array
* @throws {AppwriteException}
* @returns {Promise}
*/
- createEmailAttribute: (collectionId, attributeId, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
+ createEmailAttribute: (collectionId, key, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
- if (typeof attributeId === 'undefined') {
- throw new AppwriteException('Missing required parameter: "attributeId"');
+ if (typeof key === 'undefined') {
+ throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof required === 'undefined') {
throw new AppwriteException('Missing required parameter: "required"');
}
let path = '/database/collections/{collectionId}/attributes/email'.replace('{collectionId}', collectionId);
let payload = {};
- if (typeof attributeId !== 'undefined') {
- payload['attributeId'] = attributeId;
+ if (typeof key !== 'undefined') {
+ payload['key'] = key;
}
if (typeof required !== 'undefined') {
payload['required'] = required;
@@ -1402,7 +1408,7 @@
*
*
* @param {string} collectionId
- * @param {string} attributeId
+ * @param {string} key
* @param {string[]} elements
* @param {boolean} required
* @param {string} xdefault
@@ -1410,12 +1416,12 @@
* @throws {AppwriteException}
* @returns {Promise}
*/
- createEnumAttribute: (collectionId, attributeId, elements, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
+ createEnumAttribute: (collectionId, key, elements, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
- if (typeof attributeId === 'undefined') {
- throw new AppwriteException('Missing required parameter: "attributeId"');
+ if (typeof key === 'undefined') {
+ throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof elements === 'undefined') {
throw new AppwriteException('Missing required parameter: "elements"');
@@ -1425,8 +1431,8 @@
}
let path = '/database/collections/{collectionId}/attributes/enum'.replace('{collectionId}', collectionId);
let payload = {};
- if (typeof attributeId !== 'undefined') {
- payload['attributeId'] = attributeId;
+ if (typeof key !== 'undefined') {
+ payload['key'] = key;
}
if (typeof elements !== 'undefined') {
payload['elements'] = elements;
@@ -1453,7 +1459,7 @@
*
*
* @param {string} collectionId
- * @param {string} attributeId
+ * @param {string} key
* @param {boolean} required
* @param {string} min
* @param {string} max
@@ -1462,20 +1468,20 @@
* @throws {AppwriteException}
* @returns {Promise}
*/
- createFloatAttribute: (collectionId, attributeId, required, min, max, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
+ createFloatAttribute: (collectionId, key, required, min, max, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
- if (typeof attributeId === 'undefined') {
- throw new AppwriteException('Missing required parameter: "attributeId"');
+ if (typeof key === 'undefined') {
+ throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof required === 'undefined') {
throw new AppwriteException('Missing required parameter: "required"');
}
let path = '/database/collections/{collectionId}/attributes/float'.replace('{collectionId}', collectionId);
let payload = {};
- if (typeof attributeId !== 'undefined') {
- payload['attributeId'] = attributeId;
+ if (typeof key !== 'undefined') {
+ payload['key'] = key;
}
if (typeof required !== 'undefined') {
payload['required'] = required;
@@ -1505,7 +1511,7 @@
*
*
* @param {string} collectionId
- * @param {string} attributeId
+ * @param {string} key
* @param {boolean} required
* @param {number} min
* @param {number} max
@@ -1514,20 +1520,20 @@
* @throws {AppwriteException}
* @returns {Promise}
*/
- createIntegerAttribute: (collectionId, attributeId, required, min, max, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
+ createIntegerAttribute: (collectionId, key, required, min, max, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
- if (typeof attributeId === 'undefined') {
- throw new AppwriteException('Missing required parameter: "attributeId"');
+ if (typeof key === 'undefined') {
+ throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof required === 'undefined') {
throw new AppwriteException('Missing required parameter: "required"');
}
let path = '/database/collections/{collectionId}/attributes/integer'.replace('{collectionId}', collectionId);
let payload = {};
- if (typeof attributeId !== 'undefined') {
- payload['attributeId'] = attributeId;
+ if (typeof key !== 'undefined') {
+ payload['key'] = key;
}
if (typeof required !== 'undefined') {
payload['required'] = required;
@@ -1556,27 +1562,27 @@
*
*
* @param {string} collectionId
- * @param {string} attributeId
+ * @param {string} key
* @param {boolean} required
* @param {string} xdefault
* @param {boolean} array
* @throws {AppwriteException}
* @returns {Promise}
*/
- createIpAttribute: (collectionId, attributeId, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
+ createIpAttribute: (collectionId, key, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
- if (typeof attributeId === 'undefined') {
- throw new AppwriteException('Missing required parameter: "attributeId"');
+ if (typeof key === 'undefined') {
+ throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof required === 'undefined') {
throw new AppwriteException('Missing required parameter: "required"');
}
let path = '/database/collections/{collectionId}/attributes/ip'.replace('{collectionId}', collectionId);
let payload = {};
- if (typeof attributeId !== 'undefined') {
- payload['attributeId'] = attributeId;
+ if (typeof key !== 'undefined') {
+ payload['key'] = key;
}
if (typeof required !== 'undefined') {
payload['required'] = required;
@@ -1599,7 +1605,7 @@
*
*
* @param {string} collectionId
- * @param {string} attributeId
+ * @param {string} key
* @param {number} size
* @param {boolean} required
* @param {string} xdefault
@@ -1607,12 +1613,12 @@
* @throws {AppwriteException}
* @returns {Promise}
*/
- createStringAttribute: (collectionId, attributeId, size, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
+ createStringAttribute: (collectionId, key, size, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
- if (typeof attributeId === 'undefined') {
- throw new AppwriteException('Missing required parameter: "attributeId"');
+ if (typeof key === 'undefined') {
+ throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof size === 'undefined') {
throw new AppwriteException('Missing required parameter: "size"');
@@ -1622,8 +1628,8 @@
}
let path = '/database/collections/{collectionId}/attributes/string'.replace('{collectionId}', collectionId);
let payload = {};
- if (typeof attributeId !== 'undefined') {
- payload['attributeId'] = attributeId;
+ if (typeof key !== 'undefined') {
+ payload['key'] = key;
}
if (typeof size !== 'undefined') {
payload['size'] = size;
@@ -1649,27 +1655,27 @@
*
*
* @param {string} collectionId
- * @param {string} attributeId
+ * @param {string} key
* @param {boolean} required
* @param {string} xdefault
* @param {boolean} array
* @throws {AppwriteException}
* @returns {Promise}
*/
- createUrlAttribute: (collectionId, attributeId, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
+ createUrlAttribute: (collectionId, key, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
- if (typeof attributeId === 'undefined') {
- throw new AppwriteException('Missing required parameter: "attributeId"');
+ if (typeof key === 'undefined') {
+ throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof required === 'undefined') {
throw new AppwriteException('Missing required parameter: "required"');
}
let path = '/database/collections/{collectionId}/attributes/url'.replace('{collectionId}', collectionId);
let payload = {};
- if (typeof attributeId !== 'undefined') {
- payload['attributeId'] = attributeId;
+ if (typeof key !== 'undefined') {
+ payload['key'] = key;
}
if (typeof required !== 'undefined') {
payload['required'] = required;
@@ -1690,18 +1696,18 @@
*
*
* @param {string} collectionId
- * @param {string} attributeId
+ * @param {string} key
* @throws {AppwriteException}
* @returns {Promise}
*/
- getAttribute: (collectionId, attributeId) => __awaiter(this, void 0, void 0, function* () {
+ getAttribute: (collectionId, key) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
- if (typeof attributeId === 'undefined') {
- throw new AppwriteException('Missing required parameter: "attributeId"');
+ if (typeof key === 'undefined') {
+ throw new AppwriteException('Missing required parameter: "key"');
}
- let path = '/database/collections/{collectionId}/attributes/{attributeId}'.replace('{collectionId}', collectionId).replace('{attributeId}', attributeId);
+ let path = '/database/collections/{collectionId}/attributes/{key}'.replace('{collectionId}', collectionId).replace('{key}', key);
let payload = {};
const uri = new URL(this.config.endpoint + path);
return yield this.call('get', uri, {
@@ -1713,18 +1719,18 @@
*
*
* @param {string} collectionId
- * @param {string} attributeId
+ * @param {string} key
* @throws {AppwriteException}
* @returns {Promise}
*/
- deleteAttribute: (collectionId, attributeId) => __awaiter(this, void 0, void 0, function* () {
+ deleteAttribute: (collectionId, key) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
- if (typeof attributeId === 'undefined') {
- throw new AppwriteException('Missing required parameter: "attributeId"');
+ if (typeof key === 'undefined') {
+ throw new AppwriteException('Missing required parameter: "key"');
}
- let path = '/database/collections/{collectionId}/attributes/{attributeId}'.replace('{collectionId}', collectionId).replace('{attributeId}', attributeId);
+ let path = '/database/collections/{collectionId}/attributes/{key}'.replace('{collectionId}', collectionId).replace('{key}', key);
let payload = {};
const uri = new URL(this.config.endpoint + path);
return yield this.call('delete', uri, {
@@ -1793,8 +1799,8 @@
* @param {string} collectionId
* @param {string} documentId
* @param {object} data
- * @param {string} read
- * @param {string} write
+ * @param {string[]} read
+ * @param {string[]} write
* @throws {AppwriteException}
* @returns {Promise}
*/
@@ -1861,8 +1867,8 @@
* @param {string} collectionId
* @param {string} documentId
* @param {object} data
- * @param {string} read
- * @param {string} write
+ * @param {string[]} read
+ * @param {string[]} write
* @throws {AppwriteException}
* @returns {Promise}
*/
@@ -1974,19 +1980,19 @@
*
*
* @param {string} collectionId
- * @param {string} indexId
+ * @param {string} key
* @param {string} type
* @param {string[]} attributes
* @param {string[]} orders
* @throws {AppwriteException}
* @returns {Promise}
*/
- createIndex: (collectionId, indexId, type, attributes, orders) => __awaiter(this, void 0, void 0, function* () {
+ createIndex: (collectionId, key, type, attributes, orders) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
- if (typeof indexId === 'undefined') {
- throw new AppwriteException('Missing required parameter: "indexId"');
+ if (typeof key === 'undefined') {
+ throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof type === 'undefined') {
throw new AppwriteException('Missing required parameter: "type"');
@@ -1996,8 +2002,8 @@
}
let path = '/database/collections/{collectionId}/indexes'.replace('{collectionId}', collectionId);
let payload = {};
- if (typeof indexId !== 'undefined') {
- payload['indexId'] = indexId;
+ if (typeof key !== 'undefined') {
+ payload['key'] = key;
}
if (typeof type !== 'undefined') {
payload['type'] = type;
@@ -2018,18 +2024,18 @@
*
*
* @param {string} collectionId
- * @param {string} indexId
+ * @param {string} key
* @throws {AppwriteException}
* @returns {Promise}
*/
- getIndex: (collectionId, indexId) => __awaiter(this, void 0, void 0, function* () {
+ getIndex: (collectionId, key) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
- if (typeof indexId === 'undefined') {
- throw new AppwriteException('Missing required parameter: "indexId"');
+ if (typeof key === 'undefined') {
+ throw new AppwriteException('Missing required parameter: "key"');
}
- let path = '/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}', collectionId).replace('{indexId}', indexId);
+ let path = '/database/collections/{collectionId}/indexes/{key}'.replace('{collectionId}', collectionId).replace('{key}', key);
let payload = {};
const uri = new URL(this.config.endpoint + path);
return yield this.call('get', uri, {
@@ -2041,18 +2047,18 @@
*
*
* @param {string} collectionId
- * @param {string} indexId
+ * @param {string} key
* @throws {AppwriteException}
* @returns {Promise}
*/
- deleteIndex: (collectionId, indexId) => __awaiter(this, void 0, void 0, function* () {
+ deleteIndex: (collectionId, key) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
- if (typeof indexId === 'undefined') {
- throw new AppwriteException('Missing required parameter: "indexId"');
+ if (typeof key === 'undefined') {
+ throw new AppwriteException('Missing required parameter: "key"');
}
- let path = '/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}', collectionId).replace('{indexId}', indexId);
+ let path = '/database/collections/{collectionId}/indexes/{key}'.replace('{collectionId}', collectionId).replace('{key}', key);
let payload = {};
const uri = new URL(this.config.endpoint + path);
return yield this.call('delete', uri, {
@@ -2234,6 +2240,22 @@
'content-type': 'application/json',
}, payload);
}),
+ /**
+ * List the currently active function runtimes.
+ *
+ * Get a list of all runtimes that are currently active in your project.
+ *
+ * @throws {AppwriteException}
+ * @returns {Promise}
+ */
+ listRuntimes: () => __awaiter(this, void 0, void 0, function* () {
+ let path = '/functions/runtimes';
+ let payload = {};
+ const uri = new URL(this.config.endpoint + path);
+ return yield this.call('get', uri, {
+ 'content-type': 'application/json',
+ }, payload);
+ }),
/**
* Get Function
*
diff --git a/public/scripts/init.js b/public/scripts/init.js
index f8c2dc4887..8ab87f607b 100644
--- a/public/scripts/init.js
+++ b/public/scripts/init.js
@@ -57,10 +57,27 @@ window.addEventListener("load", async () => {
const realtime = window.ls.container.get('realtime');
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
let current = {};
- window.ls.container.get('console').subscribe('project', event => {
- for (let project in event.payload) {
- current[project] = event.payload[project] ?? 0;
+ window.ls.container.get('console').subscribe(['project', 'console'], response => {
+ switch (response.event) {
+ case 'stats.connections':
+ for (let project in response.payload) {
+ current[project] = response.payload[project] ?? 0;
+ }
+ break;
+ case 'database.attributes.create':
+ case 'database.attributes.update':
+ case 'database.attributes.delete':
+ document.dispatchEvent(new CustomEvent('database.createAttribute'));
+
+ break;
+ case 'database.indexes.create':
+ case 'database.indexes.update':
+ case 'database.indexes.delete':
+ document.dispatchEvent(new CustomEvent('database.createIndex'));
+
+ break;
}
+
});
while (true) {
@@ -190,5 +207,20 @@ window.formValidation = (form, fields) => {
element.dispatchEvent(new Event("change"));
}
}
-});
+ });
};
+/**
+ * Method to add attribute for the UI on array attributes.
+ *
+ * Needs to be global - since client side routing will break it.
+ * @param {*} doc
+ * @param {*} key
+ * @returns
+ */
+function addAttribute(doc, key) {
+ if (!Array.isArray(doc[key])) {
+ doc[key] = [];
+ }
+ doc[key].push(null);
+ return doc;
+}
\ No newline at end of file
diff --git a/public/scripts/views/forms/chart.js b/public/scripts/views/forms/chart.js
index 263293c229..b7f9525c47 100644
--- a/public/scripts/views/forms/chart.js
+++ b/public/scripts/views/forms/chart.js
@@ -38,6 +38,9 @@
datasets: []
},
options: {
+ animation: {
+ duration: 0
+ },
responsive: true,
hover: {
mode: "nearest",
diff --git a/public/styles/fontello/config.json b/public/styles/fontello/config.json
index 38987fc59f..b633de0056 100644
--- a/public/styles/fontello/config.json
+++ b/public/styles/fontello/config.json
@@ -612,20 +612,6 @@
"code": 61751,
"src": "fontawesome"
},
- {
- "uid": "00d86f3e46c3c3d768e7246eb0eadd7f",
- "css": "discord",
- "code": 59463,
- "src": "custom_icons",
- "selected": true,
- "svg": {
- "path": "M435 432.9C411.3 432.9 392.5 453.8 392.5 479.2S411.7 525.4 435 525.4C458.8 525.4 477.5 504.6 477.5 479.2 477.9 453.8 458.8 432.9 435 432.9ZM587.1 432.9C563.3 432.9 544.6 453.8 544.6 479.2S563.8 525.4 587.1 525.4C610.8 525.4 629.6 504.6 629.6 479.2S610.8 432.9 587.1 432.9ZM789.6 83.3H231.3C184.2 83.3 145.8 121.7 145.8 169.2V732.5C145.8 780 184.2 818.3 231.3 818.3H703.8L681.7 741.3 735 790.8 785.4 837.5 875 916.7V169.2C875 121.7 836.7 83.3 789.6 83.3ZM628.8 627.5S613.8 609.6 601.3 593.8C655.8 578.3 676.7 544.2 676.7 544.2 659.6 555.4 643.3 563.3 628.8 568.8 607.9 577.5 587.9 583.3 568.3 586.7 528.3 594.2 491.7 592.1 460.4 586.3 436.7 581.7 416.3 575 399.2 568.3 389.6 564.6 379.2 560 368.8 554.2 367.5 553.3 366.3 552.9 365 552.1 364.2 551.7 363.8 551.3 363.3 550.8 355.8 546.7 351.7 543.8 351.7 543.8S371.7 577.1 424.6 592.9C412.1 608.8 396.7 627.5 396.7 627.5 304.6 624.6 269.6 564.2 269.6 564.2 269.6 430 329.6 321.3 329.6 321.3 389.6 276.3 446.7 277.5 446.7 277.5L450.8 282.5C375.8 304.2 341.3 337.1 341.3 337.1S350.4 332.1 365.8 325C410.4 305.4 445.8 300 460.4 298.8 462.9 298.3 465 297.9 467.5 297.9 492.9 294.6 521.7 293.8 551.7 297.1 591.3 301.7 633.8 313.3 677.1 337.1 677.1 337.1 644.2 305.8 573.3 284.2L579.2 277.5S636.3 276.3 696.3 321.3C696.3 321.3 756.3 430 756.3 564.2 756.3 564.2 720.8 624.6 628.8 627.5Z",
- "width": 1021
- },
- "search": [
- "discord-logo-black"
- ]
- },
{
"uid": "c2152732d525871cf35345955854f711",
"css": "moon-inv",
@@ -789,6 +775,54 @@
"css": "boolean",
"code": 61957,
"src": "fontawesome"
+ },
+ {
+ "uid": "47a1f80457068fbeab69fdb83d7d0817",
+ "css": "youtube-play",
+ "code": 61802,
+ "src": "fontawesome"
+ },
+ {
+ "uid": "b6941a012434c6246e2ad74248b6453e",
+ "css": "discord",
+ "code": 59463,
+ "src": "custom_icons",
+ "selected": true,
+ "svg": {
+ "path": "M1006.7 168.6C940.3 138.1 869 115.6 794.5 102.8 793.2 102.5 791.8 103.2 791.1 104.4 781.9 120.7 771.8 142 764.7 158.7 684.6 146.7 604.9 146.7 526.4 158.7 519.3 141.6 508.7 120.7 499.5 104.4 498.8 103.2 497.5 102.6 496.1 102.8 421.7 115.6 350.4 138.1 284 168.6 283.4 168.8 282.9 169.3 282.6 169.8 147.4 371.7 110.4 568.6 128.6 763.1 128.6 764.1 129.2 765 129.9 765.5 219.1 831 305.4 870.8 390.2 897.1 391.6 897.5 393 897 393.9 895.9 413.9 868.5 431.8 839.7 447.1 809.3 448 807.5 447.2 805.4 445.3 804.7 417 794 390 780.9 364 766 361.9 764.8 361.8 761.8 363.7 760.4 369.1 756.3 374.6 752.1 379.8 747.8 380.8 747 382.1 746.8 383.2 747.3 553.8 825.2 738.5 825.2 907.1 747.3 908.2 746.8 909.5 746.9 910.5 747.7 915.7 752 921.1 756.3 926.7 760.4 928.5 761.8 928.4 764.8 926.4 766 900.4 781.1 873.4 794 845 804.7 843.2 805.4 842.3 807.5 843.2 809.3 858.9 839.6 876.8 868.5 896.5 895.9 897.3 897 898.8 897.5 900.1 897.1 985.3 870.8 1071.7 831 1160.8 765.5 1161.6 765 1162.1 764.1 1162.2 763.2 1183.9 538.3 1125.8 343 1008 169.8 1007.8 169.3 1007.3 168.8 1006.7 168.6ZM472.6 644.7C421.2 644.7 378.9 597.5 378.9 539.6 378.9 481.7 420.4 434.6 472.6 434.6 525.2 434.6 567.1 482.1 566.3 539.6 566.3 597.5 524.8 644.7 472.6 644.7ZM819 644.7C767.6 644.7 725.3 597.5 725.3 539.6 725.3 481.7 766.8 434.6 819 434.6 871.6 434.6 913.5 482.1 912.6 539.6 912.6 597.5 871.6 644.7 819 644.7Z",
+ "width": 1291
+ },
+ "search": [
+ "discord-logo-color"
+ ]
+ },
+ {
+ "uid": "00d86f3e46c3c3d768e7246eb0eadd7f",
+ "css": "discord",
+ "code": 59463,
+ "src": "custom_icons",
+ "selected": false,
+ "svg": {
+ "path": "M435 432.9C411.3 432.9 392.5 453.8 392.5 479.2S411.7 525.4 435 525.4C458.8 525.4 477.5 504.6 477.5 479.2 477.9 453.8 458.8 432.9 435 432.9ZM587.1 432.9C563.3 432.9 544.6 453.8 544.6 479.2S563.8 525.4 587.1 525.4C610.8 525.4 629.6 504.6 629.6 479.2S610.8 432.9 587.1 432.9ZM789.6 83.3H231.3C184.2 83.3 145.8 121.7 145.8 169.2V732.5C145.8 780 184.2 818.3 231.3 818.3H703.8L681.7 741.3 735 790.8 785.4 837.5 875 916.7V169.2C875 121.7 836.7 83.3 789.6 83.3ZM628.8 627.5S613.8 609.6 601.3 593.8C655.8 578.3 676.7 544.2 676.7 544.2 659.6 555.4 643.3 563.3 628.8 568.8 607.9 577.5 587.9 583.3 568.3 586.7 528.3 594.2 491.7 592.1 460.4 586.3 436.7 581.7 416.3 575 399.2 568.3 389.6 564.6 379.2 560 368.8 554.2 367.5 553.3 366.3 552.9 365 552.1 364.2 551.7 363.8 551.3 363.3 550.8 355.8 546.7 351.7 543.8 351.7 543.8S371.7 577.1 424.6 592.9C412.1 608.8 396.7 627.5 396.7 627.5 304.6 624.6 269.6 564.2 269.6 564.2 269.6 430 329.6 321.3 329.6 321.3 389.6 276.3 446.7 277.5 446.7 277.5L450.8 282.5C375.8 304.2 341.3 337.1 341.3 337.1S350.4 332.1 365.8 325C410.4 305.4 445.8 300 460.4 298.8 462.9 298.3 465 297.9 467.5 297.9 492.9 294.6 521.7 293.8 551.7 297.1 591.3 301.7 633.8 313.3 677.1 337.1 677.1 337.1 644.2 305.8 573.3 284.2L579.2 277.5S636.3 276.3 696.3 321.3C696.3 321.3 756.3 430 756.3 564.2 756.3 564.2 720.8 624.6 628.8 627.5Z",
+ "width": 1021
+ },
+ "search": [
+ "discord-logo-black"
+ ]
+ },
+ {
+ "uid": "097a1d40f8e785fbd260946461a6ea9c",
+ "css": "discord",
+ "code": 59463,
+ "src": "custom_icons",
+ "selected": false,
+ "svg": {
+ "path": "M1092.8 89.1C1010.5 51.3 922.3 23.5 830 7.6 828.4 7.2 826.7 8 825.8 9.6 814.5 29.7 801.9 56.1 793.1 76.8 693.9 61.9 595.2 61.9 498 76.8 489.2 55.6 476.2 29.7 464.8 9.6 463.9 8.1 462.2 7.3 460.5 7.6 368.3 23.4 280.1 51.2 197.8 89.1 197.1 89.4 196.5 89.9 196 90.5 28.7 340.6-17.2 584.4 5.3 825.3 5.4 826.5 6.1 827.6 7 828.3 117.4 909.4 224.4 958.6 329.4 991.3 331 991.8 332.8 991.2 333.9 989.8 358.7 955.9 380.9 920.1 399.8 882.5 401 880.3 399.9 877.7 397.6 876.8 362.5 863.5 329.1 847.3 296.9 828.8 294.4 827.3 294.1 823.7 296.5 822 303.3 816.9 310 811.6 316.5 806.3 317.7 805.3 319.3 805.1 320.7 805.7 531.9 902.2 760.6 902.2 969.4 805.7 970.8 805.1 972.4 805.3 973.6 806.2 980.1 811.6 986.9 816.9 993.7 822 996 823.7 995.9 827.3 993.3 828.8 961.2 847.6 927.7 863.5 892.6 876.8 890.3 877.6 889.3 880.3 890.4 882.5 909.8 920.1 931.9 955.8 956.3 989.7 957.3 991.2 959.1 991.8 960.8 991.3 1066.3 958.6 1173.3 909.4 1283.7 828.3 1284.6 827.6 1285.2 826.5 1285.4 825.3 1312.3 546.9 1240.3 305 1094.5 90.6 1094.1 89.9 1093.5 89.4 1092.8 89.1ZM431.4 678.6C367.8 678.6 315.4 620.2 315.4 548.5 315.4 476.8 366.8 418.4 431.4 418.4 496.5 418.4 548.4 477.3 547.4 548.5 547.4 620.2 496 678.6 431.4 678.6ZM860.3 678.6C796.7 678.6 744.3 620.2 744.3 548.5 744.3 476.8 795.7 418.4 860.3 418.4 925.5 418.4 977.4 477.3 976.3 548.5 976.3 620.2 925.5 678.6 860.3 678.6Z",
+ "width": 1291
+ },
+ "search": [
+ "discord-logo-color"
+ ]
}
]
}
\ No newline at end of file
diff --git a/public/styles/icons.less b/public/styles/icons.less
index 756d244442..abcc5de9c7 100644
--- a/public/styles/icons.less
+++ b/public/styles/icons.less
@@ -1,7 +1,7 @@
@font-face {
font-family: 'fontello';
- src: url('data:application/octet-stream;base64,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') format('woff'),
- url('data:application/octet-stream;base64,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aQBuAGEAbAAgAGEAdQB0AGgAbwByAHMAIABAACAAZgBvAG4AdABlAGwAbABvAC4AYwBvAG0AZgBvAG4AdABlAGwAbABvAFIAZQBnAHUAbABhAHIAZgBvAG4AdABlAGwAbABvAGYAbwBuAHQAZQBsAGwAbwBWAGUAcgBzAGkAbwBuACAAMQAuADAAZgBvAG4AdABlAGwAbABvAEcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAAcwB2AGcAMgB0AHQAZgAgAGYAcgBvAG0AIABGAG8AbgB0AGUAbABsAG8AIABwAHIAbwBqAGUAYwB0AC4AaAB0AHQAcAA6AC8ALwBmAG8AbgB0AGUAbABsAG8ALgBjAG8AbQAAAAACAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkBAgEDAQQBBQEGAQcBCAEJAQoBCwEMAQ0BDgEPARABEQESARMBFAEVARYBFwEYARkBGgEbARwBHQEeAR8BIAEhASIBIwEkASUBJgEnASgBKQEqASsBLAEtAS4BLwEwATEBMgEzATQBNQE2ATcBOAE5AToBOwE8AT0BPgE/AUABQQFCAUMBRAFFAUYBRwFIAUkBSgFLAUwBTQFOAU8BUAFRAVIBUwFUAVUBVgFXAVgBWQFaAVsBXAFdAV4BXwFgAWEBYgFjAWQBZQFmAWcBaAFpAWoBawFsAW0BbgFvAXABcQFyAXMBdAF1AXYBdwF4AXkBegAEdXNlcgZmb2xkZXIEbGlzdAVsb2dpbgNjb2cHdHdpdHRlcgthcnRpY2xlLWFsdAZjYW5jZWwEaG9tZQhkb3duLWRpcghmYWNlYm9vawhhc3RlcmlzawZ1cGxvYWQJc3RvcHdhdGNoBmV4cG9ydAVoZWFydARwbHVzBnVwLWRpcgRtZW51CWxlZnQtb3BlbgpyaWdodC1vcGVuBWluYm94BndyZW5jaAdjb21tZW50DXN0YWNrb3ZlcmZsb3cIcXVlc3Rpb24Kb2stY2lyY2xlZAd3YXJuaW5nBG1haWwEbGluawdrZXktaW52BXRyYXNoCGRvd25sb2FkB2dsYXNzZXMGcXJjb2RlB3NodWZmbGUDZXllBGxvY2sGc2VhcmNoBGJlbGwFdXNlcnMIbG9jYXRpb24JYnJpZWZjYXNlCWluc3RhZ3JhbQVjbG9jawVwaG9uZQhjYWxlbmRhcgVwcmludARlZGl0BGJvbGQGaXRhbGljBnJvY2tldAh3aGF0c2FwcAVkb3QtMwxpbmZvLWNpcmNsZWQIdmlkZW9jYW0LcXVvdGUtcmlnaHQHcGljdHVyZQdwYWxldHRlBGxhbXAJYm9vay1vcGVuAm9rCGNoYXQtYWx0B2FyY2hpdmUEcGxheQVwYXVzZQlkb3duLW9wZW4HdXAtb3BlbgVtaW51cwhleGNoYW5nZQduZXR3b3JrB2Rpc2NvcmQIbW9vbi1pbnYHc3VuLWludg5jYW5jZWwtY2lyY2xlZAlsaWdodG5pbmcDZGV2CXJpZ2h0LWRpcghsZWZ0LWRpcgRmaXJlCmhhY2tlcm5ld3MGcmVkZGl0BnN0cmluZwdpbnRlZ2VyAmlwBG1vcmUDa2V5CGxpbmstZXh0DmdpdGh1Yi1jaXJjbGVkBmZpbHRlcgRkb2NzC2xpc3QtYnVsbGV0DWxpc3QtbnVtYmVyZWQJdW5kZXJsaW5lBHNvcnQIbGlua2VkaW4Fc21pbGUIa2V5Ym9hcmQEY29kZQZzaGllbGQSYW5nbGUtY2lyY2xlZC1sZWZ0E2FuZ2xlLWNpcmNsZWQtcmlnaHQMeW91dHViZS1wbGF5CWJpdGJ1Y2tldAd3aW5kb3dzC2RvdC1jaXJjbGVkCndoZWVsY2hhaXIEYmFuawZnb29nbGUPYnVpbGRpbmctZmlsbGVkCGRhdGFiYXNlCGxpZmVidW95BmhlYWRlcgpiaW5vY3VsYXJzCmNoYXJ0LWFyZWEHYm9vbGVhbglwaW50ZXJlc3QGbWVkaXVtBmdpdGxhYgh0ZWxlZ3JhbQAAAAEAAf//AA8AAAAAAAAAAAAAAAAAAAAAsAAsILAAVVhFWSAgS7gADlFLsAZTWliwNBuwKFlgZiCKVViwAiVhuQgACABjYyNiGyEhsABZsABDI0SyAAEAQ2BCLbABLLAgYGYtsAIsIyEjIS2wAywgZLMDFBUAQkOwE0MgYGBCsQIUQ0KxJQNDsAJDVHggsAwjsAJDQ2FksARQeLICAgJDYEKwIWUcIbACQ0OyDhUBQhwgsAJDI0KyEwETQ2BCI7AAUFhlWbIWAQJDYEItsAQssAMrsBVDWCMhIyGwFkNDI7AAUFhlWRsgZCCwwFCwBCZasigBDUNFY0WwBkVYIbADJVlSW1ghIyEb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') format('truetype');
+ src: url('data:application/octet-stream;base64,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') format('woff'),
+ url('data:application/octet-stream;base64,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MQAuADAAZgBvAG4AdABlAGwAbABvAEcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAAcwB2AGcAMgB0AHQAZgAgAGYAcgBvAG0AIABGAG8AbgB0AGUAbABsAG8AIABwAHIAbwBqAGUAYwB0AC4AaAB0AHQAcAA6AC8ALwBmAG8AbgB0AGUAbABsAG8ALgBjAG8AbQAAAAACAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkBAgEDAQQBBQEGAQcBCAEJAQoBCwEMAQ0BDgEPARABEQESARMBFAEVARYBFwEYARkBGgEbARwBHQEeAR8BIAEhASIBIwEkASUBJgEnASgBKQEqASsBLAEtAS4BLwEwATEBMgEzATQBNQE2ATcBOAE5AToBOwE8AT0BPgE/AUABQQFCAUMBRAFFAUYBRwFIAUkBSgFLAUwBTQFOAU8BUAFRAVIBUwFUAVUBVgFXAVgBWQFaAVsBXAFdAV4BXwFgAWEBYgFjAWQBZQFmAWcBaAFpAWoBawFsAW0BbgFvAXABcQFyAXMBdAF1AXYBdwF4AXkBegAEdXNlcgZmb2xkZXIEbGlzdAVsb2dpbgNjb2cHdHdpdHRlcgthcnRpY2xlLWFsdAZjYW5jZWwEaG9tZQhkb3duLWRpcghmYWNlYm9vawhhc3RlcmlzawZ1cGxvYWQJc3RvcHdhdGNoBmV4cG9ydAVoZWFydARwbHVzBnVwLWRpcgRtZW51CWxlZnQtb3BlbgpyaWdodC1vcGVuBWluYm94BndyZW5jaAdjb21tZW50DXN0YWNrb3ZlcmZsb3cIcXVlc3Rpb24Kb2stY2lyY2xlZAd3YXJuaW5nBG1haWwEbGluawdrZXktaW52BXRyYXNoCGRvd25sb2FkB2dsYXNzZXMGcXJjb2RlB3NodWZmbGUDZXllBGxvY2sGc2VhcmNoBGJlbGwFdXNlcnMIbG9jYXRpb24JYnJpZWZjYXNlCWluc3RhZ3JhbQVjbG9jawVwaG9uZQhjYWxlbmRhcgVwcmludARlZGl0BGJvbGQGaXRhbGljBnJvY2tldAh3aGF0c2FwcAVkb3QtMwxpbmZvLWNpcmNsZWQIdmlkZW9jYW0LcXVvdGUtcmlnaHQHcGljdHVyZQdwYWxldHRlBGxhbXAJYm9vay1vcGVuAm9rCGNoYXQtYWx0B2FyY2hpdmUEcGxheQVwYXVzZQlkb3duLW9wZW4HdXAtb3BlbgVtaW51cwhleGNoYW5nZQduZXR3b3JrB2Rpc2NvcmQIbW9vbi1pbnYHc3VuLWludg5jYW5jZWwtY2lyY2xlZAlsaWdodG5pbmcDZGV2CXJpZ2h0LWRpcghsZWZ0LWRpcgRmaXJlCmhhY2tlcm5ld3MGcmVkZGl0BnN0cmluZwdpbnRlZ2VyAmlwBG1vcmUDa2V5CGxpbmstZXh0DmdpdGh1Yi1jaXJjbGVkBmZpbHRlcgRkb2NzC2xpc3QtYnVsbGV0DWxpc3QtbnVtYmVyZWQJdW5kZXJsaW5lBHNvcnQIbGlua2VkaW4Fc21pbGUIa2V5Ym9hcmQEY29kZQZzaGllbGQSYW5nbGUtY2lyY2xlZC1sZWZ0E2FuZ2xlLWNpcmNsZWQtcmlnaHQMeW91dHViZS1wbGF5CWJpdGJ1Y2tldAd3aW5kb3dzC2RvdC1jaXJjbGVkCndoZWVsY2hhaXIEYmFuawZnb29nbGUPYnVpbGRpbmctZmlsbGVkCGRhdGFiYXNlCGxpZmVidW95BmhlYWRlcgpiaW5vY3VsYXJzCmNoYXJ0LWFyZWEHYm9vbGVhbglwaW50ZXJlc3QGbWVkaXVtBmdpdGxhYgh0ZWxlZ3JhbQAAAAEAAf//AA8AAAAAAAAAAAAAAAAAAAAAsAAsILAAVVhFWSAgS7gADlFLsAZTWliwNBuwKFlgZiCKVViwAiVhuQgACABjYyNiGyEhsABZsABDI0SyAAEAQ2BCLbABLLAgYGYtsAIsIyEjIS2wAywgZLMDFBUAQkOwE0MgYGBCsQIUQ0KxJQNDsAJDVHggsAwjsAJDQ2FksARQeLICAgJDYEKwIWUcIbACQ0OyDhUBQhwgsAJDI0KyEwETQ2BCI7AAUFhlWbIWAQJDYEItsAQssAMrsBVDWCMhIyGwFkNDI7AAUFhlWRsgZCCwwFCwBCZasigBDUNFY0WwBkVYIbADJVlSW1ghIyEbilggsFBQWCGwQFkbILA4UFghsDhZWSCxAQ1DRWNFYWSwKFBYIbEBDUNFY0UgsDBQWCGwMFkbILDAUFggZiCKimEgsApQWGAbILAgUFghsApgGyCwNlBYIbA2YBtgWVlZG7ACJbAMQ2OwAFJYsABLsApQWCGwDEMbS7AeUFghsB5LYbgQ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') format('truetype');
}
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
@@ -9,7 +9,7 @@
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'fontello';
- src: url('../font/fontello.svg?4742076#fontello') format('svg');
+ src: url('../font/fontello.svg?81943528#fontello') format('svg');
}
}
*/
diff --git a/src/Appwrite/Auth/Auth.php b/src/Appwrite/Auth/Auth.php
index 2895b3db11..8ac5839ee0 100644
--- a/src/Appwrite/Auth/Auth.php
+++ b/src/Appwrite/Auth/Auth.php
@@ -242,9 +242,9 @@ class Auth
public static function isPrivilegedUser(array $roles): bool
{
if (
- array_key_exists('role:'.self::USER_ROLE_OWNER, $roles) ||
- array_key_exists('role:'.self::USER_ROLE_DEVELOPER, $roles) ||
- array_key_exists('role:'.self::USER_ROLE_ADMIN, $roles)
+ in_array('role:'.self::USER_ROLE_OWNER, $roles) ||
+ in_array('role:'.self::USER_ROLE_DEVELOPER, $roles) ||
+ in_array('role:'.self::USER_ROLE_ADMIN, $roles)
) {
return true;
}
@@ -261,7 +261,7 @@ class Auth
*/
public static function isAppUser(array $roles): bool
{
- if (array_key_exists('role:'.self::USER_ROLE_APP, $roles)) {
+ if (in_array('role:'.self::USER_ROLE_APP, $roles)) {
return true;
}
@@ -278,7 +278,7 @@ class Auth
{
$roles = [];
- if (!self::isPrivilegedUser(Authorization::$roles) && !self::isAppUser(Authorization::$roles)) {
+ if (!self::isPrivilegedUser(Authorization::getRoles()) && !self::isAppUser(Authorization::getRoles())) {
if ($user->getId()) {
$roles[] = 'user:'.$user->getId();
$roles[] = 'role:'.Auth::USER_ROLE_MEMBER;
diff --git a/src/Appwrite/Auth/Validator/Password.php b/src/Appwrite/Auth/Validator/Password.php
index d7168774bd..3e9bb6fbfc 100644
--- a/src/Appwrite/Auth/Validator/Password.php
+++ b/src/Appwrite/Auth/Validator/Password.php
@@ -18,7 +18,7 @@ class Password extends Validator
*
* @return string
*/
- public function getDescription()
+ public function getDescription(): string
{
return 'Password must be at least 8 characters';
}
@@ -30,7 +30,7 @@ class Password extends Validator
*
* @return bool
*/
- public function isValid($value)
+ public function isValid($value): bool
{
if (!\is_string($value)) {
return false;
diff --git a/src/Appwrite/Database/Validator/Authorization.php b/src/Appwrite/Database/Validator/Authorization.php
index 4a7f82b392..914c3e40db 100644
--- a/src/Appwrite/Database/Validator/Authorization.php
+++ b/src/Appwrite/Database/Validator/Authorization.php
@@ -46,7 +46,7 @@ class Authorization extends Validator
*
* @return string
*/
- public function getDescription()
+ public function getDescription(): string
{
return $this->message;
}
@@ -60,7 +60,7 @@ class Authorization extends Validator
*
* @return bool
*/
- public function isValid($permissions)
+ public function isValid($permissions): bool
{
if (!self::$status) {
return true;
diff --git a/src/Appwrite/Database/Validator/Collection.php b/src/Appwrite/Database/Validator/Collection.php
index 45faa2801d..139ff20cd0 100644
--- a/src/Appwrite/Database/Validator/Collection.php
+++ b/src/Appwrite/Database/Validator/Collection.php
@@ -39,7 +39,7 @@ class Collection extends Structure
*
* @return bool
*/
- public function isValid($document)
+ public function isValid($document): bool
{
$document = new Document(
\array_merge($this->merge, ($document instanceof Document) ? $document->getArrayCopy() : $document)
diff --git a/src/Appwrite/Database/Validator/CustomId.php b/src/Appwrite/Database/Validator/CustomId.php
index 59c14de8be..39a8500f81 100644
--- a/src/Appwrite/Database/Validator/CustomId.php
+++ b/src/Appwrite/Database/Validator/CustomId.php
@@ -13,7 +13,7 @@ class CustomId extends Key {
*
* @return bool
*/
- public function isValid($value)
+ public function isValid($value): bool
{
return $value == 'unique()' || parent::isValid($value);
diff --git a/src/Appwrite/Database/Validator/DocumentId.php b/src/Appwrite/Database/Validator/DocumentId.php
index fcc2a57aef..988497cfe6 100644
--- a/src/Appwrite/Database/Validator/DocumentId.php
+++ b/src/Appwrite/Database/Validator/DocumentId.php
@@ -42,7 +42,7 @@ class DocumentId extends Validator
*
* @return string
*/
- public function getDescription()
+ public function getDescription(): string
{
return $this->message;
}
@@ -56,7 +56,7 @@ class DocumentId extends Validator
*
* @return bool
*/
- public function isValid($id)
+ public function isValid($id): bool
{
$document = $this->database->getDocument($id);
diff --git a/src/Appwrite/Database/Validator/Key.php b/src/Appwrite/Database/Validator/Key.php
index 1aab06f76d..328f06fec6 100644
--- a/src/Appwrite/Database/Validator/Key.php
+++ b/src/Appwrite/Database/Validator/Key.php
@@ -18,7 +18,7 @@ class Key extends Validator
*
* @return string
*/
- public function getDescription()
+ public function getDescription(): string
{
return $this->message;
}
@@ -32,7 +32,7 @@ class Key extends Validator
*
* @return bool
*/
- public function isValid($value)
+ public function isValid($value): bool
{
if (!\is_string($value)) {
return false;
diff --git a/src/Appwrite/Database/Validator/Permissions.php b/src/Appwrite/Database/Validator/Permissions.php
index 2c90eef60d..1419c3e7c7 100644
--- a/src/Appwrite/Database/Validator/Permissions.php
+++ b/src/Appwrite/Database/Validator/Permissions.php
@@ -34,7 +34,7 @@ class Permissions extends Validator
*
* @return string
*/
- public function getDescription()
+ public function getDescription(): string
{
return $this->message;
}
@@ -48,7 +48,7 @@ class Permissions extends Validator
*
* @return bool
*/
- public function isValid($value)
+ public function isValid($value): bool
{
if (!\is_array($value) && !empty($value)) {
$this->message = 'Invalid permissions data structure';
diff --git a/src/Appwrite/Database/Validator/Structure.php b/src/Appwrite/Database/Validator/Structure.php
index 7f33074766..6cd143a8f7 100644
--- a/src/Appwrite/Database/Validator/Structure.php
+++ b/src/Appwrite/Database/Validator/Structure.php
@@ -109,7 +109,7 @@ class Structure extends Validator
*
* @return string
*/
- public function getDescription()
+ public function getDescription(): string
{
return 'Invalid document structure: '.$this->message;
}
@@ -123,7 +123,7 @@ class Structure extends Validator
*
* @return bool
*/
- public function isValid($document)
+ public function isValid($document): bool
{
$document = (\is_array($document)) ? new Document($document) : $document;
diff --git a/src/Appwrite/Database/Validator/UID.php b/src/Appwrite/Database/Validator/UID.php
index 8e60ec0632..ebab6b4628 100644
--- a/src/Appwrite/Database/Validator/UID.php
+++ b/src/Appwrite/Database/Validator/UID.php
@@ -13,7 +13,7 @@ class UID extends Validator
*
* @return string
*/
- public function getDescription()
+ public function getDescription(): string
{
return 'Invalid UID format';
}
@@ -27,7 +27,7 @@ class UID extends Validator
*
* @return bool
*/
- public function isValid($value)
+ public function isValid($value): bool
{
if ($value === 0) { // TODO Deprecate confition when we get the chance.
return true;
diff --git a/src/Appwrite/Detector/Detector.php b/src/Appwrite/Detector/Detector.php
index 75c4387244..186bd8ff47 100644
--- a/src/Appwrite/Detector/Detector.php
+++ b/src/Appwrite/Detector/Detector.php
@@ -47,7 +47,18 @@ class Detector
*/
public function getClient(): array
{
- $client = $this->getDetector()->getClient();
+ if (strpos($this->userAgent, 'Appwrite CLI') !== false) {
+ $version = explode(' ', $this->userAgent)[0];
+ $version = explode('/', $version)[1];
+ $client = [
+ 'type' => 'desktop',
+ 'short_name' => 'cli',
+ 'name' => 'Appwrite CLI',
+ 'version' => $version
+ ];
+ } else {
+ $client = $this->getDetector()->getClient();
+ }
return [
'clientType' => (isset($client['type'])) ? $client['type'] : '',
@@ -86,4 +97,16 @@ class Detector
return $this->detctor;
}
+
+ /**
+ * Sets whether to skip bot detection.
+ * It is needed if we want bots to be processed as a simple clients. So we can detect if it is mobile client,
+ * or desktop, or enything else. By default all this information is not retrieved for the bots.
+ *
+ * @param bool $skip
+ */
+ public function skipBotDetection(bool $skip = true): void
+ {
+ $this->getDetector()->skipBotDetection($skip);
+ }
}
diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php
index ee42b9effa..aec5389548 100644
--- a/src/Appwrite/Messaging/Adapter/Realtime.php
+++ b/src/Appwrite/Messaging/Adapter/Realtime.php
@@ -234,16 +234,18 @@ class Realtime extends Adapter
/**
* Create channels array based on the event name and payload.
- *
+ *
* @param string $event
* @param Document $payload
+ * @param Document|null $project
* @return array
*/
- public static function fromPayload(string $event, Document $payload): array
+ public static function fromPayload(string $event, Document $payload, Document $project = null, Document $collection = null): array
{
$channels = [];
$roles = [];
$permissionsChanged = false;
+ $projectId = null;
switch (true) {
case strpos($event, 'account.recovery.') === 0:
@@ -273,11 +275,29 @@ class Realtime extends Adapter
$channels[] = 'teams.' . $payload->getId();
$roles = ['team:' . $payload->getId()];
+ break;
+ case strpos($event, 'database.attributes.') === 0:
+ case strpos($event, 'database.indexes.') === 0:
+ $channels[] = 'console';
+ $projectId = 'console';
+ $roles = ['team:' . $project->getAttribute('teamId')];
+
break;
case strpos($event, 'database.documents.') === 0:
+ if ($collection->isEmpty()) {
+ throw new \Exception('Collection need to be passed to to Realtime for Document events in the Database.');
+ }
+
$channels[] = 'documents';
$channels[] = 'collections.' . $payload->getAttribute('$collection') . '.documents';
$channels[] = 'documents.' . $payload->getId();
+
+ $roles = ($collection->getAttribute('permission') === 'collection') ? $collection->getRead() : $payload->getRead();
+
+ break;
+ case strpos($event, 'storage.buckets.') === 0:
+ $channels[] = 'buckets';
+ $channels[] = 'buckets.' . $payload->getId();
$roles = $payload->getRead();
break;
@@ -301,7 +321,8 @@ class Realtime extends Adapter
return [
'channels' => $channels,
'roles' => $roles,
- 'permissionsChanged' => $permissionsChanged
+ 'permissionsChanged' => $permissionsChanged,
+ 'projectId' => $projectId
];
}
}
diff --git a/src/Appwrite/Migration/Version/V06.php b/src/Appwrite/Migration/Version/V06.php
index b28ce8ac2f..45bd284f2e 100644
--- a/src/Appwrite/Migration/Version/V06.php
+++ b/src/Appwrite/Migration/Version/V06.php
@@ -46,8 +46,8 @@ class V06 extends Migration
$document->setAttribute('secret', json_encode([
'data' => OpenSSL::encrypt($document->getAttribute('secret'), OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
'method' => OpenSSL::CIPHER_AES_128_GCM,
- 'iv' => bin2hex($iv),
- 'tag' => bin2hex($tag),
+ 'iv' => \bin2hex($iv),
+ 'tag' => \bin2hex($tag ?? ''),
'version' => '1',
]));
}
diff --git a/src/Appwrite/Network/Validator/CNAME.php b/src/Appwrite/Network/Validator/CNAME.php
index 12b88908f7..93a302b962 100644
--- a/src/Appwrite/Network/Validator/CNAME.php
+++ b/src/Appwrite/Network/Validator/CNAME.php
@@ -22,7 +22,7 @@ class CNAME extends Validator
/**
* @return string
*/
- public function getDescription()
+ public function getDescription(): string
{
return 'Invalid CNAME record';
}
@@ -34,7 +34,7 @@ class CNAME extends Validator
*
* @return bool
*/
- public function isValid($domain)
+ public function isValid($domain): bool
{
if (!is_string($domain)) {
return false;
diff --git a/src/Appwrite/Network/Validator/Domain.php b/src/Appwrite/Network/Validator/Domain.php
index 8a70ec8b03..9db6641316 100644
--- a/src/Appwrite/Network/Validator/Domain.php
+++ b/src/Appwrite/Network/Validator/Domain.php
@@ -20,7 +20,7 @@ class Domain extends Validator
*
* @return string
*/
- public function getDescription()
+ public function getDescription(): string
{
return 'Value must be a valid domain';
}
@@ -35,7 +35,7 @@ class Domain extends Validator
* @param mixed $value
* @return bool
*/
- public function isValid($value)
+ public function isValid($value): bool
{
if (empty($value)) {
return false;
diff --git a/src/Appwrite/Network/Validator/Email.php b/src/Appwrite/Network/Validator/Email.php
index ab3a324ea1..efc1a5d5b3 100644
--- a/src/Appwrite/Network/Validator/Email.php
+++ b/src/Appwrite/Network/Validator/Email.php
@@ -20,7 +20,7 @@ class Email extends Validator
*
* @return string
*/
- public function getDescription()
+ public function getDescription(): string
{
return 'Value must be a valid email address';
}
@@ -33,7 +33,7 @@ class Email extends Validator
* @param mixed $value
* @return bool
*/
- public function isValid($value)
+ public function isValid($value): bool
{
if (!\filter_var($value, FILTER_VALIDATE_EMAIL)) {
return false;
diff --git a/src/Appwrite/Network/Validator/Host.php b/src/Appwrite/Network/Validator/Host.php
index b0f8b23073..703907c3d3 100644
--- a/src/Appwrite/Network/Validator/Host.php
+++ b/src/Appwrite/Network/Validator/Host.php
@@ -30,7 +30,7 @@ class Host extends Validator
*
* @return string
*/
- public function getDescription()
+ public function getDescription(): string
{
return 'URL host must be one of: ' . \implode(', ', $this->whitelist);
}
@@ -43,7 +43,7 @@ class Host extends Validator
* @param mixed $value
* @return bool
*/
- public function isValid($value)
+ public function isValid($value): bool
{
$urlValidator = new URL();
diff --git a/src/Appwrite/Network/Validator/IP.php b/src/Appwrite/Network/Validator/IP.php
index 5d7833da6f..c9b50fe3ca 100644
--- a/src/Appwrite/Network/Validator/IP.php
+++ b/src/Appwrite/Network/Validator/IP.php
@@ -46,7 +46,7 @@ class IP extends Validator
*
* @return string
*/
- public function getDescription()
+ public function getDescription(): string
{
return 'Value must be a valid IP address';
}
@@ -59,7 +59,7 @@ class IP extends Validator
* @param mixed $value
* @return bool
*/
- public function isValid($value)
+ public function isValid($value): bool
{
switch ($this->type) {
case self::ALL:
diff --git a/src/Appwrite/Network/Validator/Origin.php b/src/Appwrite/Network/Validator/Origin.php
index 9677bc14eb..385f9e2dc0 100644
--- a/src/Appwrite/Network/Validator/Origin.php
+++ b/src/Appwrite/Network/Validator/Origin.php
@@ -84,7 +84,7 @@ class Origin extends Validator
}
}
- public function getDescription()
+ public function getDescription(): string
{
if (!\array_key_exists($this->client, $this->platforms)) {
return 'Unsupported platform';
@@ -102,7 +102,7 @@ class Origin extends Validator
*
* @return bool
*/
- public function isValid($origin)
+ public function isValid($origin): bool
{
if (!is_string($origin)) {
return false;
diff --git a/src/Appwrite/Network/Validator/URL.php b/src/Appwrite/Network/Validator/URL.php
index d26159381c..61d1941e02 100644
--- a/src/Appwrite/Network/Validator/URL.php
+++ b/src/Appwrite/Network/Validator/URL.php
@@ -20,7 +20,7 @@ class URL extends Validator
*
* @return string
*/
- public function getDescription()
+ public function getDescription(): string
{
return 'Value must be a valid URL';
}
@@ -33,7 +33,7 @@ class URL extends Validator
* @param mixed $value
* @return bool
*/
- public function isValid($value)
+ public function isValid($value): bool
{
if (\filter_var($value, FILTER_VALIDATE_URL) === false) {
return false;
diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php
index 595f5db84d..771761b186 100644
--- a/src/Appwrite/Specification/Format/Swagger2.php
+++ b/src/Appwrite/Specification/Format/Swagger2.php
@@ -392,10 +392,7 @@ class Swagger2 extends Format
foreach ($this->models as $model) {
foreach ($model->getRules() as $rule) {
- if (
- in_array($model->getType(), $usedModels)
- && !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])
- ) {
+ if (!in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])) {
$usedModels[] = $rule['type'];
}
}
diff --git a/src/Appwrite/Template/Template.php b/src/Appwrite/Template/Template.php
index 7c175de770..6f586a6256 100644
--- a/src/Appwrite/Template/Template.php
+++ b/src/Appwrite/Template/Template.php
@@ -2,8 +2,8 @@
namespace Appwrite\Template;
+use Appwrite\Utopia\View;
use Exception;
-use Utopia\View;
class Template extends View
{
@@ -63,7 +63,7 @@ class Template extends View
*
* @throws Exception
*/
- public function render($minify = true)
+ public function render($minify = true): string
{
if ($this->rendered) { // Don't render any template
return '';
diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php
index 118091a721..2c75f43472 100644
--- a/src/Appwrite/Utopia/Response.php
+++ b/src/Appwrite/Utopia/Response.php
@@ -34,7 +34,6 @@ use Appwrite\Utopia\Response\Model\File;
use Appwrite\Utopia\Response\Model\Bucket;
use Appwrite\Utopia\Response\Model\Func;
use Appwrite\Utopia\Response\Model\Index;
-use Appwrite\Utopia\Response\Model\FuncPermissions;
use Appwrite\Utopia\Response\Model\JWT;
use Appwrite\Utopia\Response\Model\Key;
use Appwrite\Utopia\Response\Model\Language;
@@ -55,6 +54,7 @@ use Appwrite\Utopia\Response\Model\Token;
use Appwrite\Utopia\Response\Model\Webhook;
use Appwrite\Utopia\Response\Model\Preferences;
use Appwrite\Utopia\Response\Model\Mock; // Keep last
+use Appwrite\Utopia\Response\Model\Runtime;
use Appwrite\Utopia\Response\Model\UsageBuckets;
use Appwrite\Utopia\Response\Model\UsageCollection;
use Appwrite\Utopia\Response\Model\UsageDatabase;
@@ -143,6 +143,8 @@ class Response extends SwooleResponse
// Functions
const MODEL_FUNCTION = 'function';
const MODEL_FUNCTION_LIST = 'functionList';
+ const MODEL_RUNTIME = 'runtime';
+ const MODEL_RUNTIME_LIST = 'runtimeList';
const MODEL_TAG = 'tag';
const MODEL_TAG_LIST = 'tagList';
const MODEL_EXECUTION = 'execution';
@@ -204,6 +206,7 @@ class Response extends SwooleResponse
->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM))
->setModel(new BaseList('Memberships List', self::MODEL_MEMBERSHIP_LIST, 'memberships', self::MODEL_MEMBERSHIP))
->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION))
+ ->setModel(new BaseList('Runtimes List', self::MODEL_RUNTIME_LIST, 'runtimes', self::MODEL_RUNTIME))
->setModel(new BaseList('Tags List', self::MODEL_TAG_LIST, 'tags', self::MODEL_TAG))
->setModel(new BaseList('Executions List', self::MODEL_EXECUTION_LIST, 'executions', self::MODEL_EXECUTION))
->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false))
@@ -243,7 +246,7 @@ class Response extends SwooleResponse
->setModel(new Team())
->setModel(new Membership())
->setModel(new Func())
- ->setModel(new FuncPermissions())
+ ->setModel(new Runtime())
->setModel(new Tag())
->setModel(new Execution())
->setModel(new Project())
diff --git a/src/Appwrite/Utopia/Response/Model/Collection.php b/src/Appwrite/Utopia/Response/Model/Collection.php
index 03cd8ed382..933b5f64f8 100644
--- a/src/Appwrite/Utopia/Response/Model/Collection.php
+++ b/src/Appwrite/Utopia/Response/Model/Collection.php
@@ -36,6 +36,12 @@ class Collection extends Model
'default' => '',
'example' => 'My Collection',
])
+ ->addRule('enabled', [
+ 'type' => self::TYPE_BOOLEAN,
+ 'description' => 'Collection enabled.',
+ 'default' => true,
+ 'example' => false,
+ ])
->addRule('permission', [
'type' => self::TYPE_STRING,
'description' => 'Collection permission model. Possible values: `document` or `collection`',
diff --git a/src/Appwrite/Utopia/Response/Model/Func.php b/src/Appwrite/Utopia/Response/Model/Func.php
index df0b35caef..f731438b09 100644
--- a/src/Appwrite/Utopia/Response/Model/Func.php
+++ b/src/Appwrite/Utopia/Response/Model/Func.php
@@ -17,10 +17,10 @@ class Func extends Model
'example' => '5e5ea5c16897e',
])
->addRule('execute', [
- 'type' => Response::MODEL_FUNC_PERMISSIONS,
- 'description' => 'Function permissions.',
- 'default' => new \stdClass,
- 'example' => new \stdClass,
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Execution permissions.',
+ 'default' => '',
+ 'example' => 'role:member',
'array' => false,
])
->addRule('name', [
diff --git a/src/Appwrite/Utopia/Response/Model/FuncPermissions.php b/src/Appwrite/Utopia/Response/Model/FuncPermissions.php
deleted file mode 100644
index 22ee62b759..0000000000
--- a/src/Appwrite/Utopia/Response/Model/FuncPermissions.php
+++ /dev/null
@@ -1,42 +0,0 @@
-addRule('execute', [
- 'type' => self::TYPE_STRING,
- 'description' => 'Execution permissions.',
- 'default' => [],
- 'example' => 'user:5e5ea5c16897e',
- 'array' => true,
- ])
- ;
- }
-
- /**
- * Get Name
- *
- * @return string
- */
- public function getName():string
- {
- return 'FuncPermissions';
- }
-
- /**
- * Get Collection
- *
- * @return string
- */
- public function getType():string
- {
- return Response::MODEL_FUNC_PERMISSIONS;
- }
-}
diff --git a/src/Appwrite/Utopia/Response/Model/Runtime.php b/src/Appwrite/Utopia/Response/Model/Runtime.php
new file mode 100644
index 0000000000..2aae2f95ef
--- /dev/null
+++ b/src/Appwrite/Utopia/Response/Model/Runtime.php
@@ -0,0 +1,78 @@
+addRule('$id', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Runtime ID.',
+ 'default' => '',
+ 'example' => 'python-3.8',
+ ])
+ ->addRule('name', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Runtime Name.',
+ 'default' => '',
+ 'example' => 'Python'
+ ])
+ ->addRule('version', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Runtime version.',
+ 'default' => '',
+ 'example' => '3.8',
+ ])
+ ->addRule('base', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Base Docker image used to build the runtime.',
+ 'default' => '',
+ 'example' => 'python:3.8-alpine',
+ ])
+ ->addRule('image', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Image name of Docker Hub.',
+ 'default' => '',
+ 'example' => 'appwrite\/runtime-for-python:3.8',
+ ])
+ ->addRule('logo', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Name of the logo image.',
+ 'default' => '',
+ 'example' => 'python.png',
+ ])
+ ->addRule('supports', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'List of supported architectures.',
+ 'default' => '',
+ 'example' => 'amd64',
+ 'array' => true,
+ ])
+ ;
+ }
+
+ /**
+ * Get Name
+ *
+ * @return string
+ */
+ public function getName():string
+ {
+ return 'Runtime';
+ }
+
+ /**
+ * Get Type
+ *
+ * @return string
+ */
+ public function getType():string
+ {
+ return Response::MODEL_RUNTIME;
+ }
+}
diff --git a/src/Appwrite/Utopia/View.php b/src/Appwrite/Utopia/View.php
new file mode 100644
index 0000000000..4dee180877
--- /dev/null
+++ b/src/Appwrite/Utopia/View.php
@@ -0,0 +1,22 @@
+ 'unique()',
'name' => 'Movies',
- 'read' => ['role:all'],
- 'write' => ['role:all'],
+ 'read' => [],
+ 'write' => [],
'permission' => 'document',
]);
@@ -30,6 +30,70 @@ trait DatabaseBase
return ['moviesId' => $movies['body']['$id']];
}
+ /**
+ * @depends testCreateCollection
+ */
+ public function testDisableCollection(array $data): void
+ {
+ /**
+ * Test for SUCCESS
+ */
+ $response = $this->client->call(Client::METHOD_PUT, '/database/collections/' . $data['moviesId'], array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey']
+ ]), [
+ 'name' => 'Movies',
+ 'enabled' => false,
+ 'permission' => 'document',
+ ]);
+
+ $this->assertEquals($response['headers']['status-code'], 200);
+ $this->assertFalse($response['body']['enabled']);
+
+ if ($this->getSide() === 'client') {
+ $responseCreateDocument = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'documentId' => 'unique()',
+ 'data' => [
+ 'title' => 'Captain America',
+ ],
+ 'read' => ['user:'.$this->getUser()['$id']],
+ 'write' => ['user:'.$this->getUser()['$id']],
+ ]);
+
+ $responseListDocument = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()));
+
+ $responseGetDocument = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents/someID', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()));
+
+ $this->assertEquals($responseCreateDocument['headers']['status-code'], 404);
+ $this->assertEquals($responseListDocument['headers']['status-code'], 404);
+ $this->assertEquals($responseGetDocument['headers']['status-code'], 404);
+ }
+
+ $response = $this->client->call(Client::METHOD_PUT, '/database/collections/' . $data['moviesId'], array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey']
+ ]), [
+ 'name' => 'Movies',
+ 'enabled' => true,
+ 'permission' => 'document',
+ ]);
+
+ $this->assertEquals($response['headers']['status-code'], 200);
+ $this->assertTrue($response['body']['enabled']);
+ }
+
+
/**
* @depends testCreateCollection
*/
@@ -40,7 +104,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'title',
+ 'key' => 'title',
'size' => 256,
'required' => true,
]);
@@ -50,7 +114,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'releaseYear',
+ 'key' => 'releaseYear',
'required' => true,
]);
@@ -59,7 +123,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'actors',
+ 'key' => 'actors',
'size' => 256,
'required' => false,
'array' => true,
@@ -113,8 +177,8 @@ trait DatabaseBase
]), [
'collectionId' => 'unique()',
'name' => 'Response Models',
- 'read' => ['role:all'],
- 'write' => ['role:all'],
+ 'read' => [],
+ 'write' => [],
'permission' => 'document',
]);
@@ -128,7 +192,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'string',
+ 'key' => 'string',
'size' => 16,
'required' => false,
'default' => 'default',
@@ -139,7 +203,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'email',
+ 'key' => 'email',
'required' => false,
'default' => 'default@example.com',
]);
@@ -149,7 +213,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'enum',
+ 'key' => 'enum',
'elements' => ['yes', 'no', 'maybe'],
'required' => false,
'default' => 'maybe',
@@ -160,7 +224,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'ip',
+ 'key' => 'ip',
'required' => false,
'default' => '192.0.2.0',
]);
@@ -170,7 +234,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'url',
+ 'key' => 'url',
'required' => false,
'default' => 'http://example.com',
]);
@@ -180,7 +244,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'integer',
+ 'key' => 'integer',
'required' => false,
'min' => 1,
'max' => 5,
@@ -192,7 +256,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'float',
+ 'key' => 'float',
'required' => false,
'min' => 1.5,
'max' => 5.5,
@@ -204,7 +268,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'boolean',
+ 'key' => 'boolean',
'required' => false,
'default' => true,
]);
@@ -568,7 +632,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'enum',
+ 'key' => 'enum',
'elements' => ['yes', 'no', ''],
'required' => false,
'default' => 'maybe',
@@ -590,7 +654,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'indexId' => 'titleIndex',
+ 'key' => 'titleIndex',
'type' => 'fulltext',
'attributes' => ['title'],
]);
@@ -606,7 +670,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'indexId' => 'releaseYear',
+ 'key' => 'releaseYear',
'type' => 'key',
'attributes' => ['releaseYear'],
]);
@@ -1229,8 +1293,8 @@ trait DatabaseBase
]), [
'collectionId' => 'unique()',
'name' => 'invalidDocumentStructure',
- 'read' => ['role:all'],
- 'write' => ['role:all'],
+ 'read' => [],
+ 'write' => [],
'permission' => 'document',
]);
@@ -1244,7 +1308,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'email',
+ 'key' => 'email',
'required' => false,
]);
@@ -1253,7 +1317,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'enum',
+ 'key' => 'enum',
'elements' => ['yes', 'no', 'maybe'],
'required' => false,
]);
@@ -1263,7 +1327,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'ip',
+ 'key' => 'ip',
'required' => false,
]);
@@ -1272,7 +1336,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'url',
+ 'key' => 'url',
'size' => 256,
'required' => false,
]);
@@ -1282,7 +1346,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'range',
+ 'key' => 'range',
'required' => false,
'min' => 1,
'max' => 10,
@@ -1294,7 +1358,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'floatRange',
+ 'key' => 'floatRange',
'required' => false,
'min' => 1.1,
'max' => 1.4,
@@ -1305,7 +1369,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'probability',
+ 'key' => 'probability',
'required' => false,
'min' => 0,
'max' => 1,
@@ -1316,7 +1380,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'upperBound',
+ 'key' => 'upperBound',
'required' => false,
'max' => 10,
]);
@@ -1326,7 +1390,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'lowerBound',
+ 'key' => 'lowerBound',
'required' => false,
'min' => 5,
]);
@@ -1339,7 +1403,7 @@ trait DatabaseBase
'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'invalidRange',
+ 'key' => 'invalidRange',
'required' => false,
'min' => 4,
'max' => 3,
@@ -1349,12 +1413,42 @@ trait DatabaseBase
'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'defaultArray',
+ 'key' => 'defaultArray',
'required' => false,
'default' => 42,
'array' => true,
]);
+ $defaultRequired = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey']
+ ]), [
+ 'attributeId' => 'defaultRequired',
+ 'required' => true,
+ 'default' => 12
+ ]);
+
+ $enumDefault = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/enum', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey']
+ ]), [
+ 'attributeId' => 'enumDefault',
+ 'elements' => ['north', 'west'],
+ 'default' => 'south'
+ ]);
+
+ $enumDefaultStrict = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/enum', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey']
+ ]), [
+ 'attributeId' => 'enumDefault',
+ 'elements' => ['north', 'west'],
+ 'default' => 'NORTH'
+ ]);
+
$this->assertEquals(201, $email['headers']['status-code']);
$this->assertEquals(201, $ip['headers']['status-code']);
$this->assertEquals(201, $url['headers']['status-code']);
@@ -1363,8 +1457,12 @@ trait DatabaseBase
$this->assertEquals(201, $probability['headers']['status-code']);
$this->assertEquals(201, $upperBound['headers']['status-code']);
$this->assertEquals(201, $lowerBound['headers']['status-code']);
+ $this->assertEquals(201, $enum['headers']['status-code']);
$this->assertEquals(400, $invalidRange['headers']['status-code']);
$this->assertEquals(400, $defaultArray['headers']['status-code']);
+ $this->assertEquals(400, $defaultRequired['headers']['status-code']);
+ $this->assertEquals(400, $enumDefault['headers']['status-code']);
+ $this->assertEquals(400, $enumDefaultStrict['headers']['status-code']);
$this->assertEquals('Minimum value must be lesser than maximum value', $invalidRange['body']['message']);
$this->assertEquals('Cannot set default value for array attributes', $defaultArray['body']['message']);
@@ -1788,7 +1886,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'attribute',
+ 'key' => 'attribute',
'size' => 64,
'required' => true,
]);
@@ -1804,7 +1902,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'indexId' => 'key_attribute',
+ 'key' => 'key_attribute',
'type' => 'key',
'attributes' => [$attribute['body']['key']],
]);
@@ -1829,13 +1927,41 @@ trait DatabaseBase
$this->assertEquals(201, $document1['headers']['status-code']);
+ $document2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'documentId' => 'unique()',
+ 'data' => [
+ 'attribute' => 'one',
+ ],
+ 'read' => [],
+ 'write' => [$user],
+ ]);
+
+ $this->assertEquals(201, $document2['headers']['status-code']);
+
+ $document3 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'documentId' => 'unique()',
+ 'data' => [
+ 'attribute' => 'one',
+ ],
+ 'read' => [],
+ 'write' => [],
+ ]);
+
+ $this->assertEquals(201, $document3['headers']['status-code']);
+
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
- $this->assertEquals(1, $documents['body']['sum']);
- $this->assertCount(1, $documents['body']['documents']);
+ $this->assertEquals(3, $documents['body']['sum']);
+ $this->assertCount(3, $documents['body']['documents']);
/*
* Test for Failure
@@ -1894,7 +2020,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
]));
- $this->assertEquals(404, $documents['headers']['status-code']);
+ $this->assertEquals(401, $documents['headers']['status-code']);
}
/**
@@ -1907,7 +2033,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'indexId' => 'unique_title',
+ 'key' => 'unique_title',
'type' => 'unique',
'attributes' => ['title'],
]);
diff --git a/tests/e2e/Services/Database/DatabaseCustomServerTest.php b/tests/e2e/Services/Database/DatabaseCustomServerTest.php
index 9f8c7130e4..659839debe 100644
--- a/tests/e2e/Services/Database/DatabaseCustomServerTest.php
+++ b/tests/e2e/Services/Database/DatabaseCustomServerTest.php
@@ -165,7 +165,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'firstName',
+ 'key' => 'firstName',
'size' => 256,
'required' => true,
]);
@@ -175,7 +175,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'lastName',
+ 'key' => 'lastName',
'size' => 256,
'required' => true,
]);
@@ -185,7 +185,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'unneeded',
+ 'key' => 'unneeded',
'size' => 256,
'required' => true,
]);
@@ -214,7 +214,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'indexId' => 'key_lastName',
+ 'key' => 'key_lastName',
'type' => 'key',
'attributes' => [
'lastName',
@@ -275,7 +275,7 @@ class DatabaseCustomServerTest extends Scope
return [
'collectionId' => $actors['body']['$id'],
- 'indexId' => $index['body']['key'],
+ 'key' => $index['body']['key'],
];
}
@@ -284,7 +284,7 @@ class DatabaseCustomServerTest extends Scope
*/
public function testDeleteIndex($data): array
{
- $index = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/indexes/'. $data['indexId'], array_merge([
+ $index = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/indexes/'. $data['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
@@ -316,7 +316,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'attribute1',
+ 'key' => 'attribute1',
'size' => 16,
'required' => true,
]);
@@ -326,7 +326,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'attribute2',
+ 'key' => 'attribute2',
'size' => 16,
'required' => true,
]);
@@ -343,7 +343,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'indexId' => 'index1',
+ 'key' => 'index1',
'type' => 'key',
'attributes' => ['attribute1', 'attribute2'],
'orders' => ['ASC', 'ASC'],
@@ -354,7 +354,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'indexId' => 'index2',
+ 'key' => 'index2',
'type' => 'key',
'attributes' => ['attribute2'],
]);
@@ -428,7 +428,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'attribute1',
+ 'key' => 'attribute1',
'size' => 16,
'required' => true,
]);
@@ -438,7 +438,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'attribute2',
+ 'key' => 'attribute2',
'size' => 16,
'required' => true,
]);
@@ -455,7 +455,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'indexId' => 'index1',
+ 'key' => 'index1',
'type' => 'key',
'attributes' => ['attribute1', 'attribute2'],
'orders' => ['ASC', 'ASC'],
@@ -466,7 +466,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'indexId' => 'index2',
+ 'key' => 'index2',
'type' => 'key',
'attributes' => ['attribute2'],
]);
@@ -615,7 +615,7 @@ class DatabaseCustomServerTest extends Scope
// 'x-appwrite-project' => $this->getProject()['$id'],
// 'x-appwrite-key' => $this->getProject()['apiKey']
// ]), [
- // 'attributeId' => "attribute{$i}",
+ // 'key' => "attribute{$i}",
// 'required' => false,
// ]);
@@ -629,7 +629,7 @@ class DatabaseCustomServerTest extends Scope
// 'x-appwrite-project' => $this->getProject()['$id'],
// 'x-appwrite-key' => $this->getProject()['apiKey']
// ]), [
- // 'attributeId' => "tooMany",
+ // 'key' => "tooMany",
// 'required' => false,
// ]);
@@ -663,7 +663,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => "attribute{$i}",
+ 'key' => "attribute{$i}",
'size' => 1024,
'required' => true,
]);
@@ -678,7 +678,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'tooWide',
+ 'key' => 'tooWide',
'size' => 1024,
'required' => true,
]);
@@ -714,7 +714,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => "attribute{$i}",
+ 'key' => "attribute{$i}",
'size' => 64,
'required' => true,
]);
@@ -751,7 +751,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'indexId' => "key_attribute{$i}",
+ 'key' => "key_attribute{$i}",
'type' => 'key',
'attributes' => ["attribute{$i}"],
]);
@@ -780,7 +780,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'indexId' => 'tooMany',
+ 'key' => 'tooMany',
'type' => 'key',
'attributes' => ['attribute61'],
]);
diff --git a/tests/e2e/Services/Database/DatabasePermissionsGuestTest.php b/tests/e2e/Services/Database/DatabasePermissionsGuestTest.php
index 1e6c6b7e18..f5e8ad350e 100644
--- a/tests/e2e/Services/Database/DatabasePermissionsGuestTest.php
+++ b/tests/e2e/Services/Database/DatabasePermissionsGuestTest.php
@@ -26,7 +26,7 @@ class DatabasePermissionsGuestTest extends Scope
$collection = ['id' => $movies['body']['$id']];
$this->client->call(Client::METHOD_POST, '/database/collections/' . $collection['id'] . '/attributes/string', $this->getServerHeader(), [
- 'attributeId' => 'title',
+ 'key' => 'title',
'size' => 256,
'required' => true,
]);
diff --git a/tests/e2e/Services/Database/DatabasePermissionsMemberTest.php b/tests/e2e/Services/Database/DatabasePermissionsMemberTest.php
index e28fb559ac..84c5ee6d6b 100644
--- a/tests/e2e/Services/Database/DatabasePermissionsMemberTest.php
+++ b/tests/e2e/Services/Database/DatabasePermissionsMemberTest.php
@@ -65,7 +65,7 @@ class DatabasePermissionsMemberTest extends Scope
$this->collections = ['public' => $public['body']['$id']];
$response = $this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['public'] . '/attributes/string', $this->getServerHeader(), [
- 'attributeId' => 'title',
+ 'key' => 'title',
'size' => 256,
'required' => true,
]);
@@ -83,7 +83,7 @@ class DatabasePermissionsMemberTest extends Scope
$this->collections['private'] = $private['body']['$id'];
$this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['private'] . '/attributes/string', $this->getServerHeader(), [
- 'attributeId' => 'title',
+ 'key' => 'title',
'size' => 256,
'required' => true,
]);
diff --git a/tests/e2e/Services/Database/DatabasePermissionsTeamTest.php b/tests/e2e/Services/Database/DatabasePermissionsTeamTest.php
index 218c9cd78a..75b4d52d0b 100644
--- a/tests/e2e/Services/Database/DatabasePermissionsTeamTest.php
+++ b/tests/e2e/Services/Database/DatabasePermissionsTeamTest.php
@@ -45,7 +45,7 @@ class DatabasePermissionsTeamTest extends Scope
$this->collections['collection1'] = $collection1['body']['$id'];
$this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['collection1'] . '/attributes/string', $this->getServerHeader(), [
- 'attributeId' => 'title',
+ 'key' => 'title',
'size' => 256,
'required' => true,
]);
@@ -61,7 +61,7 @@ class DatabasePermissionsTeamTest extends Scope
$this->collections['collection2'] = $collection2['body']['$id'];
$this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['collection2'] . '/attributes/string', $this->getServerHeader(), [
- 'attributeId' => 'title',
+ 'key' => 'title',
'size' => 256,
'required' => true,
]);
@@ -160,7 +160,7 @@ class DatabasePermissionsTeamTest extends Scope
if ($success) {
$this->assertCount(1, $documents['body']['documents']);
} else {
- $this->assertEquals(404, $documents['headers']['status-code']);
+ $this->assertEquals(401, $documents['headers']['status-code']);
}
}
diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
index a206b477df..b28e0ff223 100644
--- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
+++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
@@ -830,4 +830,27 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals($executions['body']['executions'][0]['trigger'], 'http');
$this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']);
}
+
+ public function testGetRuntimes()
+ {
+
+ $runtimes = $this->client->call(Client::METHOD_GET, '/functions/runtimes', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()));
+
+ $this->assertEquals(200, $runtimes['headers']['status-code']);
+ $this->assertGreaterThan(0, $runtimes['body']['sum']);
+
+ $runtime = $runtimes['body']['runtimes'][0];
+
+ $this->assertArrayHasKey('$id', $runtime);
+ $this->assertArrayHasKey('name', $runtime);
+ $this->assertArrayHasKey('version', $runtime);
+ $this->assertArrayHasKey('logo', $runtime);
+ $this->assertArrayHasKey('image', $runtime);
+ $this->assertArrayHasKey('base', $runtime);
+ $this->assertArrayHasKey('supports', $runtime);
+
+ }
}
diff --git a/tests/e2e/Services/Realtime/RealtimeBase.php b/tests/e2e/Services/Realtime/RealtimeBase.php
index cd359f5d6c..1f734333d8 100644
--- a/tests/e2e/Services/Realtime/RealtimeBase.php
+++ b/tests/e2e/Services/Realtime/RealtimeBase.php
@@ -2,24 +2,26 @@
namespace Tests\E2E\Services\Realtime;
-use CURLFile;
-use Tests\E2E\Client;
-use WebSocket\Client as WebSocketClient;
use WebSocket\ConnectionException;
+use WebSocket\Client as WebSocketClient;
trait RealtimeBase
{
-
- private function getWebsocket($channels = [], $headers = [])
+ private function getWebsocket($channels = [], $headers = [], $projectId = null)
{
+ if (is_null($projectId)) {
+ $projectId = $this->getProject()['$id'];
+ }
+
$headers = array_merge([
'Origin' => 'appwrite.test'
], $headers);
$query = [
- 'project' => $this->getProject()['$id'],
+ 'project' => $projectId,
'channels' => $channels
];
+
return new WebSocketClient('ws://appwrite-traefik/v1/realtime?' . http_build_query($query), [
'headers' => $headers,
'timeout' => 30,
@@ -38,17 +40,6 @@ trait RealtimeBase
/**
* Test for FAILURE
*/
- $client = $this->getWebsocket(['documents'], ['origin' => 'http://appwrite.unknown']);
- $payload = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $payload);
- $this->assertArrayHasKey('data', $payload);
- $this->assertEquals('error', $payload['type']);
- $this->assertEquals(1008, $payload['data']['code']);
- $this->assertEquals('Invalid Origin. Register your new client (appwrite.unknown) as a new Web platform on your project console dashboard', $payload['data']['message']);
- $this->expectException(ConnectionException::class); // Check if server disconnnected client
- $client->close();
-
$client = $this->getWebsocket();
$payload = json_decode($client->receive(), true);
@@ -90,1026 +81,4 @@ trait RealtimeBase
$this->expectException(ConnectionException::class); // Check if server disconnnected client
$client->close();
}
-
- public function testChannelParsing()
- {
- $user = $this->getUser();
- $userId = $user['$id'] ?? '';
- $session = $user['session'] ?? '';
- $headers = [
- 'origin' => 'http://localhost',
- 'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session
- ];
-
- $client = $this->getWebsocket(['documents'], $headers);
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('connected', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertNotEmpty($response['data']['user']);
- $this->assertCount(1, $response['data']['channels']);
- $this->assertContains('documents', $response['data']['channels']);
- $this->assertEquals($userId, $response['data']['user']['$id']);
-
- $client->close();
-
- $client = $this->getWebsocket(['account'], $headers);
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('connected', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertNotEmpty($response['data']['user']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertContains('account', $response['data']['channels']);
- $this->assertContains('account.' . $userId, $response['data']['channels']);
- $this->assertEquals($userId, $response['data']['user']['$id']);
-
- $client->close();
-
- $client = $this->getWebsocket(['account', 'documents', 'account.123'], $headers);
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('connected', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertNotEmpty($response['data']['user']);
- $this->assertCount(3, $response['data']['channels']);
- $this->assertContains('documents', $response['data']['channels']);
- $this->assertContains('account', $response['data']['channels']);
- $this->assertContains('account.' . $userId, $response['data']['channels']);
- $this->assertEquals($userId, $response['data']['user']['$id']);
-
- $client->close();
-
- $client = $this->getWebsocket([
- 'account',
- 'files',
- 'files.1',
- 'collections',
- 'collections.1',
- 'collections.1.documents',
- 'collections.2',
- 'collections.2.documents',
- 'documents',
- 'documents.1',
- 'documents.2',
- ], $headers);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('connected', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertNotEmpty($response['data']['user']);
- $this->assertCount(12, $response['data']['channels']);
- $this->assertContains('account', $response['data']['channels']);
- $this->assertContains('account.' . $userId, $response['data']['channels']);
- $this->assertContains('files', $response['data']['channels']);
- $this->assertContains('files.1', $response['data']['channels']);
- $this->assertContains('collections', $response['data']['channels']);
- $this->assertContains('collections.1', $response['data']['channels']);
- $this->assertContains('collections.1.documents', $response['data']['channels']);
- $this->assertContains('collections.2', $response['data']['channels']);
- $this->assertContains('collections.2.documents', $response['data']['channels']);
- $this->assertContains('documents', $response['data']['channels']);
- $this->assertContains('documents.1', $response['data']['channels']);
- $this->assertContains('documents.2', $response['data']['channels']);
- $this->assertEquals($userId, $response['data']['user']['$id']);
-
- $client->close();
- }
-
- public function testManualAuthentication()
- {
- $user = $this->getUser();
- $userId = $user['$id'] ?? '';
- $session = $user['session'] ?? '';
-
- /**
- * Test for SUCCESS
- */
- $client = $this->getWebsocket(['account'], [
- 'origin' => 'http://localhost'
- ]);
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('connected', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(1, $response['data']['channels']);
- $this->assertContains('account', $response['data']['channels']);
-
- $client->send(\json_encode([
- 'type' => 'authentication',
- 'data' => [
- 'session' => $session
- ]
- ]));
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('response', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertEquals('authentication', $response['data']['to']);
- $this->assertTrue($response['data']['success']);
- $this->assertNotEmpty($response['data']['user']);
- $this->assertEquals($userId, $response['data']['user']['$id']);
-
- /**
- * Test for FAILURE
- */
- $client->send(\json_encode([
- 'type' => 'authentication',
- 'data' => [
- 'session' => 'invalid_session'
- ]
- ]));
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('error', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertEquals(1003, $response['data']['code']);
- $this->assertEquals('Session is not valid.', $response['data']['message']);
-
- $client->send(\json_encode([
- 'type' => 'authentication',
- 'data' => []
- ]));
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('error', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertEquals(1003, $response['data']['code']);
- $this->assertEquals('Payload is not valid.', $response['data']['message']);
-
- $client->send(\json_encode([
- 'type' => 'unknown',
- 'data' => [
- 'session' => 'invalid_session'
- ]
- ]));
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('error', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertEquals(1003, $response['data']['code']);
- $this->assertEquals('Message type is not valid.', $response['data']['message']);
-
- $client->send(\json_encode([
- 'test' => '123',
- ]));
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('error', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertEquals(1003, $response['data']['code']);
- $this->assertEquals('Message format is not valid.', $response['data']['message']);
-
-
- $client->close();
- }
-
- public function testChannelAccount()
- {
- $user = $this->getUser();
- $userId = $user['$id'] ?? '';
- $session = $user['session'] ?? '';
- $projectId = $this->getProject()['$id'];
-
- $client = $this->getWebsocket(['account'], [
- 'origin' => 'http://localhost',
- 'cookie' => 'a_session_'.$projectId.'=' . $session
- ]);
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('connected', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertContains('account', $response['data']['channels']);
- $this->assertContains('account.' . $userId, $response['data']['channels']);
- $this->assertNotEmpty($response['data']['user']);
- $this->assertEquals($userId, $response['data']['user']['$id']);
-
- /**
- * Test Account Name Event
- */
- $name = "Torsten Dittmann";
-
- $this->client->call(Client::METHOD_PATCH, '/account/name', array_merge([
- 'origin' => 'http://localhost',
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $projectId,
- 'cookie' => 'a_session_' . $projectId . '=' . $session,
- ]), [
- 'name' => $name
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertContains('account', $response['data']['channels']);
- $this->assertContains('account.' . $userId, $response['data']['channels']);
- $this->assertEquals('account.update.name', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- $this->assertEquals($name, $response['data']['payload']['name']);
-
-
- /**
- * Test Account Password Event
- */
- $this->client->call(Client::METHOD_PATCH, '/account/password', array_merge([
- 'origin' => 'http://localhost',
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $projectId,
- 'cookie' => 'a_session_'.$projectId.'=' . $session,
- ]), [
- 'password' => 'new-password',
- 'oldPassword' => 'password',
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertContains('account', $response['data']['channels']);
- $this->assertContains('account.' . $userId, $response['data']['channels']);
- $this->assertEquals('account.update.password', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- $this->assertEquals($name, $response['data']['payload']['name']);
-
- /**
- * Test Account Email Update
- */
- $this->client->call(Client::METHOD_PATCH, '/account/email', array_merge([
- 'origin' => 'http://localhost',
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $projectId,
- 'cookie' => 'a_session_'.$projectId.'=' . $session,
- ]), [
- 'email' => 'torsten@appwrite.io',
- 'password' => 'new-password',
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertContains('account', $response['data']['channels']);
- $this->assertContains('account.' . $userId, $response['data']['channels']);
- $this->assertEquals('account.update.email', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- $this->assertEquals('torsten@appwrite.io', $response['data']['payload']['email']);
-
- /**
- * Test Account Verification Create
- */
- $this->client->call(Client::METHOD_POST, '/account/verification', array_merge([
- 'origin' => 'http://localhost',
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $projectId,
- 'cookie' => 'a_session_'.$projectId.'=' . $session,
- ]), [
- 'url' => 'http://localhost/verification',
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertContains('account', $response['data']['channels']);
- $this->assertContains('account.' . $userId, $response['data']['channels']);
- $this->assertEquals('account.verification.create', $response['data']['event']);
-
- $lastEmail = $this->getLastEmail();
- $verification = substr($lastEmail['text'], strpos($lastEmail['text'], '&secret=', 0) + 8, 256);
-
- /**
- * Test Account Verification Complete
- */
- $response = $this->client->call(Client::METHOD_PUT, '/account/verification', array_merge([
- 'origin' => 'http://localhost',
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $projectId,
- 'cookie' => 'a_session_'.$projectId.'=' . $session,
- ]), [
- 'userId' => $userId,
- 'secret' => $verification,
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertContains('account', $response['data']['channels']);
- $this->assertContains('account.' . $userId, $response['data']['channels']);
- $this->assertEquals('account.verification.update', $response['data']['event']);
-
- /**
- * Test Acoount Prefs Update
- */
- $this->client->call(Client::METHOD_PATCH, '/account/prefs', array_merge([
- 'origin' => 'http://localhost',
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $projectId,
- 'cookie' => 'a_session_'.$projectId.'=' . $session,
- ]), [
- 'prefs' => [
- 'prefKey1' => 'prefValue1',
- 'prefKey2' => 'prefValue2',
- ]
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertContains('account', $response['data']['channels']);
- $this->assertContains('account.' . $userId, $response['data']['channels']);
- $this->assertEquals('account.update.prefs', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- /**
- * Test Account Session Create
- */
- $response = $this->client->call(Client::METHOD_POST, '/account/sessions', array_merge([
- 'origin' => 'http://localhost',
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $projectId,
- ]), [
- 'email' => 'torsten@appwrite.io',
- 'password' => 'new-password',
- ]);
-
- $sessionNew = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$projectId];
- $sessionNewId = $response['body']['$id'];
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertContains('account', $response['data']['channels']);
- $this->assertContains('account.' . $userId, $response['data']['channels']);
- $this->assertEquals('account.sessions.create', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- /**
- * Test Account Session Delete
- */
- $this->client->call(Client::METHOD_DELETE, '/account/sessions/'.$sessionNewId, array_merge([
- 'origin' => 'http://localhost',
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $projectId,
- 'cookie' => 'a_session_'.$projectId.'=' . $sessionNew,
- ]));
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertContains('account', $response['data']['channels']);
- $this->assertContains('account.' . $userId, $response['data']['channels']);
- $this->assertEquals('account.sessions.delete', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- /**
- * Test Account Create Recovery
- */
- $this->client->call(Client::METHOD_POST, '/account/recovery', array_merge([
- 'origin' => 'http://localhost',
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $projectId,
- ]), [
- 'email' => 'torsten@appwrite.io',
- 'url' => 'http://localhost/recovery',
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $lastEmail = $this->getLastEmail();
- $recovery = substr($lastEmail['text'], strpos($lastEmail['text'], '&secret=', 0) + 8, 256);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertContains('account', $response['data']['channels']);
- $this->assertContains('account.' . $userId, $response['data']['channels']);
- $this->assertEquals('account.recovery.create', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- $response = $this->client->call(Client::METHOD_PUT, '/account/recovery', array_merge([
- 'origin' => 'http://localhost',
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $projectId,
- ]), [
- 'userId' => $userId,
- 'secret' => $recovery,
- 'password' => 'test-recovery',
- 'passwordAgain' => 'test-recovery',
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertContains('account', $response['data']['channels']);
- $this->assertContains('account.' . $userId, $response['data']['channels']);
- $this->assertEquals('account.recovery.update', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- $client->close();
- }
-
- public function testChannelDatabase()
- {
- $user = $this->getUser();
- $session = $user['session'] ?? '';
- $projectId = $this->getProject()['$id'];
-
- $client = $this->getWebsocket(['documents', 'collections'], [
- 'origin' => 'http://localhost',
- 'cookie' => 'a_session_'.$projectId.'=' . $session
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('connected', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertContains('documents', $response['data']['channels']);
- $this->assertContains('collections', $response['data']['channels']);
- $this->assertNotEmpty($response['data']['user']);
- $this->assertEquals($user['$id'], $response['data']['user']['$id']);
-
- /**
- * Test Collection Create
- */
- $actors = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- 'x-appwrite-key' => $this->getProject()['apiKey']
- ]), [
- 'collectionId' => 'unique()',
- 'name' => 'Actors',
- 'read' => ['role:all'],
- 'write' => ['role:all'],
- 'permission' => 'collection'
- ]);
-
- $data = ['actorsId' => $actors['body']['$id']];
-
- $name = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/attributes/string', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- 'x-appwrite-key' => $this->getProject()['apiKey']
- ]), [
- 'attributeId' => 'name',
- 'size' => 256,
- 'required' => true,
- ]);
-
- $this->assertEquals($name['headers']['status-code'], 201);
- $this->assertEquals($name['body']['key'], 'name');
- $this->assertEquals($name['body']['type'], 'string');
- $this->assertEquals($name['body']['size'], 256);
- $this->assertEquals($name['body']['required'], true);
-
- sleep(2);
-
- /**
- * Test Document Create
- */
- $document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/documents', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'documentId' => 'unique()',
- 'data' => [
- 'name' => 'Chris Evans'
- ],
- 'read' => ['role:all'],
- 'write' => ['role:all'],
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertCount(3, $response['data']['channels']);
- $this->assertContains('documents', $response['data']['channels']);
- $this->assertContains('documents.' . $document['body']['$id'], $response['data']['channels']);
- $this->assertContains('collections.' . $actors['body']['$id'] . '.documents', $response['data']['channels']);
- $this->assertEquals('database.documents.create', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
- $this->assertEquals($response['data']['payload']['name'], 'Chris Evans');
-
- $data['documentId'] = $document['body']['$id'];
-
- /**
- * Test Document Update
- */
- $document = $this->client->call(Client::METHOD_PATCH, '/database/collections/' . $data['actorsId'] . '/documents/' . $data['documentId'], array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'documentId' => 'unique()',
- 'data' => [
- 'name' => 'Chris Evans 2'
- ],
- 'read' => ['role:all'],
- 'write' => ['role:all'],
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertCount(3, $response['data']['channels']);
- $this->assertContains('documents', $response['data']['channels']);
- $this->assertContains('documents.' . $data['documentId'], $response['data']['channels']);
- $this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
- $this->assertEquals('database.documents.update', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- $this->assertEquals($response['data']['payload']['name'], 'Chris Evans 2');
-
-
- /**
- * Test Document Delete
- */
- $document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/documents', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'documentId' => 'unique()',
- 'data' => [
- 'name' => 'Bradley Cooper'
- ],
- 'read' => ['role:all'],
- 'write' => ['role:all'],
- ]);
-
- $client->receive();
-
- $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['actorsId'] . '/documents/' . $document['body']['$id'], array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()));
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertCount(3, $response['data']['channels']);
- $this->assertContains('documents', $response['data']['channels']);
- $this->assertContains('documents.' . $document['body']['$id'], $response['data']['channels']);
- $this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
- $this->assertEquals('database.documents.delete', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
- $this->assertEquals($response['data']['payload']['name'], 'Bradley Cooper');
-
- $client->close();
- }
-
- public function testChannelFiles()
- {
- $user = $this->getUser();
- $session = $user['session'] ?? '';
- $projectId = $this->getProject()['$id'];
-
- $client = $this->getWebsocket(['files'], [
- 'origin' => 'http://localhost',
- 'cookie' => 'a_session_'.$projectId.'=' . $session
- ]);
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('connected', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(1, $response['data']['channels']);
- $this->assertContains('files', $response['data']['channels']);
- $this->assertNotEmpty($response['data']['user']);
- $this->assertEquals($user['$id'], $response['data']['user']['$id']);
-
- /**
- * Test File Create
- */
- $bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- 'x-appwrite-key' => $this->getProject()['apiKey'],
- ], $this->getHeaders()), [
- 'bucketId' => 'unique()',
- 'name' => 'Test Bucket',
- 'permission' => 'file',
- 'read' => ['role:all'],
- 'write' => ['role:all'],
- ]);
- $this->assertEquals(201, $bucket['headers']['status-code']);
- $this->assertNotEmpty($bucket['body']['$id']);
-
- $bucketId = $bucket['body']['$id'];
-
- $file = $this->client->call(Client::METHOD_POST, '/storage/buckets/'.$bucketId.'/files', array_merge([
- 'content-type' => 'multipart/form-data',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'fileId' => 'unique()',
- 'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
- 'read' => ['role:all'],
- 'write' => ['role:all'],
- 'folderId' => 'xyz',
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertCount(3, $response['data']['channels']);
- $this->assertContains('files', $response['data']['channels']);
- $this->assertContains('files.' . $file['body']['$id'], $response['data']['channels']);
- $this->assertContains('buckets.' . $bucketId . '.files', $response['data']['channels']);
- $this->assertEquals('storage.files.create', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- $data = ['fileId' => $file['body']['$id']];
-
- /**
- * Test File Update
- */
- $this->client->call(Client::METHOD_PUT, '/storage/buckets/'.$bucketId.'/files/' . $data['fileId'], array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'read' => ['role:all'],
- 'write' => ['role:all'],
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertCount(3, $response['data']['channels']);
- $this->assertContains('files', $response['data']['channels']);
- $this->assertContains('buckets.'.$bucketId.'.files', $response['data']['channels']);
- $this->assertContains('files.' . $file['body']['$id'], $response['data']['channels']);
- $this->assertEquals('storage.files.update', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- /**
- * Test File Delete
- */
- $this->client->call(Client::METHOD_DELETE, '/storage/buckets/'. $bucketId . '/files/' . $data['fileId'], array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()));
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertCount(3, $response['data']['channels']);
- $this->assertContains('files', $response['data']['channels']);
- $this->assertContains('buckets.'.$bucketId.'.files', $response['data']['channels']);
- $this->assertContains('files.' . $file['body']['$id'], $response['data']['channels']);
- $this->assertEquals('storage.files.delete', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- $client->close();
- }
-
- public function testChannelExecutions()
- {
- $user = $this->getUser();
- $session = $user['session'] ?? '';
- $projectId = $this->getProject()['$id'];
-
- $client = $this->getWebsocket(['executions'], [
- 'origin' => 'http://localhost',
- 'cookie' => 'a_session_'.$projectId.'=' . $session
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('connected', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(1, $response['data']['channels']);
- $this->assertContains('executions', $response['data']['channels']);
- $this->assertNotEmpty($response['data']['user']);
- $this->assertEquals($user['$id'], $response['data']['user']['$id']);
-
- /**
- * Test Functions Create
- */
- $function = $this->client->call(Client::METHOD_POST, '/functions', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- 'x-appwrite-key' => $this->getProject()['apiKey']
- ], [
- 'functionId' => 'unique()',
- 'name' => 'Test',
- 'execute' => ['role:member'],
- 'runtime' => 'php-8.0',
- 'timeout' => 10,
- ]);
-
- $functionId = $function['body']['$id'] ?? '';
-
- $this->assertEquals($function['headers']['status-code'], 201);
- $this->assertNotEmpty($function['body']['$id']);
-
- $tag = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/tags', array_merge([
- 'content-type' => 'multipart/form-data',
- 'x-appwrite-project' => $this->getProject()['$id'],
- 'x-appwrite-key' => $this->getProject()['apiKey']
- ]), [
- 'command' => 'php index.php',
- 'code' => new CURLFile(realpath(__DIR__ . '/../../../resources/functions/timeout.tar.gz'), 'application/x-gzip', 'php-fx.tar.gz'),
- ]);
-
- $tagId = $tag['body']['$id'] ?? '';
-
- $this->assertEquals($tag['headers']['status-code'], 201);
- $this->assertNotEmpty($tag['body']['$id']);
-
- $response = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/tag', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- 'x-appwrite-key' => $this->getProject()['apiKey']
- ]), [
- 'tag' => $tagId,
- ]);
-
- $this->assertEquals($response['headers']['status-code'], 200);
- $this->assertNotEmpty($response['body']['$id']);
-
- $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id']
- ], $this->getHeaders()), []);
-
- $this->assertEquals($execution['headers']['status-code'], 201);
- $this->assertNotEmpty($execution['body']['$id']);
-
- $response = json_decode($client->receive(), true);
- $responseUpdate = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertCount(3, $response['data']['channels']);
- $this->assertContains('executions', $response['data']['channels']);
- $this->assertContains('executions.' . $execution['body']['$id'], $response['data']['channels']);
- $this->assertContains('functions.' . $execution['body']['functionId'], $response['data']['channels']);
- $this->assertEquals('functions.executions.create', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- $this->assertArrayHasKey('type', $responseUpdate);
- $this->assertArrayHasKey('data', $responseUpdate);
- $this->assertEquals('event', $responseUpdate['type']);
- $this->assertNotEmpty($responseUpdate['data']);
- $this->assertArrayHasKey('timestamp', $responseUpdate['data']);
- $this->assertCount(3, $responseUpdate['data']['channels']);
- $this->assertContains('executions', $responseUpdate['data']['channels']);
- $this->assertContains('executions.' . $execution['body']['$id'], $responseUpdate['data']['channels']);
- $this->assertContains('functions.' . $execution['body']['functionId'], $responseUpdate['data']['channels']);
- $this->assertEquals('functions.executions.update', $responseUpdate['data']['event']);
- $this->assertNotEmpty($responseUpdate['data']['payload']);
-
- $client->close();
- }
-
- public function testChannelTeams(): array
- {
- $user = $this->getUser();
- $session = $user['session'] ?? '';
- $projectId = $this->getProject()['$id'];
-
- $client = $this->getWebsocket(['teams'], [
- 'origin' => 'http://localhost',
- 'cookie' => 'a_session_'.$projectId.'=' . $session
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('connected', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(1, $response['data']['channels']);
- $this->assertContains('teams', $response['data']['channels']);
- $this->assertNotEmpty($response['data']['user']);
- $this->assertEquals($user['$id'], $response['data']['user']['$id']);
-
- /**
- * Test Team Create
- */
- $team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $projectId,
- ], $this->getHeaders()), [
- 'teamId' => 'unique()',
- 'name' => 'Arsenal'
- ]);
-
- $teamId = $team['body']['$id'] ?? '';
-
- $this->assertEquals(201, $team['headers']['status-code']);
- $this->assertNotEmpty($team['body']['$id']);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertContains('teams', $response['data']['channels']);
- $this->assertContains('teams.' . $teamId, $response['data']['channels']);
- $this->assertEquals('teams.create', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- /**
- * Test Team Update
- */
- $team = $this->client->call(Client::METHOD_PUT, '/teams/'.$teamId, array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $projectId,
- ], $this->getHeaders()), [
- 'name' => 'Manchester'
- ]);
-
- $this->assertEquals($team['headers']['status-code'], 200);
- $this->assertNotEmpty($team['body']['$id']);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertContains('teams', $response['data']['channels']);
- $this->assertContains('teams.' . $teamId, $response['data']['channels']);
- $this->assertEquals('teams.update', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- $client->close();
-
- return ['teamId' => $teamId];
- }
-
- /**
- * @depends testChannelTeams
- */
- public function testChannelMemberships(array $data)
- {
- $teamId = $data['teamId'] ?? '';
-
- $user = $this->getUser();
- $session = $user['session'] ?? '';
- $projectId = $this->getProject()['$id'];
-
- $client = $this->getWebsocket(['memberships'], [
- 'origin' => 'http://localhost',
- 'cookie' => 'a_session_'.$projectId.'='.$session
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('connected', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertCount(1, $response['data']['channels']);
- $this->assertContains('memberships', $response['data']['channels']);
- $this->assertNotEmpty($response['data']['user']);
- $this->assertEquals($user['$id'], $response['data']['user']['$id']);
-
- $response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamId.'/memberships', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()));
-
- $membershipId = $response['body']['memberships'][0]['$id'];
-
- /**
- * Test Update Membership
- */
- $roles = ['admin', 'editor', 'uncle'];
- $this->client->call(Client::METHOD_PATCH, '/teams/'.$teamId.'/memberships/'.$membershipId, array_merge([
- 'origin' => 'http://localhost',
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'roles' => $roles
- ]);
-
- $response = json_decode($client->receive(), true);
-
- $this->assertArrayHasKey('type', $response);
- $this->assertArrayHasKey('data', $response);
- $this->assertEquals('event', $response['type']);
- $this->assertNotEmpty($response['data']);
- $this->assertArrayHasKey('timestamp', $response['data']);
- $this->assertCount(2, $response['data']['channels']);
- $this->assertContains('memberships', $response['data']['channels']);
- $this->assertContains('memberships.' . $membershipId, $response['data']['channels']);
- $this->assertEquals('teams.memberships.update', $response['data']['event']);
- $this->assertNotEmpty($response['data']['payload']);
-
- $client->close();
- }
}
diff --git a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php
new file mode 100644
index 0000000000..0b19665bf8
--- /dev/null
+++ b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php
@@ -0,0 +1,276 @@
+getUser();
+ $session = $user['session'] ?? '';
+ $projectId = 'console';
+
+ $client = $this->getWebsocket(['console'], [
+ 'origin' => 'http://localhost',
+ 'cookie' => 'a_session_console='. $this->getRoot()['session'],
+ ], $projectId);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('console', $response['data']['channels']);
+ $this->assertNotEmpty($response['data']['user']);
+
+ /**
+ * Test Attributes
+ */
+ $actors = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'collectionId' => 'unique()',
+ 'name' => 'Actors',
+ 'read' => ['role:all'],
+ 'write' => ['role:all'],
+ 'permission' => 'collection'
+ ]);
+
+ $data = ['actorsId' => $actors['body']['$id']];
+
+ $name = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/attributes/string', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'key' => 'name',
+ 'size' => 256,
+ 'required' => true,
+ ]);
+
+ $this->assertEquals($name['headers']['status-code'], 201);
+ $this->assertEquals($name['body']['key'], 'name');
+ $this->assertEquals($name['body']['type'], 'string');
+ $this->assertEquals($name['body']['size'], 256);
+ $this->assertEquals($name['body']['required'], true);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('console', $response['data']['channels']);
+ $this->assertEquals('database.attributes.create', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+ $this->assertEquals('processing', $response['data']['payload']['status']);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('console', $response['data']['channels']);
+ $this->assertEquals('database.attributes.update', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+ $this->assertEquals('available', $response['data']['payload']['status']);
+
+ $client->close();
+
+ return $data;
+ }
+
+ /**
+ * @depends testAttributes
+ */
+ public function testIndexes(array $data)
+ {
+ $user = $this->getUser();
+ $session = $user['session'] ?? '';
+ $projectId = 'console';
+
+ $client = $this->getWebsocket(['console'], [
+ 'origin' => 'http://localhost',
+ 'cookie' => 'a_session_console='. $this->getRoot()['session'],
+ ], $projectId);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('console', $response['data']['channels']);
+ $this->assertNotEmpty($response['data']['user']);
+
+ /**
+ * Test Indexes
+ */
+ $index = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/indexes', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'key' => 'key_name',
+ 'type' => 'key',
+ 'attributes' => [
+ 'name',
+ ],
+ ]);
+
+ $this->assertEquals($index['headers']['status-code'], 201);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('console', $response['data']['channels']);
+ $this->assertEquals('database.indexes.create', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+ $this->assertEquals('processing', $response['data']['payload']['status']);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('console', $response['data']['channels']);
+ $this->assertEquals('database.indexes.update', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+ $this->assertEquals('available', $response['data']['payload']['status']);
+
+ $client->close();
+
+ return $data;
+ }
+
+ /**
+ * @depends testIndexes
+ */
+ public function testDeleteIndex(array $data)
+ {
+ $user = $this->getUser();
+ $session = $user['session'] ?? '';
+ $projectId = 'console';
+
+ $client = $this->getWebsocket(['console'], [
+ 'origin' => 'http://localhost',
+ 'cookie' => 'a_session_console='. $this->getRoot()['session'],
+ ], $projectId);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('console', $response['data']['channels']);
+ $this->assertNotEmpty($response['data']['user']);
+
+ /**
+ * Test Delete Index
+ */
+ $attribute = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['actorsId'] . '/indexes/key_name', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()));
+
+ $this->assertEquals($attribute['headers']['status-code'], 204);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('console', $response['data']['channels']);
+ $this->assertEquals('database.indexes.delete', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ $client->close();
+
+ return $data;
+ }
+
+ /**
+ * @depends testDeleteIndex
+ */
+ public function testDeleteAttribute(array $data)
+ {
+ $user = $this->getUser();
+ $session = $user['session'] ?? '';
+ $projectId = 'console';
+
+ $client = $this->getWebsocket(['console'], [
+ 'origin' => 'http://localhost',
+ 'cookie' => 'a_session_console='. $this->getRoot()['session'],
+ ], $projectId);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('console', $response['data']['channels']);
+ $this->assertNotEmpty($response['data']['user']);
+
+ /**
+ * Test Delete Attribute
+ */
+ $attribute = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['actorsId'] . '/attributes/name', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()));
+
+ $this->assertEquals($attribute['headers']['status-code'], 204);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('console', $response['data']['channels']);
+ $this->assertEquals('database.attributes.delete', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ $client->close();
+ }
+}
\ No newline at end of file
diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php
index 8822c493b2..48593e1532 100644
--- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php
+++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php
@@ -2,9 +2,12 @@
namespace Tests\E2E\Services\Realtime;
+use CURLFile;
+use Tests\E2E\Client;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\SideClient;
+use WebSocket\ConnectionException;
class RealtimeCustomClientTest extends Scope
@@ -12,4 +15,1211 @@ class RealtimeCustomClientTest extends Scope
use RealtimeBase;
use ProjectCustom;
use SideClient;
+
+ public function testChannelParsing()
+ {
+ $user = $this->getUser();
+ $userId = $user['$id'] ?? '';
+ $session = $user['session'] ?? '';
+
+ $headers = [
+ 'origin' => 'http://localhost',
+ 'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session
+ ];
+
+ $client = $this->getWebsocket(['documents'], $headers);
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertNotEmpty($response['data']['user']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('documents', $response['data']['channels']);
+ $this->assertEquals($userId, $response['data']['user']['$id']);
+
+ $client->close();
+
+ $client = $this->getWebsocket(['account'], $headers);
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertNotEmpty($response['data']['user']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertContains('account', $response['data']['channels']);
+ $this->assertContains('account.' . $userId, $response['data']['channels']);
+ $this->assertEquals($userId, $response['data']['user']['$id']);
+
+ $client->close();
+
+ $client = $this->getWebsocket(['account', 'documents', 'account.123'], $headers);
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertNotEmpty($response['data']['user']);
+ $this->assertCount(3, $response['data']['channels']);
+ $this->assertContains('documents', $response['data']['channels']);
+ $this->assertContains('account', $response['data']['channels']);
+ $this->assertContains('account.' . $userId, $response['data']['channels']);
+ $this->assertEquals($userId, $response['data']['user']['$id']);
+
+ $client->close();
+
+ $client = $this->getWebsocket([
+ 'account',
+ 'files',
+ 'files.1',
+ 'collections',
+ 'collections.1.documents',
+ 'collections.2.documents',
+ 'documents',
+ 'documents.1',
+ 'documents.2',
+ ], $headers);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertNotEmpty($response['data']['user']);
+ $this->assertCount(10, $response['data']['channels']);
+ $this->assertContains('account', $response['data']['channels']);
+ $this->assertContains('account.' . $userId, $response['data']['channels']);
+ $this->assertContains('files', $response['data']['channels']);
+ $this->assertContains('files.1', $response['data']['channels']);
+ $this->assertContains('collections', $response['data']['channels']);
+ $this->assertContains('collections.1.documents', $response['data']['channels']);
+ $this->assertContains('collections.2.documents', $response['data']['channels']);
+ $this->assertContains('documents', $response['data']['channels']);
+ $this->assertContains('documents.1', $response['data']['channels']);
+ $this->assertContains('documents.2', $response['data']['channels']);
+ $this->assertEquals($userId, $response['data']['user']['$id']);
+
+ $client->close();
+ }
+
+ public function testManualAuthentication()
+ {
+ $user = $this->getUser();
+ $userId = $user['$id'] ?? '';
+ $session = $user['session'] ?? '';
+
+ /**
+ * Test for SUCCESS
+ */
+ $client = $this->getWebsocket(['account'], [
+ 'origin' => 'http://localhost'
+ ]);
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('account', $response['data']['channels']);
+
+ $client->send(\json_encode([
+ 'type' => 'authentication',
+ 'data' => [
+ 'session' => $session
+ ]
+ ]));
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('response', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertEquals('authentication', $response['data']['to']);
+ $this->assertTrue($response['data']['success']);
+ $this->assertNotEmpty($response['data']['user']);
+ $this->assertEquals($userId, $response['data']['user']['$id']);
+
+ /**
+ * Test for FAILURE
+ */
+ $client->send(\json_encode([
+ 'type' => 'authentication',
+ 'data' => [
+ 'session' => 'invalid_session'
+ ]
+ ]));
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('error', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertEquals(1003, $response['data']['code']);
+ $this->assertEquals('Session is not valid.', $response['data']['message']);
+
+ $client->send(\json_encode([
+ 'type' => 'authentication',
+ 'data' => []
+ ]));
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('error', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertEquals(1003, $response['data']['code']);
+ $this->assertEquals('Payload is not valid.', $response['data']['message']);
+
+ $client->send(\json_encode([
+ 'type' => 'unknown',
+ 'data' => [
+ 'session' => 'invalid_session'
+ ]
+ ]));
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('error', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertEquals(1003, $response['data']['code']);
+ $this->assertEquals('Message type is not valid.', $response['data']['message']);
+
+ $client->send(\json_encode([
+ 'test' => '123',
+ ]));
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('error', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertEquals(1003, $response['data']['code']);
+ $this->assertEquals('Message format is not valid.', $response['data']['message']);
+
+
+ $client->close();
+ }
+
+ public function testConnectionPlatform()
+ {
+ /**
+ * Test for FAILURE
+ */
+ $client = $this->getWebsocket(['documents'], ['origin' => 'http://appwrite.unknown']);
+ $payload = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $payload);
+ $this->assertArrayHasKey('data', $payload);
+ $this->assertEquals('error', $payload['type']);
+ $this->assertEquals(1008, $payload['data']['code']);
+ $this->assertEquals('Invalid Origin. Register your new client (appwrite.unknown) as a new Web platform on your project console dashboard', $payload['data']['message']);
+ $this->expectException(ConnectionException::class); // Check if server disconnnected client
+ $client->close();
+ }
+
+ public function testChannelAccount()
+ {
+ $user = $this->getUser();
+ $userId = $user['$id'] ?? '';
+ $session = $user['session'] ?? '';
+ $projectId = $this->getProject()['$id'];
+
+ $client = $this->getWebsocket(['account'], [
+ 'origin' => 'http://localhost',
+ 'cookie' => 'a_session_'.$projectId.'=' . $session
+ ]);
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertContains('account', $response['data']['channels']);
+ $this->assertContains('account.' . $userId, $response['data']['channels']);
+ $this->assertNotEmpty($response['data']['user']);
+ $this->assertEquals($userId, $response['data']['user']['$id']);
+
+ /**
+ * Test Account Name Event
+ */
+ $name = "Torsten Dittmann";
+
+ $this->client->call(Client::METHOD_PATCH, '/account/name', array_merge([
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $projectId,
+ 'cookie' => 'a_session_' . $projectId . '=' . $session,
+ ]), [
+ 'name' => $name
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertContains('account', $response['data']['channels']);
+ $this->assertContains('account.' . $userId, $response['data']['channels']);
+ $this->assertEquals('account.update.name', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ $this->assertEquals($name, $response['data']['payload']['name']);
+
+
+ /**
+ * Test Account Password Event
+ */
+ $this->client->call(Client::METHOD_PATCH, '/account/password', array_merge([
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $projectId,
+ 'cookie' => 'a_session_'.$projectId.'=' . $session,
+ ]), [
+ 'password' => 'new-password',
+ 'oldPassword' => 'password',
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertContains('account', $response['data']['channels']);
+ $this->assertContains('account.' . $userId, $response['data']['channels']);
+ $this->assertEquals('account.update.password', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ $this->assertEquals($name, $response['data']['payload']['name']);
+
+ /**
+ * Test Account Email Update
+ */
+ $this->client->call(Client::METHOD_PATCH, '/account/email', array_merge([
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $projectId,
+ 'cookie' => 'a_session_'.$projectId.'=' . $session,
+ ]), [
+ 'email' => 'torsten@appwrite.io',
+ 'password' => 'new-password',
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertContains('account', $response['data']['channels']);
+ $this->assertContains('account.' . $userId, $response['data']['channels']);
+ $this->assertEquals('account.update.email', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ $this->assertEquals('torsten@appwrite.io', $response['data']['payload']['email']);
+
+ /**
+ * Test Account Verification Create
+ */
+ $this->client->call(Client::METHOD_POST, '/account/verification', array_merge([
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $projectId,
+ 'cookie' => 'a_session_'.$projectId.'=' . $session,
+ ]), [
+ 'url' => 'http://localhost/verification',
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertContains('account', $response['data']['channels']);
+ $this->assertContains('account.' . $userId, $response['data']['channels']);
+ $this->assertEquals('account.verification.create', $response['data']['event']);
+
+ $lastEmail = $this->getLastEmail();
+ $verification = substr($lastEmail['text'], strpos($lastEmail['text'], '&secret=', 0) + 8, 256);
+
+ /**
+ * Test Account Verification Complete
+ */
+ $response = $this->client->call(Client::METHOD_PUT, '/account/verification', array_merge([
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $projectId,
+ 'cookie' => 'a_session_'.$projectId.'=' . $session,
+ ]), [
+ 'userId' => $userId,
+ 'secret' => $verification,
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertContains('account', $response['data']['channels']);
+ $this->assertContains('account.' . $userId, $response['data']['channels']);
+ $this->assertEquals('account.verification.update', $response['data']['event']);
+
+ /**
+ * Test Acoount Prefs Update
+ */
+ $this->client->call(Client::METHOD_PATCH, '/account/prefs', array_merge([
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $projectId,
+ 'cookie' => 'a_session_'.$projectId.'=' . $session,
+ ]), [
+ 'prefs' => [
+ 'prefKey1' => 'prefValue1',
+ 'prefKey2' => 'prefValue2',
+ ]
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertContains('account', $response['data']['channels']);
+ $this->assertContains('account.' . $userId, $response['data']['channels']);
+ $this->assertEquals('account.update.prefs', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ /**
+ * Test Account Session Create
+ */
+ $response = $this->client->call(Client::METHOD_POST, '/account/sessions', array_merge([
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $projectId,
+ ]), [
+ 'email' => 'torsten@appwrite.io',
+ 'password' => 'new-password',
+ ]);
+
+ $sessionNew = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$projectId];
+ $sessionNewId = $response['body']['$id'];
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertContains('account', $response['data']['channels']);
+ $this->assertContains('account.' . $userId, $response['data']['channels']);
+ $this->assertEquals('account.sessions.create', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ /**
+ * Test Account Session Delete
+ */
+ $this->client->call(Client::METHOD_DELETE, '/account/sessions/'.$sessionNewId, array_merge([
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $projectId,
+ 'cookie' => 'a_session_'.$projectId.'=' . $sessionNew,
+ ]));
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertContains('account', $response['data']['channels']);
+ $this->assertContains('account.' . $userId, $response['data']['channels']);
+ $this->assertEquals('account.sessions.delete', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ /**
+ * Test Account Create Recovery
+ */
+ $this->client->call(Client::METHOD_POST, '/account/recovery', array_merge([
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $projectId,
+ ]), [
+ 'email' => 'torsten@appwrite.io',
+ 'url' => 'http://localhost/recovery',
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $lastEmail = $this->getLastEmail();
+ $recovery = substr($lastEmail['text'], strpos($lastEmail['text'], '&secret=', 0) + 8, 256);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertContains('account', $response['data']['channels']);
+ $this->assertContains('account.' . $userId, $response['data']['channels']);
+ $this->assertEquals('account.recovery.create', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ $response = $this->client->call(Client::METHOD_PUT, '/account/recovery', array_merge([
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $projectId,
+ ]), [
+ 'userId' => $userId,
+ 'secret' => $recovery,
+ 'password' => 'test-recovery',
+ 'passwordAgain' => 'test-recovery',
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertContains('account', $response['data']['channels']);
+ $this->assertContains('account.' . $userId, $response['data']['channels']);
+ $this->assertEquals('account.recovery.update', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ $client->close();
+ }
+
+ public function testChannelDatabase()
+ {
+ $user = $this->getUser();
+ $session = $user['session'] ?? '';
+ $projectId = $this->getProject()['$id'];
+
+ $client = $this->getWebsocket(['documents', 'collections'], [
+ 'origin' => 'http://localhost',
+ 'cookie' => 'a_session_'.$projectId.'=' . $session
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertContains('documents', $response['data']['channels']);
+ $this->assertContains('collections', $response['data']['channels']);
+ $this->assertNotEmpty($response['data']['user']);
+ $this->assertEquals($user['$id'], $response['data']['user']['$id']);
+
+ /**
+ * Test Collection Create
+ */
+ $actors = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey']
+ ]), [
+ 'collectionId' => 'unique()',
+ 'name' => 'Actors',
+ 'read' => [],
+ 'write' => [],
+ 'permission' => 'document'
+ ]);
+
+ $data = ['actorsId' => $actors['body']['$id']];
+
+ $name = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/attributes/string', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey']
+ ]), [
+ 'key' => 'name',
+ 'size' => 256,
+ 'required' => true,
+ ]);
+
+ $this->assertEquals($name['headers']['status-code'], 201);
+ $this->assertEquals($name['body']['key'], 'name');
+ $this->assertEquals($name['body']['type'], 'string');
+ $this->assertEquals($name['body']['size'], 256);
+ $this->assertEquals($name['body']['required'], true);
+
+ sleep(2);
+
+ /**
+ * Test Document Create
+ */
+ $document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/documents', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'documentId' => 'unique()',
+ 'data' => [
+ 'name' => 'Chris Evans'
+ ],
+ 'read' => ['role:all'],
+ 'write' => ['role:all'],
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(3, $response['data']['channels']);
+ $this->assertContains('documents', $response['data']['channels']);
+ $this->assertContains('documents.' . $document['body']['$id'], $response['data']['channels']);
+ $this->assertContains('collections.' . $actors['body']['$id'] . '.documents', $response['data']['channels']);
+ $this->assertEquals('database.documents.create', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+ $this->assertEquals($response['data']['payload']['name'], 'Chris Evans');
+
+ $data['documentId'] = $document['body']['$id'];
+
+ /**
+ * Test Document Update
+ */
+ $document = $this->client->call(Client::METHOD_PATCH, '/database/collections/' . $data['actorsId'] . '/documents/' . $data['documentId'], array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'documentId' => 'unique()',
+ 'data' => [
+ 'name' => 'Chris Evans 2'
+ ],
+ 'read' => ['role:all'],
+ 'write' => ['role:all'],
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(3, $response['data']['channels']);
+ $this->assertContains('documents', $response['data']['channels']);
+ $this->assertContains('documents.' . $data['documentId'], $response['data']['channels']);
+ $this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
+ $this->assertEquals('database.documents.update', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ $this->assertEquals($response['data']['payload']['name'], 'Chris Evans 2');
+
+ /**
+ * Test Document Delete
+ */
+ $document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/documents', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'documentId' => 'unique()',
+ 'data' => [
+ 'name' => 'Bradley Cooper'
+ ],
+ 'read' => ['role:all'],
+ 'write' => ['role:all'],
+ ]);
+
+ $client->receive();
+
+ $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['actorsId'] . '/documents/' . $document['body']['$id'], array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()));
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(3, $response['data']['channels']);
+ $this->assertContains('documents', $response['data']['channels']);
+ $this->assertContains('documents.' . $document['body']['$id'], $response['data']['channels']);
+ $this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
+ $this->assertEquals('database.documents.delete', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+ $this->assertEquals($response['data']['payload']['name'], 'Bradley Cooper');
+
+ $client->close();
+ }
+
+ public function testChannelDatabaseCollectionPermissions()
+ {
+ $user = $this->getUser();
+ $session = $user['session'] ?? '';
+ $projectId = $this->getProject()['$id'];
+
+ $client = $this->getWebsocket(['documents', 'collections'], [
+ 'origin' => 'http://localhost',
+ 'cookie' => 'a_session_'.$projectId.'=' . $session
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertContains('documents', $response['data']['channels']);
+ $this->assertContains('collections', $response['data']['channels']);
+ $this->assertNotEmpty($response['data']['user']);
+ $this->assertEquals($user['$id'], $response['data']['user']['$id']);
+
+ /**
+ * Test Collection Create
+ */
+ $actors = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey']
+ ]), [
+ 'collectionId' => 'unique()',
+ 'name' => 'Actors',
+ 'read' => ['role:all'],
+ 'write' => ['role:all'],
+ 'permission' => 'collection'
+ ]);
+
+ $data = ['actorsId' => $actors['body']['$id']];
+
+ $name = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/attributes/string', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey']
+ ]), [
+ 'key' => 'name',
+ 'size' => 256,
+ 'required' => true,
+ ]);
+
+ $this->assertEquals($name['headers']['status-code'], 201);
+ $this->assertEquals($name['body']['key'], 'name');
+ $this->assertEquals($name['body']['type'], 'string');
+ $this->assertEquals($name['body']['size'], 256);
+ $this->assertEquals($name['body']['required'], true);
+
+ sleep(2);
+
+ /**
+ * Test Document Create
+ */
+ $document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/documents', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'documentId' => 'unique()',
+ 'data' => [
+ 'name' => 'Chris Evans'
+ ],
+ 'read' => [],
+ 'write' => [],
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(3, $response['data']['channels']);
+ $this->assertContains('documents', $response['data']['channels']);
+ $this->assertContains('documents.' . $document['body']['$id'], $response['data']['channels']);
+ $this->assertContains('collections.' . $actors['body']['$id'] . '.documents', $response['data']['channels']);
+ $this->assertEquals('database.documents.create', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+ $this->assertEquals($response['data']['payload']['name'], 'Chris Evans');
+
+ $data['documentId'] = $document['body']['$id'];
+
+ /**
+ * Test Document Update
+ */
+ $document = $this->client->call(Client::METHOD_PATCH, '/database/collections/' . $data['actorsId'] . '/documents/' . $data['documentId'], array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'data' => [
+ 'name' => 'Chris Evans 2'
+ ],
+ 'read' => [],
+ 'write' => [],
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(3, $response['data']['channels']);
+ $this->assertContains('documents', $response['data']['channels']);
+ $this->assertContains('documents.' . $data['documentId'], $response['data']['channels']);
+ $this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
+ $this->assertEquals('database.documents.update', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ $this->assertEquals($response['data']['payload']['name'], 'Chris Evans 2');
+
+ /**
+ * Test Document Delete
+ */
+ $document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/documents', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'documentId' => 'unique()',
+ 'data' => [
+ 'name' => 'Bradley Cooper'
+ ],
+ 'read' => [],
+ 'write' => [],
+ ]);
+
+ $client->receive();
+
+ $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['actorsId'] . '/documents/' . $document['body']['$id'], array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()));
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(3, $response['data']['channels']);
+ $this->assertContains('documents', $response['data']['channels']);
+ $this->assertContains('documents.' . $document['body']['$id'], $response['data']['channels']);
+ $this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
+ $this->assertEquals('database.documents.delete', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+ $this->assertEquals($response['data']['payload']['name'], 'Bradley Cooper');
+
+ $client->close();
+ }
+
+ public function testChannelFiles()
+ {
+ $user = $this->getUser();
+ $session = $user['session'] ?? '';
+ $projectId = $this->getProject()['$id'];
+
+ $client = $this->getWebsocket(['files', 'buckets'], [
+ 'origin' => 'http://localhost',
+ 'cookie' => 'a_session_'.$projectId.'=' . $session
+ ]);
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertContains('files', $response['data']['channels']);
+ $this->assertContains('buckets', $response['data']['channels']);
+ $this->assertNotEmpty($response['data']['user']);
+ $this->assertEquals($user['$id'], $response['data']['user']['$id']);
+
+ /**
+ * Test Bucket Create
+ */
+ $bucket1 = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey']
+ ]), [
+ 'bucketId' => 'unique()',
+ 'name' => 'Bucket 1',
+ 'read' => ['role:all'],
+ 'write' => ['role:all'],
+ 'permission' => 'bucket'
+ ]);
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertContains('buckets', $response['data']['channels']);
+ $this->assertContains('buckets.' . $bucket1['body']['$id'], $response['data']['channels']);
+ $this->assertEquals('storage.buckets.create', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+ $data = ['bucketId' => $bucket1['body']['$id']];
+ /**
+ * Test File Create
+ */
+ $file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
+ 'content-type' => 'multipart/form-data',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'fileId' => 'unique()',
+ 'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
+ 'read' => ['role:all'],
+ 'write' => ['role:all'],
+ 'folderId' => 'xyz',
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(3, $response['data']['channels']);
+ $this->assertContains('files', $response['data']['channels']);
+ $this->assertContains('files.' . $file['body']['$id'], $response['data']['channels']);
+ $this->assertContains('buckets.' . $data['bucketId'] . '.files', $response['data']['channels']);
+ $this->assertEquals('storage.files.create', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ $data['fileId'] = $file['body']['$id'];
+
+ /**
+ * Test File Update
+ */
+ $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'], array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'read' => ['role:all'],
+ 'write' => ['role:all'],
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(3, $response['data']['channels']);
+ $this->assertContains('files', $response['data']['channels']);
+ $this->assertContains('files.' . $file['body']['$id'], $response['data']['channels']);
+ $this->assertContains('buckets.' . $data['bucketId'] . '.files', $response['data']['channels']);
+ $this->assertEquals('storage.files.update', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ /**
+ * Test File Delete
+ */
+ $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'], array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()));
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(3, $response['data']['channels']);
+ $this->assertContains('files', $response['data']['channels']);
+ $this->assertContains('files.' . $file['body']['$id'], $response['data']['channels']);
+ $this->assertContains('buckets.' . $data['bucketId'] . '.files', $response['data']['channels']);
+ $this->assertEquals('storage.files.delete', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ $client->close();
+ }
+
+ public function testChannelExecutions()
+ {
+ $user = $this->getUser();
+ $session = $user['session'] ?? '';
+ $projectId = $this->getProject()['$id'];
+
+ $client = $this->getWebsocket(['executions'], [
+ 'origin' => 'http://localhost',
+ 'cookie' => 'a_session_'.$projectId.'=' . $session
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('executions', $response['data']['channels']);
+ $this->assertNotEmpty($response['data']['user']);
+ $this->assertEquals($user['$id'], $response['data']['user']['$id']);
+
+ /**
+ * Test Functions Create
+ */
+ $function = $this->client->call(Client::METHOD_POST, '/functions', [
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey']
+ ], [
+ 'functionId' => 'unique()',
+ 'name' => 'Test',
+ 'execute' => ['role:member'],
+ 'runtime' => 'php-8.0',
+ 'timeout' => 10,
+ ]);
+
+ $functionId = $function['body']['$id'] ?? '';
+
+ $this->assertEquals($function['headers']['status-code'], 201);
+ $this->assertNotEmpty($function['body']['$id']);
+
+ $tag = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/tags', array_merge([
+ 'content-type' => 'multipart/form-data',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey']
+ ]), [
+ 'command' => 'php index.php',
+ 'code' => new CURLFile(realpath(__DIR__ . '/../../../resources/functions/timeout.tar.gz'), 'application/x-gzip', 'php-fx.tar.gz'),
+ ]);
+
+ $tagId = $tag['body']['$id'] ?? '';
+
+ $this->assertEquals($tag['headers']['status-code'], 201);
+ $this->assertNotEmpty($tag['body']['$id']);
+
+ $response = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/tag', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey']
+ ]), [
+ 'tag' => $tagId,
+ ]);
+
+ $this->assertEquals($response['headers']['status-code'], 200);
+ $this->assertNotEmpty($response['body']['$id']);
+
+ $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id']
+ ], $this->getHeaders()), []);
+
+ $this->assertEquals($execution['headers']['status-code'], 201);
+ $this->assertNotEmpty($execution['body']['$id']);
+
+ $response = json_decode($client->receive(), true);
+ $responseUpdate = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(3, $response['data']['channels']);
+ $this->assertContains('executions', $response['data']['channels']);
+ $this->assertContains('executions.' . $execution['body']['$id'], $response['data']['channels']);
+ $this->assertContains('functions.' . $execution['body']['functionId'], $response['data']['channels']);
+ $this->assertEquals('functions.executions.create', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ $this->assertArrayHasKey('type', $responseUpdate);
+ $this->assertArrayHasKey('data', $responseUpdate);
+ $this->assertEquals('event', $responseUpdate['type']);
+ $this->assertNotEmpty($responseUpdate['data']);
+ $this->assertArrayHasKey('timestamp', $responseUpdate['data']);
+ $this->assertCount(3, $responseUpdate['data']['channels']);
+ $this->assertContains('executions', $responseUpdate['data']['channels']);
+ $this->assertContains('executions.' . $execution['body']['$id'], $responseUpdate['data']['channels']);
+ $this->assertContains('functions.' . $execution['body']['functionId'], $responseUpdate['data']['channels']);
+ $this->assertEquals('functions.executions.update', $responseUpdate['data']['event']);
+ $this->assertNotEmpty($responseUpdate['data']['payload']);
+
+ $client->close();
+ }
+
+ public function testChannelTeams(): array
+ {
+ $user = $this->getUser();
+ $session = $user['session'] ?? '';
+ $projectId = $this->getProject()['$id'];
+
+ $client = $this->getWebsocket(['teams'], [
+ 'origin' => 'http://localhost',
+ 'cookie' => 'a_session_'.$projectId.'=' . $session
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('teams', $response['data']['channels']);
+ $this->assertNotEmpty($response['data']['user']);
+ $this->assertEquals($user['$id'], $response['data']['user']['$id']);
+
+ /**
+ * Test Team Create
+ */
+ $team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $projectId,
+ ], $this->getHeaders()), [
+ 'teamId' => 'unique()',
+ 'name' => 'Arsenal'
+ ]);
+
+ $teamId = $team['body']['$id'] ?? '';
+
+ $this->assertEquals(201, $team['headers']['status-code']);
+ $this->assertNotEmpty($team['body']['$id']);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertContains('teams', $response['data']['channels']);
+ $this->assertContains('teams.' . $teamId, $response['data']['channels']);
+ $this->assertEquals('teams.create', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ /**
+ * Test Team Update
+ */
+ $team = $this->client->call(Client::METHOD_PUT, '/teams/'.$teamId, array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $projectId,
+ ], $this->getHeaders()), [
+ 'name' => 'Manchester'
+ ]);
+
+ $this->assertEquals($team['headers']['status-code'], 200);
+ $this->assertNotEmpty($team['body']['$id']);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertContains('teams', $response['data']['channels']);
+ $this->assertContains('teams.' . $teamId, $response['data']['channels']);
+ $this->assertEquals('teams.update', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ $client->close();
+
+ return ['teamId' => $teamId];
+ }
+
+ /**
+ * @depends testChannelTeams
+ */
+ public function testChannelMemberships(array $data)
+ {
+ $teamId = $data['teamId'] ?? '';
+
+ $user = $this->getUser();
+ $session = $user['session'] ?? '';
+ $projectId = $this->getProject()['$id'];
+
+ $client = $this->getWebsocket(['memberships'], [
+ 'origin' => 'http://localhost',
+ 'cookie' => 'a_session_'.$projectId.'='.$session
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('connected', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertCount(1, $response['data']['channels']);
+ $this->assertContains('memberships', $response['data']['channels']);
+ $this->assertNotEmpty($response['data']['user']);
+ $this->assertEquals($user['$id'], $response['data']['user']['$id']);
+
+ $response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamId.'/memberships', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()));
+
+ $membershipId = $response['body']['memberships'][0]['$id'];
+
+ /**
+ * Test Update Membership
+ */
+ $roles = ['admin', 'editor', 'uncle'];
+ $this->client->call(Client::METHOD_PATCH, '/teams/'.$teamId.'/memberships/'.$membershipId, array_merge([
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'roles' => $roles
+ ]);
+
+ $response = json_decode($client->receive(), true);
+
+ $this->assertArrayHasKey('type', $response);
+ $this->assertArrayHasKey('data', $response);
+ $this->assertEquals('event', $response['type']);
+ $this->assertNotEmpty($response['data']);
+ $this->assertArrayHasKey('timestamp', $response['data']);
+ $this->assertCount(2, $response['data']['channels']);
+ $this->assertContains('memberships', $response['data']['channels']);
+ $this->assertContains('memberships.' . $membershipId, $response['data']['channels']);
+ $this->assertEquals('teams.memberships.update', $response['data']['event']);
+ $this->assertNotEmpty($response['data']['payload']);
+
+ $client->close();
+ }
}
\ No newline at end of file
diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php
index 6fbfe8ee32..87d45e579f 100644
--- a/tests/e2e/Services/Storage/StorageBase.php
+++ b/tests/e2e/Services/Storage/StorageBase.php
@@ -406,8 +406,8 @@ trait StorageBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
- 'read' => ['role:all', 'user:x'],
- 'write' => ['role:all', 'user:x'],
+ 'read' => ['user:'.$this->getUser()['$id']],
+ 'write' => ['user:'.$this->getUser()['$id']],
]);
$this->assertEquals(200, $file['headers']['status-code']);
@@ -424,8 +424,8 @@ trait StorageBase
//$this->assertNotEmpty($file['body']['fileOpenSSLIV']);
$this->assertIsArray($file['body']['$read']);
$this->assertIsArray($file['body']['$write']);
- $this->assertCount(2, $file['body']['$read']);
- $this->assertCount(2, $file['body']['$write']);
+ $this->assertCount(1, $file['body']['$read']);
+ $this->assertCount(1, $file['body']['$write']);
/**
* Test for FAILURE unknown Bucket
@@ -435,8 +435,8 @@ trait StorageBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
- 'read' => ['role:all', 'user:x'],
- 'write' => ['role:all', 'user:x'],
+ 'read' => ['user:'.$this->getUser()['$id']],
+ 'write' => ['user:'.$this->getUser()['$id']],
]);
$this->assertEquals(404, $file['headers']['status-code']);
diff --git a/tests/e2e/Services/Storage/StorageCustomClientTest.php b/tests/e2e/Services/Storage/StorageCustomClientTest.php
index c99587bb25..01d410f1a9 100644
--- a/tests/e2e/Services/Storage/StorageCustomClientTest.php
+++ b/tests/e2e/Services/Storage/StorageCustomClientTest.php
@@ -3,6 +3,9 @@
namespace Tests\E2E\Services\Storage;
use CURLFile;
+use Exception;
+use SebastianBergmann\RecursionContext\InvalidArgumentException;
+use PHPUnit\Framework\ExpectationFailedException;
use Tests\E2E\Client;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\ProjectCustom;
@@ -14,7 +17,7 @@ class StorageCustomClientTest extends Scope
use ProjectCustom;
use SideClient;
- public function testCreateFileDefaultPermissions():void
+ public function testCreateFileDefaultPermissions(): array
{
/**
* Test for SUCCESS
@@ -49,5 +52,114 @@ class StorageCustomClientTest extends Scope
$this->assertEquals('permissions.png', $file['body']['name']);
$this->assertEquals('image/png', $file['body']['mimeType']);
$this->assertEquals(47218, $file['body']['sizeOriginal']);
+
+ return ['fileId' => $file['body']['$id'], 'bucketId' => $bucket['body']['$id']];
+ }
+
+ /**
+ * @depends testCreateFileDefaultPermissions
+ */
+ public function testCreateFileAbusePermissions(array $data): void
+ {
+ /**
+ * Test for FAILURE
+ */
+ $file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
+ 'content-type' => 'multipart/form-data',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'fileId' => 'unique()',
+ 'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'),
+ 'folderId' => 'xyz',
+ 'read' => ['user:notme']
+ ]);
+
+ $this->assertEquals(400, $file['headers']['status-code']);
+ $this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']);
+ $this->assertStringContainsString('role:all', $file['body']['message']);
+ $this->assertStringContainsString('role:member', $file['body']['message']);
+ $this->assertStringContainsString('user:'.$this->getUser()['$id'], $file['body']['message']);
+
+ $file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
+ 'content-type' => 'multipart/form-data',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'fileId' => 'unique()',
+ 'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'),
+ 'folderId' => 'xyz',
+ 'write' => ['user:notme']
+ ]);
+
+ $this->assertEquals($file['headers']['status-code'], 400);
+ $this->assertStringStartsWith('Write permissions must be one of:', $file['body']['message']);
+ $this->assertStringContainsString('role:all', $file['body']['message']);
+ $this->assertStringContainsString('role:member', $file['body']['message']);
+ $this->assertStringContainsString('user:'.$this->getUser()['$id'], $file['body']['message']);
+
+ $file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
+ 'content-type' => 'multipart/form-data',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'fileId' => 'unique()',
+ 'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'),
+ 'folderId' => 'xyz',
+ 'read' => ['user:notme'],
+ 'write' => ['user:notme']
+ ]);
+
+ $this->assertEquals($file['headers']['status-code'], 400);
+ $this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']);
+ $this->assertStringContainsString('role:all', $file['body']['message']);
+ $this->assertStringContainsString('role:member', $file['body']['message']);
+ $this->assertStringContainsString('user:'.$this->getUser()['$id'], $file['body']['message']);
+ }
+
+ /**
+ * @depends testCreateFileDefaultPermissions
+ */
+ public function testUpdateFileAbusePermissions(array $data): void
+ {
+ /**
+ * Test for FAILURE
+ */
+ $file = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'], array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'read' => ['user:notme']
+ ]);
+
+ $this->assertEquals($file['headers']['status-code'], 400);
+ $this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']);
+ $this->assertStringContainsString('role:all', $file['body']['message']);
+ $this->assertStringContainsString('role:member', $file['body']['message']);
+ $this->assertStringContainsString('user:'.$this->getUser()['$id'], $file['body']['message']);
+
+ $file = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'], array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'write' => ['user:notme']
+ ]);
+
+ $this->assertEquals($file['headers']['status-code'], 400);
+ $this->assertStringStartsWith('Write permissions must be one of:', $file['body']['message']);
+ $this->assertStringContainsString('role:all', $file['body']['message']);
+ $this->assertStringContainsString('role:member', $file['body']['message']);
+ $this->assertStringContainsString('user:'.$this->getUser()['$id'], $file['body']['message']);
+
+ $file = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'], array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'read' => ['user:notme'],
+ 'write' => ['user:notme']
+ ]);
+
+ $this->assertEquals($file['headers']['status-code'], 400);
+ $this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']);
+ $this->assertStringContainsString('role:all', $file['body']['message']);
+ $this->assertStringContainsString('role:member', $file['body']['message']);
+ $this->assertStringContainsString('user:'.$this->getUser()['$id'], $file['body']['message']);
}
}
\ No newline at end of file
diff --git a/tests/e2e/Services/Webhooks/WebhooksBase.php b/tests/e2e/Services/Webhooks/WebhooksBase.php
index 66a2459504..714f1d1e05 100644
--- a/tests/e2e/Services/Webhooks/WebhooksBase.php
+++ b/tests/e2e/Services/Webhooks/WebhooksBase.php
@@ -57,7 +57,7 @@ trait WebhooksBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'firstName',
+ 'key' => 'firstName',
'size' => 256,
'required' => true,
]);
@@ -67,7 +67,7 @@ trait WebhooksBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'lastName',
+ 'key' => 'lastName',
'size' => 256,
'required' => true,
]);
@@ -77,7 +77,7 @@ trait WebhooksBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'attributeId' => 'extra',
+ 'key' => 'extra',
'size' => 64,
'required' => false,
]);
diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php
index 09aaa63524..cb243c8c2b 100644
--- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php
+++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php
@@ -64,7 +64,7 @@ class WebhooksCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
- 'indexId' => 'fullname',
+ 'key' => 'fullname',
'type' => 'key',
'attributes' => ['lastName', 'firstName'],
'orders' => ['ASC', 'ASC'],
@@ -125,7 +125,7 @@ class WebhooksCustomServerTest extends Scope
'write' => ['role:all'],
'permission' => 'document'
]);
-
+
$this->assertEquals($actors['headers']['status-code'], 201);
$this->assertNotEmpty($actors['body']['$id']);
@@ -134,7 +134,7 @@ class WebhooksCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), []);
-
+
$this->assertEquals($actors['headers']['status-code'], 204);
$webhook = $this->getLastRequest();
diff --git a/tests/unit/Auth/AuthTest.php b/tests/unit/Auth/AuthTest.php
index c3eb711155..c3b9d00758 100644
--- a/tests/unit/Auth/AuthTest.php
+++ b/tests/unit/Auth/AuthTest.php
@@ -172,35 +172,35 @@ class AuthTest extends TestCase
public function testIsPrivilegedUser()
{
$this->assertEquals(false, Auth::isPrivilegedUser([]));
- $this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_GUEST => true]));
- $this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_MEMBER => true]));
- $this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_ADMIN => true]));
- $this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_DEVELOPER => true]));
- $this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER => true]));
- $this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP => true]));
- $this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_SYSTEM => true]));
+ $this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_GUEST]));
+ $this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_MEMBER]));
+ $this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_ADMIN]));
+ $this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_DEVELOPER]));
+ $this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER]));
+ $this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP]));
+ $this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_SYSTEM]));
- $this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP => true, 'role:'.Auth::USER_ROLE_APP => true]));
- $this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP => true, 'role:'.Auth::USER_ROLE_GUEST => true]));
- $this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER => true, 'role:'.Auth::USER_ROLE_GUEST => true]));
- $this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER => true, 'role:'.Auth::USER_ROLE_ADMIN => true, 'role:'.Auth::USER_ROLE_DEVELOPER => true]));
+ $this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP, 'role:'.Auth::USER_ROLE_APP]));
+ $this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP, 'role:'.Auth::USER_ROLE_GUEST]));
+ $this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER, 'role:'.Auth::USER_ROLE_GUEST]));
+ $this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER, 'role:'.Auth::USER_ROLE_ADMIN, 'role:'.Auth::USER_ROLE_DEVELOPER]));
}
public function testIsAppUser()
{
$this->assertEquals(false, Auth::isAppUser([]));
- $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_GUEST => true]));
- $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_MEMBER => true]));
- $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_ADMIN => true]));
- $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_DEVELOPER => true]));
- $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_OWNER => true]));
- $this->assertEquals(true, Auth::isAppUser(['role:'.Auth::USER_ROLE_APP => true]));
- $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_SYSTEM => true]));
+ $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_GUEST]));
+ $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_MEMBER]));
+ $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_ADMIN]));
+ $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_DEVELOPER]));
+ $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_OWNER]));
+ $this->assertEquals(true, Auth::isAppUser(['role:'.Auth::USER_ROLE_APP]));
+ $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_SYSTEM]));
- $this->assertEquals(true, Auth::isAppUser(['role:'.Auth::USER_ROLE_APP => true, 'role:'.Auth::USER_ROLE_APP => true]));
- $this->assertEquals(true, Auth::isAppUser(['role:'.Auth::USER_ROLE_APP => true, 'role:'.Auth::USER_ROLE_GUEST => true]));
- $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_OWNER => true, 'role:'.Auth::USER_ROLE_GUEST => true]));
- $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_OWNER => true, 'role:'.Auth::USER_ROLE_ADMIN => true, 'role:'.Auth::USER_ROLE_DEVELOPER => true]));
+ $this->assertEquals(true, Auth::isAppUser(['role:'.Auth::USER_ROLE_APP, 'role:'.Auth::USER_ROLE_APP]));
+ $this->assertEquals(true, Auth::isAppUser(['role:'.Auth::USER_ROLE_APP, 'role:'.Auth::USER_ROLE_GUEST]));
+ $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_OWNER, 'role:'.Auth::USER_ROLE_GUEST]));
+ $this->assertEquals(false, Auth::isAppUser(['role:'.Auth::USER_ROLE_OWNER, 'role:'.Auth::USER_ROLE_ADMIN, 'role:'.Auth::USER_ROLE_DEVELOPER]));
}
public function testGuestRoles()
diff --git a/tests/unit/Messaging/MessagingTest.php b/tests/unit/Messaging/MessagingTest.php
index dd258d3ae6..a0cad0bae9 100644
--- a/tests/unit/Messaging/MessagingTest.php
+++ b/tests/unit/Messaging/MessagingTest.php
@@ -2,7 +2,7 @@
namespace Appwrite\Tests;
-use Appwrite\Database\Document;
+use Utopia\Database\Document;
use Appwrite\Messaging\Adapter\Realtime;
use PHPUnit\Framework\TestCase;
@@ -195,4 +195,51 @@ class MessagingTest extends TestCase
$this->assertArrayHasKey('account', $channels);
$this->assertArrayNotHasKey('account.456', $channels);
}
+
+ public function testFromPayloadCollectionLevelPermissions(): void
+ {
+ /**
+ * Test Collection Level Permissions
+ */
+ $result = Realtime::fromPayload(
+ event: 'database.documents.create',
+ payload: new Document([
+ '$id' => 'test',
+ '$collection' => 'collection',
+ '$read' => ['role:admin'],
+ '$write' => ['role:admin']
+ ]),
+ collection: new Document([
+ '$id' => 'collection',
+ '$read' => ['role:all'],
+ '$write' => ['role:all'],
+ 'permission' => 'collection'
+ ])
+ );
+
+ $this->assertContains('role:all', $result['roles']);
+ $this->assertNotContains('role:admin', $result['roles']);
+
+ /**
+ * Test Document Level Permissions
+ */
+ $result = Realtime::fromPayload(
+ event: 'database.documents.create',
+ payload: new Document([
+ '$id' => 'test',
+ '$collection' => 'collection',
+ '$read' => ['role:all'],
+ '$write' => ['role:all']
+ ]),
+ collection: new Document([
+ '$id' => 'collection',
+ '$read' => ['role:admin'],
+ '$write' => ['role:admin'],
+ 'permission' => 'document'
+ ])
+ );
+
+ $this->assertContains('role:all', $result['roles']);
+ $this->assertNotContains('role:admin', $result['roles']);
+ }
}