From 3c7b62d88993fd59da8b417954c80604e2b8f7c6 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 6 Apr 2021 13:51:59 +0200 Subject: [PATCH 01/10] Update form.js --- public/scripts/services/form.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/public/scripts/services/form.js b/public/scripts/services/form.js index 636c07b36f..220f4a597e 100644 --- a/public/scripts/services/form.js +++ b/public/scripts/services/form.js @@ -42,6 +42,12 @@ let ref = json; if (name && 'FORM' !== element.tagName) { + if (name.startsWith('[')) { // Check for array names + let splitName = name.split('.'); + if (splitName.length > 1 && splitName[0].endsWith(']')) { + name = splitName[splitName.length-1]; + } + } if ('FIELDSET' === element.tagName) { // Fieldset Array / Object if (castTo === 'object') { @@ -118,4 +124,4 @@ } }, true, false); -})(window); \ No newline at end of file +})(window); From 03111a2a46dae1cc02732fbeb3d39edd4824146d Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 6 Apr 2021 13:52:49 +0200 Subject: [PATCH 02/10] Update collection.phtml --- app/views/console/database/collection.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/console/database/collection.phtml b/app/views/console/database/collection.phtml index 45d3287cf5..4a3bbf10be 100644 --- a/app/views/console/database/collection.phtml +++ b/app/views/console/database/collection.phtml @@ -520,7 +520,7 @@ $maxCells = 10;
- +
From fd5ff9ac8fa50f150476fd40a6087e0362d7f288 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 6 Apr 2021 15:40:24 +0200 Subject: [PATCH 03/10] Update form.js --- public/scripts/services/form.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/scripts/services/form.js b/public/scripts/services/form.js index 636c07b36f..b02fc13631 100644 --- a/public/scripts/services/form.js +++ b/public/scripts/services/form.js @@ -4,6 +4,10 @@ window.ls.container.set('form', function () { function cast(value, to) { + if (value && Array.isArray(value) && to !== 'array') { + value = value.map(element => cast(element, to)); + return value; + } switch (to) { case 'int': case 'integer': @@ -118,4 +122,4 @@ } }, true, false); -})(window); \ No newline at end of file +})(window); From 317a0b63246dad35f9fa2883e9e196c46ff8e9c1 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 6 Apr 2021 15:48:28 +0200 Subject: [PATCH 04/10] build scripts --- public/dist/scripts/app-all.js | 3 ++- public/dist/scripts/app.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/public/dist/scripts/app-all.js b/public/dist/scripts/app-all.js index b89f03613e..b0529118ef 100644 --- a/public/dist/scripts/app-all.js +++ b/public/dist/scripts/app-all.js @@ -2211,7 +2211,8 @@ match=text.match(new RegExp(regex,'gi')) if(!match){return fail} for(i=0,len=match.length;icast(element,to));return value;} +switch(to){case'int':case'integer':value=parseInt(value);break;case'numeric':value=Number(value);break;case'string':value=value.toString();break;case'json':value=(value)?JSON.parse(value):[];break;case'array':value=(value&&value.constructor&&value.constructor===Array)?value:[value];break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;} return value;} function toJson(element,json){json=json||{};let name=element.getAttribute('name');let type=element.getAttribute('type');let castTo=element.getAttribute('data-cast-to');let ref=json;if(name&&'FORM'!==element.tagName){if('FIELDSET'===element.tagName){if(castTo==='object'){if(json[name]===undefined){json[name]={};} ref=json[name];} diff --git a/public/dist/scripts/app.js b/public/dist/scripts/app.js index 9b4cf5070b..6861a737ae 100644 --- a/public/dist/scripts/app.js +++ b/public/dist/scripts/app.js @@ -256,7 +256,8 @@ match=text.match(new RegExp(regex,'gi')) if(!match){return fail} for(i=0,len=match.length;icast(element,to));return value;} +switch(to){case'int':case'integer':value=parseInt(value);break;case'numeric':value=Number(value);break;case'string':value=value.toString();break;case'json':value=(value)?JSON.parse(value):[];break;case'array':value=(value&&value.constructor&&value.constructor===Array)?value:[value];break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;} return value;} function toJson(element,json){json=json||{};let name=element.getAttribute('name');let type=element.getAttribute('type');let castTo=element.getAttribute('data-cast-to');let ref=json;if(name&&'FORM'!==element.tagName){if('FIELDSET'===element.tagName){if(castTo==='object'){if(json[name]===undefined){json[name]={};} ref=json[name];} From a8af10dbcaf39883e41d66827fc2afd6c92994e9 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 6 Apr 2021 18:12:32 +0200 Subject: [PATCH 05/10] build scripts --- public/dist/scripts/app-all.js | 3 ++- public/dist/scripts/app.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/public/dist/scripts/app-all.js b/public/dist/scripts/app-all.js index b89f03613e..641cc7ce1e 100644 --- a/public/dist/scripts/app-all.js +++ b/public/dist/scripts/app-all.js @@ -2213,7 +2213,8 @@ for(i=0,len=match.length;i1&&splitName[0].endsWith(']')){name=splitName[splitName.length-1];}} +if('FIELDSET'===element.tagName){if(castTo==='object'){if(json[name]===undefined){json[name]={};} ref=json[name];} else{if(!Array.isArray(json[name])){json[name]=[];} json[name].push({});ref=json[name][json[name].length-1];}} diff --git a/public/dist/scripts/app.js b/public/dist/scripts/app.js index 9b4cf5070b..3312475fee 100644 --- a/public/dist/scripts/app.js +++ b/public/dist/scripts/app.js @@ -258,7 +258,8 @@ for(i=0,len=match.length;i1&&splitName[0].endsWith(']')){name=splitName[splitName.length-1];}} +if('FIELDSET'===element.tagName){if(castTo==='object'){if(json[name]===undefined){json[name]={};} ref=json[name];} else{if(!Array.isArray(json[name])){json[name]=[];} json[name].push({});ref=json[name][json[name].length-1];}} From 3ff66179c5fb9bfcd77906475e10381f9f12f3c0 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Thu, 8 Apr 2021 10:39:23 +0200 Subject: [PATCH 06/10] feat: add document response model that extends any type --- app/config/events.php | 6 +-- app/controllers/api/database.php | 14 ++--- src/Appwrite/Utopia/Response.php | 5 +- src/Appwrite/Utopia/Response/Filters/V06.php | 1 + .../Utopia/Response/Model/Document.php | 52 +++++++++++++++++++ 5 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Model/Document.php diff --git a/app/config/events.php b/app/config/events.php index 601b502163..ed4b6da2b9 100644 --- a/app/config/events.php +++ b/app/config/events.php @@ -84,17 +84,17 @@ return [ ], 'database.documents.create' => [ 'description' => 'This event triggers when a database document is created.', - 'model' => Response::MODEL_ANY, + 'model' => Response::MODEL_DOCUMENT, 'note' => '', ], 'database.documents.update' => [ 'description' => 'This event triggers when a database document is updated.', - 'model' => Response::MODEL_ANY, + 'model' => Response::MODEL_DOCUMENT, 'note' => '', ], 'database.documents.delete' => [ 'description' => 'This event triggers when a database document is deleted.', - 'model' => Response::MODEL_ANY, + 'model' => Response::MODEL_DOCUMENT, 'note' => '', ], 'storage.files.create' => [ diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 2c32ae981e..f33b2e1351 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -294,7 +294,7 @@ App::post('/v1/database/collections/:collectionId/documents') ->label('sdk.description', '/docs/references/database/create-document.md') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_ANY) + ->label('sdk.response.model', Response::MODEL_DOCUMENT) ->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).') ->param('data', [], new JSON(), 'Document data as JSON object.') ->param('read', null, new ArrayList(new Text(64)), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true) @@ -401,7 +401,7 @@ App::post('/v1/database/collections/:collectionId/documents') $response ->setStatusCode(Response::STATUS_CODE_CREATED) - ->dynamic($data, Response::MODEL_ANY) + ->dynamic($data, Response::MODEL_DOCUMENT) ; }); @@ -478,7 +478,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId') ->label('sdk.description', '/docs/references/database/get-document.md') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_ANY) + ->label('sdk.response.model', Response::MODEL_DOCUMENT) ->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).') ->param('documentId', null, new UID(), 'Document unique ID.') ->inject('response') @@ -494,7 +494,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId') throw new Exception('No document found', 404); } - $response->dynamic($document, Response::MODEL_ANY); + $response->dynamic($document, Response::MODEL_DOCUMENT); }); App::patch('/v1/database/collections/:collectionId/documents/:documentId') @@ -508,7 +508,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') ->label('sdk.description', '/docs/references/database/update-document.md') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_ANY) + ->label('sdk.response.model', Response::MODEL_DOCUMENT) ->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).') ->param('documentId', null, new UID(), 'Document unique ID.') ->param('data', [], new JSON(), 'Document data as JSON object.') @@ -566,7 +566,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') ->setParam('data', $data->getArrayCopy()) ; - $response->dynamic($data, Response::MODEL_ANY); + $response->dynamic($data, Response::MODEL_DOCUMENT); }); App::delete('/v1/database/collections/:collectionId/documents/:documentId') @@ -614,7 +614,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId') } $events - ->setParam('payload', $response->output($document, Response::MODEL_ANY)) + ->setParam('payload', $response->output($document, Response::MODEL_DOCUMENT)) ; $audits diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index e7f7e4e84e..0bed14e45c 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -15,6 +15,7 @@ use Appwrite\Utopia\Response\Model\Collection; use Appwrite\Utopia\Response\Model\Continent; use Appwrite\Utopia\Response\Model\Country; use Appwrite\Utopia\Response\Model\Currency; +use Appwrite\Utopia\Response\Model\Document as ModelDocument; use Appwrite\Utopia\Response\Model\Domain; use Appwrite\Utopia\Response\Model\Error; use Appwrite\Utopia\Response\Model\ErrorDev; @@ -61,6 +62,7 @@ class Response extends SwooleResponse const MODEL_COLLECTION = 'collection'; const MODEL_COLLECTION_LIST = 'collectionList'; const MODEL_RULE = 'rule'; + const MODEL_DOCUMENT = 'document'; const MODEL_DOCUMENT_LIST = 'documentList'; // Users @@ -145,7 +147,7 @@ class Response extends SwooleResponse ->setModel(new ErrorDev()) // Lists ->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION)) - ->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_ANY)) + ->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_DOCUMENT)) ->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER)) ->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION)) ->setModel(new BaseList('Logs List', self::MODEL_LOG_LIST, 'logs', self::MODEL_LOG, false)) @@ -169,6 +171,7 @@ class Response extends SwooleResponse // Entities ->setModel(new Permissions()) ->setModel(new Collection()) + ->setModel(new ModelDocument()) ->setModel(new Rule()) ->setModel(new Log()) ->setModel(new User()) diff --git a/src/Appwrite/Utopia/Response/Filters/V06.php b/src/Appwrite/Utopia/Response/Filters/V06.php index 137ecbfb5a..13ee89cbb7 100644 --- a/src/Appwrite/Utopia/Response/Filters/V06.php +++ b/src/Appwrite/Utopia/Response/Filters/V06.php @@ -108,6 +108,7 @@ class V06 extends Filter { break; case Response::MODEL_ANY : + case Response::MODEL_DOCUMENT : $parsedResponse = $content; break; diff --git a/src/Appwrite/Utopia/Response/Model/Document.php b/src/Appwrite/Utopia/Response/Model/Document.php new file mode 100644 index 0000000000..a8a9f80970 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/Document.php @@ -0,0 +1,52 @@ +addRule('$id', [ + 'type' => self::TYPE_STRING, + 'description' => 'Document ID.', + 'default' => '', + 'example' => '5e5ea5c16897e', + ]) + ->addRule('$collection', [ + 'type' => self::TYPE_STRING, + 'description' => 'Collection ID.', + 'default' => '', + 'example' => '5e5ea5c15117e', + ]) + ->addRule('$permissions', [ + 'type' => Response::MODEL_PERMISSIONS, + 'description' => 'Document permissions.', + 'default' => new \stdClass, + 'example' => new \stdClass, + 'array' => false, + ]); + } +} From d8fd30cffabc296283449e2a8d2b11f2166d6e71 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Thu, 8 Apr 2021 10:39:44 +0200 Subject: [PATCH 07/10] feat: filter response models in swagger spec --- src/Appwrite/Specification/Format/Swagger2.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 4dd0406088..b91546693c 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -81,7 +81,9 @@ class Swagger2 extends Format $output['securityDefinitions']['Mode']['x-appwrite'] = ['demo' => '']; } - foreach ($this->routes as $route) { /* @var $route \Utopia\Route */ + $usedModels = []; + + foreach ($this->routes as $route) { /** @var \Utopia\Route $route */ $url = \str_replace('/v1', '', $route->getURL()); $scope = $route->getLabel('scope', ''); $hide = $route->getLabel('sdk.hide', false); @@ -148,6 +150,7 @@ class Swagger2 extends Format ], ]; } else { + $usedModels[] = $model->getType(); $temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [ 'description' => $model->getName(), 'schema' => [ @@ -232,7 +235,7 @@ class Swagger2 extends Format $node['format'] = 'format'; $node['x-example'] = 'password'; break; - case 'Utopia\Validator\Range': /* @var $validator \Utopia\Validator\Range */ + case 'Utopia\Validator\Range': /** @var \Utopia\Validator\Range $validator */ $node['type'] = 'integer'; $node['format'] = 'int32'; $node['x-example'] = $validator->getMin(); @@ -249,7 +252,7 @@ class Swagger2 extends Format $node['format'] = 'url'; $node['x-example'] = 'https://example.com'; break; - case 'Utopia\Validator\WhiteList': /* @var $validator \Utopia\Validator\WhiteList */ + case 'Utopia\Validator\WhiteList': /** @var \Utopia\Validator\WhiteList $validator */ $node['type'] = 'string'; $node['x-example'] = $validator->getList()[0]; break; @@ -310,6 +313,10 @@ class Swagger2 extends Format } foreach ($this->models as $model) { + if (!in_array($model->getType(), $usedModels)) { + continue; + } + $required = $model->getRequired(); $rules = $model->getRules(); From 8bac3caeb5cb7fca9d984ab171d9e586b1774c97 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Thu, 8 Apr 2021 10:46:45 +0200 Subject: [PATCH 08/10] feat: filter response models in openapi3 spec --- src/Appwrite/Specification/Format/OpenAPI3.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 3c62daf5f7..6b32f10057 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -83,7 +83,9 @@ class OpenAPI3 extends Format $output['components']['securitySchemes']['Mode']['x-appwrite'] = ['demo' => '']; } - foreach ($this->routes as $route) { /* @var $route \Utopia\Route */ + $usedModels = []; + + foreach ($this->routes as $route) { /** @var \Utopia\Route $route */ $url = \str_replace('/v1', '', $route->getURL()); $scope = $route->getLabel('scope', ''); $hide = $route->getLabel('sdk.hide', false); @@ -146,6 +148,7 @@ class OpenAPI3 extends Format // ], ]; } else { + $usedModels[] = $model->getType(); $temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [ 'description' => $model->getName(), 'content' => [ @@ -236,7 +239,7 @@ class OpenAPI3 extends Format $node['schema']['format'] = 'format'; $node['schema']['x-example'] = 'password'; break; - case 'Utopia\Validator\Range': /* @var $validator \Utopia\Validator\Range */ + case 'Utopia\Validator\Range': /** @var \Utopia\Validator\Range $validator */ $node['schema']['type'] = 'integer'; $node['schema']['format'] = 'int32'; $node['schema']['x-example'] = $validator->getMin(); @@ -253,7 +256,7 @@ class OpenAPI3 extends Format $node['schema']['format'] = 'url'; $node['schema']['x-example'] = 'https://example.com'; break; - case 'Utopia\Validator\WhiteList': /* @var $validator \Utopia\Validator\WhiteList */ + case 'Utopia\Validator\WhiteList': /** @var \Utopia\Validator\WhiteList $validator */ $node['schema']['type'] = 'string'; $node['schema']['x-example'] = $validator->getList()[0]; break; @@ -309,6 +312,10 @@ class OpenAPI3 extends Format } foreach ($this->models as $model) { + if (!in_array($model->getType(), $usedModels)) { + continue; + } + $required = $model->getRequired(); $rules = $model->getRules(); From ccf114120e1bef39f4ab06b3adb1253aad299cce Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Thu, 8 Apr 2021 11:24:41 +0200 Subject: [PATCH 09/10] fix: add nested models in models --- src/Appwrite/Specification/Format/OpenAPI3.php | 8 +++++++- src/Appwrite/Specification/Format/Swagger2.php | 9 ++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 6b32f10057..591fda709a 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -310,7 +310,13 @@ class OpenAPI3 extends Format $output['paths'][$url][\strtolower($route->getMethod())] = $temp; } - + foreach ($this->models as $model) { + foreach ($model->getRules() as $rule) { + if (!in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])) { + $usedModels[] = $rule['type']; + } + } + } foreach ($this->models as $model) { if (!in_array($model->getType(), $usedModels)) { continue; diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index b91546693c..0e639e9a86 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -4,6 +4,7 @@ namespace Appwrite\Specification\Format; use Appwrite\Specification\Format; use Appwrite\Template\Template; +use Appwrite\Utopia\Response\Model; use stdClass; class Swagger2 extends Format @@ -311,7 +312,13 @@ class Swagger2 extends Format $output['paths'][$url][\strtolower($route->getMethod())] = $temp; } - + foreach ($this->models as $model) { + foreach ($model->getRules() as $rule) { + if (!in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])) { + $usedModels[] = $rule['type']; + } + } + } foreach ($this->models as $model) { if (!in_array($model->getType(), $usedModels)) { continue; From 3cc08c849f04229229eec1ba2390ba18feff23d1 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Thu, 8 Apr 2021 11:27:59 +0200 Subject: [PATCH 10/10] fix: add error types --- src/Appwrite/Specification/Format/OpenAPI3.php | 2 +- src/Appwrite/Specification/Format/Swagger2.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 591fda709a..f01673d3a5 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -318,7 +318,7 @@ class OpenAPI3 extends Format } } foreach ($this->models as $model) { - if (!in_array($model->getType(), $usedModels)) { + if (!in_array($model->getType(), $usedModels) && $model->getType() !== 'error') { continue; } diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 0e639e9a86..1d92a0ea3c 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -320,7 +320,7 @@ class Swagger2 extends Format } } foreach ($this->models as $model) { - if (!in_array($model->getType(), $usedModels)) { + if (!in_array($model->getType(), $usedModels) && $model->getType() !== 'error') { continue; }