1
0
Fork 0
mirror of synced 2024-06-02 10:54:44 +12:00

Merge branch 'feat-database-indexing' into fix-missing-encryption-filters

This commit is contained in:
Damodar Lohani 2021-12-02 13:43:12 +05:45
commit bc22bbae46
24 changed files with 3803 additions and 3828 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1186,7 +1186,7 @@ App::get('/v1/account/logs')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_LOG_LIST)
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 900000000), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->inject('response')
->inject('user')
->inject('locale')

View file

@ -207,7 +207,7 @@ App::get('/v1/database/collections')
->label('sdk.response.model', Response::MODEL_COLLECTION_LIST)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 40000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('cursor', '', new UID(), 'ID of the collection used as the starting point for the query, excluding the collection itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
@ -500,7 +500,7 @@ App::get('/v1/database/collections/:collectionId/logs')
->label('sdk.response.model', Response::MODEL_LOG_LIST)
->param('collectionId', '', new UID(), 'Collection unique ID.')
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 900000000), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
@ -1699,7 +1699,7 @@ App::get('/v1/database/collections/:collectionId/documents')
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('queries', [], new ArrayList(new Text(128)), 'Array of query strings.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of documents to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 900000000), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->param('cursor', '', new UID(), 'ID of the document used as the starting point for the query, excluding the document itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderAttributes', [], new ArrayList(new Text(128)), 'Array of attributes used to sort results.', true)
@ -1839,7 +1839,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId/logs')
->param('collectionId', '', new UID(), 'Collection unique ID.')
->param('documentId', null, new UID(), 'Document unique ID.')
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 900000000), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')

View file

@ -89,7 +89,7 @@ App::get('/v1/functions')
->label('sdk.response.model', Response::MODEL_FUNCTION_LIST)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('cursor', '', new UID(), 'ID of the function used as the starting point for the query, excluding the function itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
@ -508,7 +508,7 @@ App::get('/v1/functions/:functionId/tags')
->param('functionId', '', new UID(), 'Function unique ID.')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('cursor', '', new UID(), 'ID of the tag used as the starting point for the query, excluding the tag itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
@ -769,7 +769,7 @@ App::get('/v1/functions/:functionId/executions')
->label('sdk.response.model', Response::MODEL_EXECUTION_LIST)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('cursor', '', new UID(), 'ID of the execution used as the starting point for the query, excluding the execution itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)

View file

@ -59,13 +59,11 @@ App::post('/v1/projects')
->inject('dbForConsole')
->inject('dbForInternal')
->inject('dbForExternal')
->inject('consoleDB')
->action(function ($projectId, $name, $teamId, $description, $logo, $url, $legalName, $legalCountry, $legalState, $legalCity, $legalAddress, $legalTaxId, $response, $dbForConsole, $dbForInternal, $dbForExternal, $consoleDB) {
->action(function ($projectId, $name, $teamId, $description, $logo, $url, $legalName, $legalCountry, $legalState, $legalCity, $legalAddress, $legalTaxId, $response, $dbForConsole, $dbForInternal, $dbForExternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Utopia\Database\Database $dbForExternal */
/** @var Appwrite\Database\Database $consoleDB */
$team = $dbForConsole->getDocument('teams', $teamId);
@ -106,7 +104,7 @@ App::post('/v1/projects')
'search' => implode(' ', [$projectId, $name]),
]));
$collections = Config::getParam('collections2', []); /** @var array $collections */
$collections = Config::getParam('collections', []); /** @var array $collections */
$dbForInternal->setNamespace('project_' . $project->getId() . '_internal');
$dbForInternal->create();
@ -148,8 +146,6 @@ App::post('/v1/projects')
$dbForInternal->createCollection($key, $attributes, $indexes);
}
$consoleDB->createNamespace($project->getId());
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($project, Response::MODEL_PROJECT);
});
@ -166,7 +162,7 @@ App::get('/v1/projects')
->label('sdk.response.model', Response::MODEL_PROJECT_LIST)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('cursor', '', new UID(), 'ID of the project used as the starting point for the query, excluding the project itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)

View file

@ -176,7 +176,7 @@ App::get('/v1/storage/files')
->label('sdk.response.model', Response::MODEL_FILE_LIST)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('cursor', '', new UID(), 'ID of the file used as the starting point for the query, excluding the file itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)

View file

@ -106,7 +106,7 @@ App::get('/v1/teams')
->label('sdk.response.model', Response::MODEL_TEAM_LIST)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('cursor', '', new UID(), 'ID of the team used as the starting point for the query, excluding the team itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
@ -444,7 +444,7 @@ App::get('/v1/teams/:teamId/memberships')
->param('teamId', '', new UID(), 'Team unique ID.')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('cursor', '', new UID(), 'ID of the membership used as the starting point for the query, excluding the membership itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)

View file

@ -94,7 +94,7 @@ App::get('/v1/users')
->label('sdk.response.model', Response::MODEL_USER_LIST)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('cursor', '', new UID(), 'ID of the user used as the starting point for the query, excluding the user itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
@ -260,7 +260,7 @@ App::get('/v1/users/:userId/logs')
->label('sdk.response.model', Response::MODEL_LOG_LIST)
->param('userId', '', new UID(), 'User unique ID.')
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 900000000), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->inject('response')
->inject('dbForInternal')
->inject('locale')

View file

@ -90,7 +90,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
if(!$dbForConsole->exists()) {
Console::success('[Setup] - Server database init started...');
$collections = Config::getParam('collections2', []); /** @var array $collections */
$collections = Config::getParam('collections', []); /** @var array $collections */
$redis->flushAll();

View file

@ -128,7 +128,6 @@ Config::load('auth', __DIR__.'/config/auth.php');
Config::load('providers', __DIR__.'/config/providers.php');
Config::load('platforms', __DIR__.'/config/platforms.php');
Config::load('collections', __DIR__.'/config/collections.php');
Config::load('collections2', __DIR__.'/config/collections2.php');
Config::load('runtimes', __DIR__.'/config/runtimes.php');
Config::load('roles', __DIR__.'/config/roles.php'); // User roles and scopes
Config::load('scopes', __DIR__.'/config/scopes.php'); // User roles and scopes
@ -805,24 +804,6 @@ App::setResource('console', function() {
]);
}, []);
App::setResource('consoleDB', function($db, $cache) {
$consoleDB = new DatabaseOld();
$consoleDB->setAdapter(new RedisAdapter(new MySQLAdapter($db, $cache), $cache));
$consoleDB->setNamespace('app_console'); // Should be replaced with param if we want to have parent projects
$consoleDB->setMocks(Config::getParam('collections', []));
return $consoleDB;
}, ['db', 'cache']);
App::setResource('projectDB', function($db, $cache, $project) {
$projectDB = new DatabaseOld();
$projectDB->setAdapter(new RedisAdapter(new MySQLAdapter($db, $cache), $cache));
$projectDB->setNamespace('app_'.$project->getId());
$projectDB->setMocks(Config::getParam('collections', []));
return $projectDB;
}, ['db', 'cache', 'project']);
App::setResource('dbForInternal', function($db, $cache, $project) {
$cache = new Cache(new RedisCache($cache));

View file

@ -11,6 +11,8 @@ use Appwrite\Database\Adapter\Redis as RedisAdapter;
use Appwrite\Migration\Migration;
use Utopia\Validator\Text;
Config::load('collections.old', __DIR__.'/../config/collections.old.php');
$cli
->task('migrate')
->param('version', APP_VERSION_STABLE, new Text(8), 'Version to migrate to.', true)
@ -29,12 +31,12 @@ $cli
$consoleDB
->setAdapter(new RedisAdapter(new MySQLAdapter($db, $cache), $cache))
->setNamespace('app_console') // Main DB
->setMocks(Config::getParam('collections', []));
->setMocks(Config::getParam('collections.old', []));
$projectDB = new Database();
$projectDB
->setAdapter(new RedisAdapter(new MySQLAdapter($db, $cache), $cache))
->setMocks(Config::getParam('collections', []));
->setMocks(Config::getParam('collections.old', []));
$console = $consoleDB->getDocument('console');

View file

@ -78,11 +78,11 @@ $logs = $this->getParam('logs', null);
<tbody data-ls-attrs="x-init=documents = {{project-documents.documents}}" x-data="{documents: []}">
<template x-for="doc in documents">
<tr>
<td>
<td data-title="$id: ">
<a :href="`/console/database/document?id=${doc.$id}&collection=${doc.$collection}&project=${project}`" x-text="doc.$id"></a>
</td>
<template x-for="attr in attributes">
<td x-show="attr.status === 'available'" style="width: 170px">
<td x-show="attr.status === 'available'" :data-title="attr.key + ':'">
<a :href="`/console/database/document?id=${doc.$id}&collection=${doc.$collection}&project=${project}`">
<span x-text="doc[attr.key] ?? 'n/a'"></span>
</a>
@ -192,7 +192,7 @@ $logs = $this->getParam('logs', null);
<td data-title="Default:">
<span class="text-size-small" data-ls-bind="{{attribute.default}}" data-ls-attr="title={{attribute.default}}"></span>
<span class="text-fade text-size-small" data-ls-if="!({{attribute.default}})">n/a</span>
<span class="text-fade text-size-small" data-ls-if="{{attribute.default}} != ''">n/a</span>
</td>
<td data-title="">

View file

@ -234,11 +234,11 @@ $logs = $this->getParam('logs', null);
</script>
<script type="text/html" id="template-boolean">
<input type="hidden" data-forms-switch data-ls-attrs="name={{attribute.key}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="boolean" class="margin-bottom-no" />
<input type="hidden" data-forms-switch data-ls-attrs="name={{attribute.key}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="boolean" />
</script>
<script type="text/html" id="template-boolean-array">
<input type="hidden" data-forms-switch data-ls-attrs="name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="boolean" class="margin-bottom-no" />
<input type="hidden" data-forms-switch data-ls-attrs="name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="boolean" />
</script>
<script type="text/html" id="template-url">

View file

@ -3433,8 +3433,9 @@ const highest=history.reduce((prev,curr)=>{return(curr.value>prev)?curr.value:pr
newHistory[project]=history;}
let currentSnapshot={...current};for(let index=.1;index<=1;index+=.05){let currentTransition={...currentSnapshot};for(const project in current){if(project in newHistory){let base=newHistory[project][bars-2].value;let cur=currentSnapshot[project];let offset=(cur-base)*index;currentTransition[project]=base+Math.floor(offset);}}
realtime.setCurrent(currentTransition);await sleep(250);}
realtime.setHistory(newHistory);}});window.formValidation=(form,fields)=>{const elements=form.elements;const actionHandler=(action,attribute)=>{switch(action){case"disable":elements[attribute].setAttribute("disabled",true);elements[attribute].dispatchEvent(new Event('change'));break;case"enable":elements[attribute].removeAttribute("disabled");elements[attribute].dispatchEvent(new Event('change'));break;case"unvalue":elements[attribute].value="";break;case"check":elements[attribute].value="true";break;case"uncheck":elements[attribute].value="false";break;}};for(const field in fields){for(const attribute in fields[field]){const attr=fields[field][attribute];if(Array.isArray(attr)){attr.forEach(action=>{if(elements[field].value==="true"){actionHandler(action,attribute);}})}else{const condition=attr.if.some(c=>{return elements[c].value==="true";});if(condition){for(const thenAction in attr.then){attr.then[thenAction].forEach(action=>{actionHandler(action,thenAction);});}}else{for(const elseAction in attr.else){attr.else[elseAction].forEach(action=>{actionHandler(action,elseAction);});}}}}}
form.addEventListener("reset",()=>{for(const key in fields){if(Object.hasOwnProperty.call(fields,key)){const element=form.elements[key];element.setAttribute("value","");element.removeAttribute("disabled");element.dispatchEvent(new Event("change"));}}});};(function(window){"use strict";window.ls.container.set('alerts',function(window){return{list:[],ids:0,counter:0,max:5,add:function(message,time){var scope=this;message.id=scope.ids++;message.remove=function(){scope.remove(message.id);};scope.counter++;scope.list.unshift(message);if(scope.counter>scope.max){scope.list.pop();scope.counter--;}
realtime.setHistory(newHistory);}});window.formValidation=(form,fields)=>{const elements=Array.from(form.querySelectorAll('[name]')).reduce((prev,curr)=>{if(!curr.name){return prev;}
prev[curr.name]=curr;return prev;},{});const actionHandler=(action,attribute)=>{switch(action){case"disable":elements[attribute].setAttribute("disabled",true);elements[attribute].dispatchEvent(new Event('change'));break;case"enable":elements[attribute].removeAttribute("disabled");elements[attribute].dispatchEvent(new Event('change'));break;case"unvalue":elements[attribute].value="";break;case"check":elements[attribute].value="true";break;case"uncheck":elements[attribute].value="false";break;}};for(const field in fields){for(const attribute in fields[field]){const attr=fields[field][attribute];if(Array.isArray(attr)){attr.forEach(action=>{if(elements[field].value==="true"){actionHandler(action,attribute);}})}else{const condition=attr.if.some(c=>{return elements[c].value==="true";});if(condition){for(const thenAction in attr.then){attr.then[thenAction].forEach(action=>{actionHandler(action,thenAction);});}}else{for(const elseAction in attr.else){attr.else[elseAction].forEach(action=>{actionHandler(action,elseAction);});}}}}}
form.addEventListener("reset",()=>{for(const key in fields){if(Object.hasOwnProperty.call(fields,key)){const element=elements[key];element.setAttribute("value","");element.removeAttribute("disabled");element.dispatchEvent(new Event("change"));}}});};(function(window){"use strict";window.ls.container.set('alerts',function(window){return{list:[],ids:0,counter:0,max:5,add:function(message,time){var scope=this;message.id=scope.ids++;message.remove=function(){scope.remove(message.id);};scope.counter++;scope.list.unshift(message);if(scope.counter>scope.max){scope.list.pop();scope.counter--;}
if(time>0){window.setTimeout(function(message){return function(){scope.remove(message.id)}}(message),time);}
return message.id;},remove:function(id){let scope=this;for(let index=0;index<scope.list.length;index++){let obj=scope.list[index];if(obj.id===parseInt(id)){scope.counter--;if(typeof obj.callback==="function"){obj.callback();}
scope.list.splice(index,1);};}}};},true,true);})(window);(function(window){"use strict";window.ls.container.set('appwrite',function(window,env){let config={endpoint:'https://appwrite.io/v1',};let http=function(document,env){let globalParams=[],globalHeaders=[];let addParam=function(url,param,value){let a=document.createElement('a'),regex=/(?:\?|&amp;|&)+([^=]+)(?:=([^&]*))*/g;let match,str=[];a.href=url;param=encodeURIComponent(param);while(match=regex.exec(a.search))if(param!==match[1])str.push(match[1]+(match[2]?"="+match[2]:""));str.push(param+(value?"="+encodeURIComponent(value):""));a.search=str.join("&");return a.href;};let buildQuery=function(params){let str=[];for(let p in params){if(params.hasOwnProperty(p)){str.push(encodeURIComponent(p)+"="+encodeURIComponent(params[p]));}}
@ -3757,7 +3758,7 @@ if(selected&&list[selected].dataset["selected"]){let parent=element.querySelecto
if(!buttonAlias){buttonElements.forEach(button=>{button.innerText=buttonText;button.className=buttonClass;button.type=buttonElement;if(buttonIcon){let iconElement=document.createElement("i");iconElement.className=buttonIcon;button.insertBefore(iconElement,button.firstChild);}});}
if(buttonEvent){buttonElements.forEach(button=>{button.addEventListener("click",function(){document.dispatchEvent(new CustomEvent(buttonEvent,{bubbles:false,cancelable:true}));});});}
element.classList.add("modal");if(!buttonAlias&&!buttonHide){buttonElements.forEach(button=>{element.parentNode.insertBefore(button,element);});}
let open=function(){document.documentElement.classList.add("modal-open");document.dispatchEvent(new CustomEvent("modal-open",{bubbles:false,cancelable:true}));element.classList.add("open");element.classList.remove("close");let form=element.querySelector('form');let elements=(form&&form.elements)?[...form.elements]:[];for(let index=0;index<elements.length;index++){let element=elements[index];if(element.type!=='hidden'&&element.type!=='button'&&element.type!=='submit'&&!element.disabled){element.focus();break;}}};let close=function(event){document.documentElement.classList.remove("modal-open");element.classList.add("close");element.classList.remove("open");};if(name){document.querySelectorAll("[data-ui-modal-ref='"+name+"']").forEach(function(elem){elem.addEventListener("click",open);});}
let open=function(){document.documentElement.classList.add("modal-open");document.dispatchEvent(new CustomEvent("modal-open",{bubbles:false,cancelable:true}));element.classList.add("open");element.classList.remove("close");let form=element.querySelector('form');let elements=form?form.querySelectorAll('[name]'):[];for(const element of elements){if(element.type!=='hidden'&&element.type!=='button'&&element.type!=='submit'&&!element.disabled){element.focus();break;}}};let close=function(event){document.documentElement.classList.remove("modal-open");element.classList.add("close");element.classList.remove("open");};if(name){document.querySelectorAll("[data-ui-modal-ref='"+name+"']").forEach(function(elem){elem.addEventListener("click",open);});}
if(openEvent){document.addEventListener(openEvent,open);}
buttonElements.forEach(button=>{button.addEventListener("click",open);});document.addEventListener("keydown",function(event){if(event.which===27){close();}});element.addEventListener("blur",close);let closeButtons=element.querySelectorAll("[data-ui-modal-close]");for(let i=0;i<closeButtons.length;i++){closeButtons[i].addEventListener("click",close);}
document.addEventListener("modal-close",close);element.addEventListener(closeEvent,close);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-open",controller:function(element,window){let def=element.classList.contains("open")?"open":"close";let buttonClass=element.dataset["buttonClass"]||"ls-ui-open";let buttonText=element.dataset["buttonText"]||"";let buttonIcon=element.dataset["buttonIcon"]||"";let buttonAria=element.dataset["buttonAria"]||"Open";let buttonSelector=element.dataset["buttonSelector"]||"";let hover=element.hasAttribute("data-hover");let blur=element.hasAttribute("data-blur");let button=window.document.createElement("button");let isTouch=function(){return("ontouchstart"in window||navigator.maxTouchPoints);};button.innerText=buttonText;button.className=buttonClass;button.type="button";if(buttonIcon){let icon=window.document.createElement("i");icon.className=buttonIcon;button.insertBefore(icon,button.firstChild);}

View file

@ -499,8 +499,9 @@ const highest=history.reduce((prev,curr)=>{return(curr.value>prev)?curr.value:pr
newHistory[project]=history;}
let currentSnapshot={...current};for(let index=.1;index<=1;index+=.05){let currentTransition={...currentSnapshot};for(const project in current){if(project in newHistory){let base=newHistory[project][bars-2].value;let cur=currentSnapshot[project];let offset=(cur-base)*index;currentTransition[project]=base+Math.floor(offset);}}
realtime.setCurrent(currentTransition);await sleep(250);}
realtime.setHistory(newHistory);}});window.formValidation=(form,fields)=>{const elements=form.elements;const actionHandler=(action,attribute)=>{switch(action){case"disable":elements[attribute].setAttribute("disabled",true);elements[attribute].dispatchEvent(new Event('change'));break;case"enable":elements[attribute].removeAttribute("disabled");elements[attribute].dispatchEvent(new Event('change'));break;case"unvalue":elements[attribute].value="";break;case"check":elements[attribute].value="true";break;case"uncheck":elements[attribute].value="false";break;}};for(const field in fields){for(const attribute in fields[field]){const attr=fields[field][attribute];if(Array.isArray(attr)){attr.forEach(action=>{if(elements[field].value==="true"){actionHandler(action,attribute);}})}else{const condition=attr.if.some(c=>{return elements[c].value==="true";});if(condition){for(const thenAction in attr.then){attr.then[thenAction].forEach(action=>{actionHandler(action,thenAction);});}}else{for(const elseAction in attr.else){attr.else[elseAction].forEach(action=>{actionHandler(action,elseAction);});}}}}}
form.addEventListener("reset",()=>{for(const key in fields){if(Object.hasOwnProperty.call(fields,key)){const element=form.elements[key];element.setAttribute("value","");element.removeAttribute("disabled");element.dispatchEvent(new Event("change"));}}});};(function(window){"use strict";window.ls.container.set('alerts',function(window){return{list:[],ids:0,counter:0,max:5,add:function(message,time){var scope=this;message.id=scope.ids++;message.remove=function(){scope.remove(message.id);};scope.counter++;scope.list.unshift(message);if(scope.counter>scope.max){scope.list.pop();scope.counter--;}
realtime.setHistory(newHistory);}});window.formValidation=(form,fields)=>{const elements=Array.from(form.querySelectorAll('[name]')).reduce((prev,curr)=>{if(!curr.name){return prev;}
prev[curr.name]=curr;return prev;},{});const actionHandler=(action,attribute)=>{switch(action){case"disable":elements[attribute].setAttribute("disabled",true);elements[attribute].dispatchEvent(new Event('change'));break;case"enable":elements[attribute].removeAttribute("disabled");elements[attribute].dispatchEvent(new Event('change'));break;case"unvalue":elements[attribute].value="";break;case"check":elements[attribute].value="true";break;case"uncheck":elements[attribute].value="false";break;}};for(const field in fields){for(const attribute in fields[field]){const attr=fields[field][attribute];if(Array.isArray(attr)){attr.forEach(action=>{if(elements[field].value==="true"){actionHandler(action,attribute);}})}else{const condition=attr.if.some(c=>{return elements[c].value==="true";});if(condition){for(const thenAction in attr.then){attr.then[thenAction].forEach(action=>{actionHandler(action,thenAction);});}}else{for(const elseAction in attr.else){attr.else[elseAction].forEach(action=>{actionHandler(action,elseAction);});}}}}}
form.addEventListener("reset",()=>{for(const key in fields){if(Object.hasOwnProperty.call(fields,key)){const element=elements[key];element.setAttribute("value","");element.removeAttribute("disabled");element.dispatchEvent(new Event("change"));}}});};(function(window){"use strict";window.ls.container.set('alerts',function(window){return{list:[],ids:0,counter:0,max:5,add:function(message,time){var scope=this;message.id=scope.ids++;message.remove=function(){scope.remove(message.id);};scope.counter++;scope.list.unshift(message);if(scope.counter>scope.max){scope.list.pop();scope.counter--;}
if(time>0){window.setTimeout(function(message){return function(){scope.remove(message.id)}}(message),time);}
return message.id;},remove:function(id){let scope=this;for(let index=0;index<scope.list.length;index++){let obj=scope.list[index];if(obj.id===parseInt(id)){scope.counter--;if(typeof obj.callback==="function"){obj.callback();}
scope.list.splice(index,1);};}}};},true,true);})(window);(function(window){"use strict";window.ls.container.set('appwrite',function(window,env){let config={endpoint:'https://appwrite.io/v1',};let http=function(document,env){let globalParams=[],globalHeaders=[];let addParam=function(url,param,value){let a=document.createElement('a'),regex=/(?:\?|&amp;|&)+([^=]+)(?:=([^&]*))*/g;let match,str=[];a.href=url;param=encodeURIComponent(param);while(match=regex.exec(a.search))if(param!==match[1])str.push(match[1]+(match[2]?"="+match[2]:""));str.push(param+(value?"="+encodeURIComponent(value):""));a.search=str.join("&");return a.href;};let buildQuery=function(params){let str=[];for(let p in params){if(params.hasOwnProperty(p)){str.push(encodeURIComponent(p)+"="+encodeURIComponent(params[p]));}}
@ -823,7 +824,7 @@ if(selected&&list[selected].dataset["selected"]){let parent=element.querySelecto
if(!buttonAlias){buttonElements.forEach(button=>{button.innerText=buttonText;button.className=buttonClass;button.type=buttonElement;if(buttonIcon){let iconElement=document.createElement("i");iconElement.className=buttonIcon;button.insertBefore(iconElement,button.firstChild);}});}
if(buttonEvent){buttonElements.forEach(button=>{button.addEventListener("click",function(){document.dispatchEvent(new CustomEvent(buttonEvent,{bubbles:false,cancelable:true}));});});}
element.classList.add("modal");if(!buttonAlias&&!buttonHide){buttonElements.forEach(button=>{element.parentNode.insertBefore(button,element);});}
let open=function(){document.documentElement.classList.add("modal-open");document.dispatchEvent(new CustomEvent("modal-open",{bubbles:false,cancelable:true}));element.classList.add("open");element.classList.remove("close");let form=element.querySelector('form');let elements=(form&&form.elements)?[...form.elements]:[];for(let index=0;index<elements.length;index++){let element=elements[index];if(element.type!=='hidden'&&element.type!=='button'&&element.type!=='submit'&&!element.disabled){element.focus();break;}}};let close=function(event){document.documentElement.classList.remove("modal-open");element.classList.add("close");element.classList.remove("open");};if(name){document.querySelectorAll("[data-ui-modal-ref='"+name+"']").forEach(function(elem){elem.addEventListener("click",open);});}
let open=function(){document.documentElement.classList.add("modal-open");document.dispatchEvent(new CustomEvent("modal-open",{bubbles:false,cancelable:true}));element.classList.add("open");element.classList.remove("close");let form=element.querySelector('form');let elements=form?form.querySelectorAll('[name]'):[];for(const element of elements){if(element.type!=='hidden'&&element.type!=='button'&&element.type!=='submit'&&!element.disabled){element.focus();break;}}};let close=function(event){document.documentElement.classList.remove("modal-open");element.classList.add("close");element.classList.remove("open");};if(name){document.querySelectorAll("[data-ui-modal-ref='"+name+"']").forEach(function(elem){elem.addEventListener("click",open);});}
if(openEvent){document.addEventListener(openEvent,open);}
buttonElements.forEach(button=>{button.addEventListener("click",open);});document.addEventListener("keydown",function(event){if(event.which===27){close();}});element.addEventListener("blur",close);let closeButtons=element.querySelectorAll("[data-ui-modal-close]");for(let i=0;i<closeButtons.length;i++){closeButtons[i].addEventListener("click",close);}
document.addEventListener("modal-close",close);element.addEventListener(closeEvent,close);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-open",controller:function(element,window){let def=element.classList.contains("open")?"open":"close";let buttonClass=element.dataset["buttonClass"]||"ls-ui-open";let buttonText=element.dataset["buttonText"]||"";let buttonIcon=element.dataset["buttonIcon"]||"";let buttonAria=element.dataset["buttonAria"]||"Open";let buttonSelector=element.dataset["buttonSelector"]||"";let hover=element.hasAttribute("data-hover");let blur=element.hasAttribute("data-blur");let button=window.document.createElement("button");let isTouch=function(){return("ontouchstart"in window||navigator.maxTouchPoints);};button.innerText=buttonText;button.className=buttonClass;button.type="button";if(buttonIcon){let icon=window.document.createElement("i");icon.className=buttonIcon;button.insertBefore(icon,button.firstChild);}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -124,7 +124,13 @@ window.addEventListener("load", async () => {
});
window.formValidation = (form, fields) => {
const elements = form.elements;
const elements = Array.from(form.querySelectorAll('[name]')).reduce((prev, curr) => {
if(!curr.name) {
return prev;
}
prev[curr.name] = curr;
return prev;
}, {});
const actionHandler = (action, attribute) => {
switch (action) {
case "disable":
@ -178,7 +184,7 @@ window.formValidation = (form, fields) => {
form.addEventListener("reset", () => {
for (const key in fields) {
if (Object.hasOwnProperty.call(fields, key)) {
const element = form.elements[key];
const element = elements[key];
element.setAttribute("value", "");
element.removeAttribute("disabled");
element.dispatchEvent(new Event("change"));

View file

@ -85,11 +85,9 @@
element.classList.remove("close");
let form = element.querySelector('form');
let elements = (form && form.elements) ? [...form.elements] : [];
for (let index = 0; index < elements.length; index++) {
let element = elements[index];
let elements = form ? form.querySelectorAll('[name]') : [];
for (const element of elements) {
if(element.type !== 'hidden'
&& element.type !== 'button'
&& element.type !== 'submit'

View file

@ -821,6 +821,7 @@ label.switch {
background: var(--config-color-fade);
display: inline-block;
margin: 0;
margin-bottom: 32px;
padding: 5px;
.func-padding-start(5px);
.func-padding-end(30px);

View file

@ -1377,22 +1377,9 @@ class ProjectsConsoleClientTest extends Scope
'store' => '',
'hostname' => 'localhost',
]);
$this->assertEquals(400, $response['headers']['status-code']);
// $response = $this->client->call(Client::METHOD_POST, '/projects/'.$id.'/platforms', array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()), [
// 'type' => 'web',
// 'name' => 'Web App',
// 'key' => '',
// 'store' => '',
// 'hostname' => 'https://localhost',
// ]);
// $this->assertEquals(400, $response['headers']['status-code']);
return $data;
}
@ -1402,7 +1389,9 @@ class ProjectsConsoleClientTest extends Scope
public function testListProjectPlatform($data): array
{
$id = $data['projectId'] ?? '';
sleep(1);
$response = $this->client->call(Client::METHOD_GET, '/projects/'.$id.'/platforms', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@ -1424,7 +1413,7 @@ class ProjectsConsoleClientTest extends Scope
public function testGetProjectPlatform($data): array
{
$id = $data['projectId'] ?? '';
$platformWebId = $data['platformWebId'] ?? '';
$response = $this->client->call(Client::METHOD_GET, '/projects/'.$id.'/platforms/'.$platformWebId, array_merge([

View file

@ -20,11 +20,11 @@ class CollectionsTest extends TestCase
public function testDuplicateRules()
{
foreach ($this->collections as $key => $collection) {
if (array_key_exists('rules', $collection)) {
foreach ($collection['rules'] as $check) {
if (array_key_exists('attributes', $collection)) {
foreach ($collection['attributes'] as $check) {
$occurences = 0;
foreach ($collection['rules'] as $rule) {
if ($rule['key'] == $check['key']) {
foreach ($collection['attributes'] as $attribute) {
if ($attribute['$id'] == $check['$id']) {
$occurences++;
}
}