1
0
Fork 0
mirror of synced 2024-06-29 19:50:26 +12:00

Merge pull request #4346 from appwrite/master

Sync 1.1.x
This commit is contained in:
Eldad A. Fux 2022-10-10 07:27:26 +03:00 committed by GitHub
commit 0733909d04
44 changed files with 640 additions and 310 deletions

2
.env
View file

@ -56,7 +56,7 @@ _APP_SMTP_PORT=1025
_APP_SMTP_SECURE= _APP_SMTP_SECURE=
_APP_SMTP_USERNAME= _APP_SMTP_USERNAME=
_APP_SMTP_PASSWORD= _APP_SMTP_PASSWORD=
_APP_SMS_PROVIDER=sms://mock _APP_SMS_PROVIDER=sms://username:password@mock
_APP_SMS_FROM=+123456789 _APP_SMS_FROM=+123456789
_APP_STORAGE_LIMIT=30000000 _APP_STORAGE_LIMIT=30000000
_APP_STORAGE_PREVIEW_LIMIT=20000000 _APP_STORAGE_PREVIEW_LIMIT=20000000

View file

@ -4,7 +4,7 @@ on: [pull_request]
jobs: jobs:
tests: tests:
name: Unit & E2E name: Unit & E2E
runs-on: self-hosted runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
@ -23,12 +23,14 @@ jobs:
# Upstream bug causes buildkit pulls to fail so prefetch base images # Upstream bug causes buildkit pulls to fail so prefetch base images
# https://github.com/moby/moby/issues/41864 # https://github.com/moby/moby/issues/41864
run: | run: |
echo "_APP_FUNCTIONS_RUNTIMES=php-8.0" >> .env export COMPOSE_INTERACTIVE_NO_CLI
export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1
export BUILDKIT_PROGRESS=plain
docker pull composer:2.0 docker pull composer:2.0
docker pull php:8.0-cli-alpine docker compose -f docker-compose.yml -f docker-compose.ci.yml build appwrite
docker compose build --progress=plain docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d
docker compose up -d sleep 30
sleep 10
- name: Doctor - name: Doctor
run: docker compose exec -T appwrite doctor run: docker compose exec -T appwrite doctor
@ -37,10 +39,3 @@ jobs:
- name: Run Tests - name: Run Tests
run: docker compose exec -T appwrite test --debug run: docker compose exec -T appwrite test --debug
- name: Teardown
if: always()
run: |
docker ps -aq | xargs docker rm --force || true
docker volume prune --force || true
docker network prune --force || true

View file

@ -1,6 +1,18 @@
# Version 1.0.2
## Bugs
- Fixed nullable values in functions variables [#3885](https://github.com/appwrite/appwrite/pull/3885)
- Fixed migration for audit by migrating the `time` attribute [#4038](https://github.com/appwrite/appwrite/pull/4038)
- Fixed default value for creating Boolean Attribute [#4040](https://github.com/appwrite/appwrite/pull/4040)
- Fixed phone authentication code to be hashed in the internal database [#3906](https://github.com/appwrite/appwrite/pull/3906)
- Fixed `/v1/teams/:teamId/memberships/:membershipId` response [#3883](https://github.com/appwrite/appwrite/pull/3883)
- Fixed removing variables when function is deleted [#3884](https://github.com/appwrite/appwrite/pull/3884)
- Fixed scheduled function not being triggered [#3908](https://github.com/appwrite/appwrite/pull/3908)
- Fixed Phone Provider configuration [#3862](https://github.com/appwrite/appwrite/pull/3883)
- Fixed Queries with `0` values [utopia-php/database#194](https://github.com/utopia-php/database/pull/194)
# Version 1.0.1 # Version 1.0.1
## Bugs ## Bugs
- Fixed migration for abuse by migrating the `time` attribute [3839](https://github.com/appwrite/appwrite/pull/3839) - Fixed migration for abuse by migrating the `time` attribute [#3839](https://github.com/appwrite/appwrite/pull/3839)
# Version 1.0.0 # Version 1.0.0
## BREAKING CHANGES ## BREAKING CHANGES

View file

@ -62,7 +62,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \ --entrypoint="install" \
appwrite/appwrite:1.0.1 appwrite/appwrite:1.0.2
``` ```
### Windows ### Windows
@ -74,7 +74,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^ --volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^ --entrypoint="install" ^
appwrite/appwrite:1.0.1 appwrite/appwrite:1.0.2
``` ```
#### PowerShell #### PowerShell
@ -84,7 +84,7 @@ docker run -it --rm ,
--volume /var/run/docker.sock:/var/run/docker.sock , --volume /var/run/docker.sock:/var/run/docker.sock ,
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw , --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ,
--entrypoint="install" , --entrypoint="install" ,
appwrite/appwrite:1.0.1 appwrite/appwrite:1.0.2
``` ```
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。 运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。

View file

@ -14,7 +14,7 @@
<!-- [![Build Status](https://img.shields.io/travis/com/appwrite/appwrite?style=flat-square)](https://travis-ci.com/appwrite/appwrite) --> <!-- [![Build Status](https://img.shields.io/travis/com/appwrite/appwrite?style=flat-square)](https://travis-ci.com/appwrite/appwrite) -->
[![Hacktoberfest](https://img.shields.io/static/v1?label=hacktoberfest&message=friendly&color=191120&style=flat-square)](https://hacktoberfest.appwrite.io) [![Hacktoberfest](https://img.shields.io/static/v1?label=hacktoberfest&message=ready&color=191120&style=flat-square)](https://hacktoberfest.appwrite.io)
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord?r=Github) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord?r=Github)
[![Build Status](https://img.shields.io/github/workflow/status/appwrite/appwrite/Tests?label=tests&style=flat-square)](https://github.com/appwrite/appwrite/actions) [![Build Status](https://img.shields.io/github/workflow/status/appwrite/appwrite/Tests?label=tests&style=flat-square)](https://github.com/appwrite/appwrite/actions)
[![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) [![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite)
@ -75,7 +75,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \ --entrypoint="install" \
appwrite/appwrite:1.0.1 appwrite/appwrite:1.0.2
``` ```
### Windows ### Windows
@ -87,7 +87,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^ --volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^ --entrypoint="install" ^
appwrite/appwrite:1.0.1 appwrite/appwrite:1.0.2
``` ```
#### PowerShell #### PowerShell
@ -97,7 +97,7 @@ docker run -it --rm `
--volume /var/run/docker.sock:/var/run/docker.sock ` --volume /var/run/docker.sock:/var/run/docker.sock `
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
--entrypoint="install" ` --entrypoint="install" `
appwrite/appwrite:1.0.1 appwrite/appwrite:1.0.2
``` ```
Once the Docker installation completes, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after installation completes. Once the Docker installation completes, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after installation completes.

View file

@ -2,7 +2,6 @@
use Ahc\Jwt\JWT; use Ahc\Jwt\JWT;
use Appwrite\Auth\Auth; use Appwrite\Auth\Auth;
use Appwrite\SMS\Adapter\Mock;
use Appwrite\Auth\Validator\Password; use Appwrite\Auth\Validator\Password;
use Appwrite\Auth\Validator\Phone; use Appwrite\Auth\Validator\Phone;
use Appwrite\Detector\Detector; use Appwrite\Detector\Detector;
@ -930,7 +929,7 @@ App::post('/v1/account/sessions/phone')
]))); ])));
} }
$secret = (App::getEnv('_APP_SMS_PROVIDER') === 'sms://mock') ? Mock::$digits : Auth::codeGenerator(); $secret = Auth::codeGenerator();
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_PHONE); $expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_PHONE);
$token = new Document([ $token = new Document([
@ -1418,7 +1417,7 @@ App::get('/v1/account/sessions/:sessionId')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_SESSION) ->label('sdk.response.model', Response::MODEL_SESSION)
->param('sessionId', null, new UID(), 'Session ID. Use the string \'current\' to get the current device session.') ->param('sessionId', '', new UID(), 'Session ID. Use the string \'current\' to get the current device session.')
->inject('response') ->inject('response')
->inject('user') ->inject('user')
->inject('locale') ->inject('locale')
@ -1696,7 +1695,7 @@ App::delete('/v1/account/sessions/:sessionId')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->label('abuse-limit', 100) ->label('abuse-limit', 100)
->param('sessionId', null, new UID(), 'Session ID. Use the string \'current\' to delete the current device session.') ->param('sessionId', '', new UID(), 'Session ID. Use the string \'current\' to delete the current device session.')
->inject('request') ->inject('request')
->inject('response') ->inject('response')
->inject('user') ->inject('user')
@ -1769,7 +1768,7 @@ App::patch('/v1/account/sessions/:sessionId')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_SESSION) ->label('sdk.response.model', Response::MODEL_SESSION)
->label('abuse-limit', 10) ->label('abuse-limit', 10)
->param('sessionId', null, new UID(), 'Session ID. Use the string \'current\' to update the current device session.') ->param('sessionId', '', new UID(), 'Session ID. Use the string \'current\' to update the current device session.')
->inject('request') ->inject('request')
->inject('response') ->inject('response')
->inject('user') ->inject('user')
@ -2258,7 +2257,7 @@ App::post('/v1/account/verification/phone')
$isPrivilegedUser = Auth::isPrivilegedUser($roles); $isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles); $isAppUser = Auth::isAppUser($roles);
$verificationSecret = Auth::tokenGenerator(); $verificationSecret = Auth::tokenGenerator();
$secret = (App::getEnv('_APP_SMS_PROVIDER') === 'sms://mock') ? Mock::$digits : Auth::codeGenerator(); $secret = Auth::codeGenerator();
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM); $expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
$verification = new Document([ $verification = new Document([

View file

@ -89,11 +89,11 @@ function createAttribute(string $databaseId, string $collectionId, Document $att
} }
// Must throw here since dbForProject->createAttribute is performed by db worker // Must throw here since dbForProject->createAttribute is performed by db worker
if ($required && $default) { if ($required && isset($default)) {
throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required attribute'); throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required attribute');
} }
if ($array && $default) { if ($array && isset($default)) {
throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array attributes'); throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array attributes');
} }
@ -1783,7 +1783,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('databaseId', '', new UID(), 'Database ID.') ->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('key', '', new Key(), 'Index Key.') ->param('key', '', new Key(), 'Index Key.')
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')
@ -1856,7 +1856,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
->label('sdk.response.model', Response::MODEL_DOCUMENT) ->label('sdk.response.model', Response::MODEL_DOCUMENT)
->param('databaseId', '', new UID(), 'Database ID.') ->param('databaseId', '', new UID(), 'Database ID.')
->param('documentId', '', new CustomId(), 'Document ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('documentId', '', new CustomId(), 'Document ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define attributes before creating documents.') ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define attributes before creating documents.')
->param('data', [], new JSON(), 'Document data as JSON object.') ->param('data', [], new JSON(), 'Document data as JSON object.')
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default the current user is granted with all permissions. [Learn more about permissions](/docs/permissions).', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default the current user is granted with all permissions. [Learn more about permissions](/docs/permissions).', true)
->inject('response') ->inject('response')
@ -2074,8 +2074,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOCUMENT) ->label('sdk.response.model', Response::MODEL_DOCUMENT)
->param('databaseId', '', new UID(), 'Database ID.') ->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('documentId', null, new UID(), 'Document ID.') ->param('documentId', '', new UID(), 'Document ID.')
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')
->inject('mode') ->inject('mode')
@ -2137,7 +2137,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
->label('sdk.response.model', Response::MODEL_LOG_LIST) ->label('sdk.response.model', Response::MODEL_LOG_LIST)
->param('databaseId', '', new UID(), 'Database ID.') ->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.') ->param('collectionId', '', new UID(), 'Collection ID.')
->param('documentId', null, new UID(), 'Document ID.') ->param('documentId', '', new UID(), 'Document ID.')
->param('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true) ->param('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true)
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')
@ -2243,8 +2243,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOCUMENT) ->label('sdk.response.model', Response::MODEL_DOCUMENT)
->param('databaseId', '', new UID(), 'Database ID.') ->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', null, new UID(), 'Collection ID.') ->param('collectionId', '', new UID(), 'Collection ID.')
->param('documentId', null, new UID(), 'Document ID.') ->param('documentId', '', new UID(), 'Document ID.')
->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true) ->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true)
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default the current permissions are inherited. [Learn more about permissions](/docs/permissions).', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default the current permissions are inherited. [Learn more about permissions](/docs/permissions).', true)
->inject('response') ->inject('response')
@ -2376,8 +2376,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('databaseId', '', new UID(), 'Database ID.') ->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('documentId', null, new UID(), 'Document ID.') ->param('documentId', '', new UID(), 'Document ID.')
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')
->inject('events') ->inject('events')
@ -2534,7 +2534,7 @@ App::get('/v1/databases/usage')
}; };
$stats[$metric][] = [ $stats[$metric][] = [
'value' => 0, 'value' => 0,
'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
]; ];
$backfill--; $backfill--;
} }
@ -2648,7 +2648,7 @@ App::get('/v1/databases/:databaseId/usage')
}; };
$stats[$metric][] = [ $stats[$metric][] = [
'value' => 0, 'value' => 0,
'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
]; ];
$backfill--; $backfill--;
} }
@ -2763,7 +2763,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
}; };
$stats[$metric][] = [ $stats[$metric][] = [
'value' => 0, 'value' => 0,
'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
]; ];
$backfill--; $backfill--;
} }

View file

@ -71,8 +71,13 @@ App::post('/v1/functions')
->param('enabled', true, new Boolean(), 'Is function enabled?', true) ->param('enabled', true, new Boolean(), 'Is function enabled?', true)
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')
->inject('project')
->inject('user')
->inject('events') ->inject('events')
->action(function (string $functionId, string $name, array $execute, string $runtime, array $events, string $schedule, int $timeout, bool $enabled, Response $response, Database $dbForProject, Event $eventsInstance) { ->action(function (string $functionId, string $name, array $execute, string $runtime, array $events, string $schedule, int $timeout, bool $enabled, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance) {
$cron = !empty($schedule) ? new CronExpression($schedule) : null;
$next = !empty($schedule) ? DateTime::format($cron->getNextRunDate()) : null;
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId; $functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
$function = $dbForProject->createDocument('functions', new Document([ $function = $dbForProject->createDocument('functions', new Document([
@ -86,11 +91,22 @@ App::post('/v1/functions')
'schedule' => $schedule, 'schedule' => $schedule,
'scheduleUpdatedAt' => DateTime::now(), 'scheduleUpdatedAt' => DateTime::now(),
'schedulePrevious' => null, 'schedulePrevious' => null,
'scheduleNext' => null, 'scheduleNext' => $next,
'timeout' => $timeout, 'timeout' => $timeout,
'search' => implode(' ', [$functionId, $name, $runtime]) 'search' => implode(' ', [$functionId, $name, $runtime])
])); ]));
if ($next) {
// Async task reschedule
$functionEvent = new Func();
$functionEvent
->setFunction($function)
->setType('schedule')
->setUser($user)
->setProject($project)
->schedule(new \DateTime($next));
}
$eventsInstance->setParam('functionId', $function->getId()); $eventsInstance->setParam('functionId', $function->getId());
$response $response
@ -281,7 +297,7 @@ App::get('/v1/functions/:functionId/usage')
}; };
$stats[$metric][] = [ $stats[$metric][] = [
'value' => 0, 'value' => 0,
'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
]; ];
$backfill--; $backfill--;
} }
@ -384,7 +400,7 @@ App::get('/v1/functions/usage')
}; };
$stats[$metric][] = [ $stats[$metric][] = [
'value' => 0, 'value' => 0,
'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
]; ];
$backfill--; $backfill--;
} }
@ -442,11 +458,9 @@ App::put('/v1/functions/:functionId')
throw new Exception(Exception::FUNCTION_NOT_FOUND); throw new Exception(Exception::FUNCTION_NOT_FOUND);
} }
$original = $function->getAttribute('schedule', ''); $cron = !empty($schedule) ? new CronExpression($schedule) : null;
$cron = (!empty($function->getAttribute('deployment')) && !empty($schedule)) ? new CronExpression($schedule) : null; $next = !empty($schedule) ? DateTime::format($cron->getNextRunDate()) : null;
$next = (!empty($function->getAttribute('deployment')) && !empty($schedule)) ? DateTime::format($cron->getNextRunDate()) : null;
$scheduleUpdatedAt = $schedule !== $original ? DateTime::now() : $function->getAttribute('scheduleUpdatedAt');
$enabled ??= $function->getAttribute('enabled', true); $enabled ??= $function->getAttribute('enabled', true);
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [ $function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
@ -454,14 +468,14 @@ App::put('/v1/functions/:functionId')
'name' => $name, 'name' => $name,
'events' => $events, 'events' => $events,
'schedule' => $schedule, 'schedule' => $schedule,
'scheduleUpdatedAt' => $scheduleUpdatedAt, 'scheduleUpdatedAt' => DateTime::now(),
'scheduleNext' => $next, 'scheduleNext' => $next,
'timeout' => $timeout, 'timeout' => $timeout,
'enabled' => $enabled, 'enabled' => $enabled,
'search' => implode(' ', [$functionId, $name, $function->getAttribute('runtime')]), 'search' => implode(' ', [$functionId, $name, $function->getAttribute('runtime')]),
]))); ])));
if ($next && $schedule !== $original) { if ($next) {
// Async task reschedule // Async task reschedule
$functionEvent = new Func(); $functionEvent = new Func();
$functionEvent $functionEvent
@ -519,24 +533,10 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
throw new Exception(Exception::BUILD_NOT_READY); throw new Exception(Exception::BUILD_NOT_READY);
} }
$schedule = $function->getAttribute('schedule', '');
$cron = (empty($function->getAttribute('deployment')) && !empty($schedule)) ? new CronExpression($schedule) : null;
$next = (empty($function->getAttribute('deployment')) && !empty($schedule)) ? DateTime::format($cron->getNextRunDate()) : null;
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [ $function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
'deployment' => $deployment->getId(), 'deployment' => $deployment->getId()
'scheduleNext' => $next,
]))); ])));
if ($next) { // Init first schedule
$functionEvent = new Func();
$functionEvent
->setType('schedule')
->setFunction($function)
->setProject($project)
->schedule(new \DateTime($next));
}
$events $events
->setParam('functionId', $function->getId()) ->setParam('functionId', $function->getId())
->setParam('deploymentId', $deployment->getId()); ->setParam('deploymentId', $deployment->getId());
@ -1127,21 +1127,21 @@ App::post('/v1/functions/:functionId/executions')
} }
$vars = array_reduce($function['vars'] ?? [], function (array $carry, Document $var) { $vars = array_reduce($function['vars'] ?? [], function (array $carry, Document $var) {
$carry[$var->getAttribute('key')] = $var->getAttribute('value'); $carry[$var->getAttribute('key')] = $var->getAttribute('value') ?? '';
return $carry; return $carry;
}, []); }, []);
$vars = \array_merge($vars, [ $vars = \array_merge($vars, [
'APPWRITE_FUNCTION_ID' => $function->getId(), 'APPWRITE_FUNCTION_ID' => $function->getId(),
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''), 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(), 'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
'APPWRITE_FUNCTION_TRIGGER' => 'http',
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'],
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'],
'APPWRITE_FUNCTION_DATA' => $data,
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
'APPWRITE_FUNCTION_USER_ID' => $user->getId(), 'APPWRITE_FUNCTION_TRIGGER' => 'http',
'APPWRITE_FUNCTION_JWT' => $jwt, 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
'APPWRITE_FUNCTION_DATA' => $data ?? '',
'APPWRITE_FUNCTION_USER_ID' => $user->getId() ?? '',
'APPWRITE_FUNCTION_JWT' => $jwt ?? '',
]); ]);
/** Execute function */ /** Execute function */
@ -1334,7 +1334,7 @@ App::post('/v1/functions/:functionId/variables')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_VARIABLE) ->label('sdk.response.model', Response::MODEL_VARIABLE)
->param('functionId', null, new UID(), 'Function unique ID.', false) ->param('functionId', '', new UID(), 'Function unique ID.', false)
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false) ->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', false) ->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', false)
->inject('response') ->inject('response')
@ -1386,7 +1386,7 @@ App::get('/v1/functions/:functionId/variables')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_VARIABLE_LIST) ->label('sdk.response.model', Response::MODEL_VARIABLE_LIST)
->param('functionId', null, new UID(), 'Function unique ID.', false) ->param('functionId', '', new UID(), 'Function unique ID.', false)
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')
->action(function (string $functionId, Response $response, Database $dbForProject) { ->action(function (string $functionId, Response $response, Database $dbForProject) {
@ -1413,8 +1413,8 @@ App::get('/v1/functions/:functionId/variables/:variableId')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_VARIABLE) ->label('sdk.response.model', Response::MODEL_VARIABLE)
->param('functionId', null, new UID(), 'Function unique ID.', false) ->param('functionId', '', new UID(), 'Function unique ID.', false)
->param('variableId', null, new UID(), 'Variable unique ID.', false) ->param('variableId', '', new UID(), 'Variable unique ID.', false)
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')
->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject) { ->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject) {
@ -1449,8 +1449,8 @@ App::put('/v1/functions/:functionId/variables/:variableId')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_VARIABLE) ->label('sdk.response.model', Response::MODEL_VARIABLE)
->param('functionId', null, new UID(), 'Function unique ID.', false) ->param('functionId', '', new UID(), 'Function unique ID.', false)
->param('variableId', null, new UID(), 'Variable unique ID.', false) ->param('variableId', '', new UID(), 'Variable unique ID.', false)
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false) ->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', true) ->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', true)
->inject('response') ->inject('response')
@ -1501,8 +1501,8 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
->label('sdk.description', '/docs/references/functions/delete-variable.md') ->label('sdk.description', '/docs/references/functions/delete-variable.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', null, new UID(), 'Function unique ID.', false) ->param('functionId', '', new UID(), 'Function unique ID.', false)
->param('variableId', null, new UID(), 'Variable unique ID.', false) ->param('variableId', '', new UID(), 'Variable unique ID.', false)
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')
->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject) { ->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject) {

View file

@ -332,7 +332,7 @@ App::get('/v1/projects/:projectId/usage')
}; };
$stats[$metric][] = [ $stats[$metric][] = [
'value' => 0, 'value' => 0,
'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
]; ];
$backfill--; $backfill--;
} }
@ -586,7 +586,7 @@ App::post('/v1/projects/:projectId/webhooks')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_WEBHOOK) ->label('sdk.response.model', Response::MODEL_WEBHOOK)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.') ->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.')
->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.') ->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
->param('url', null, new URL(['http', 'https']), 'Webhook URL.') ->param('url', null, new URL(['http', 'https']), 'Webhook URL.')
@ -674,8 +674,8 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_WEBHOOK) ->label('sdk.response.model', Response::MODEL_WEBHOOK)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('webhookId', null, new UID(), 'Webhook unique ID.') ->param('webhookId', '', new UID(), 'Webhook unique ID.')
->inject('response') ->inject('response')
->inject('dbForConsole') ->inject('dbForConsole')
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) { ->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
@ -708,8 +708,8 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_WEBHOOK) ->label('sdk.response.model', Response::MODEL_WEBHOOK)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('webhookId', null, new UID(), 'Webhook unique ID.') ->param('webhookId', '', new UID(), 'Webhook unique ID.')
->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.') ->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.')
->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.') ->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
->param('url', null, new URL(['http', 'https']), 'Webhook URL.') ->param('url', null, new URL(['http', 'https']), 'Webhook URL.')
@ -762,8 +762,8 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_WEBHOOK) ->label('sdk.response.model', Response::MODEL_WEBHOOK)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('webhookId', null, new UID(), 'Webhook unique ID.') ->param('webhookId', '', new UID(), 'Webhook unique ID.')
->inject('response') ->inject('response')
->inject('dbForConsole') ->inject('dbForConsole')
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) { ->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
@ -800,8 +800,8 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
->label('sdk.method', 'deleteWebhook') ->label('sdk.method', 'deleteWebhook')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('webhookId', null, new UID(), 'Webhook unique ID.') ->param('webhookId', '', new UID(), 'Webhook unique ID.')
->inject('response') ->inject('response')
->inject('dbForConsole') ->inject('dbForConsole')
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) { ->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
@ -840,7 +840,7 @@ App::post('/v1/projects/:projectId/keys')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_KEY) ->label('sdk.response.model', Response::MODEL_KEY)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.') ->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('expire', null, new DatetimeValidator(), 'Expiration time in ISO 8601 format. Use null for unlimited expiration.', true) ->param('expire', null, new DatetimeValidator(), 'Expiration time in ISO 8601 format. Use null for unlimited expiration.', true)
@ -890,7 +890,7 @@ App::get('/v1/projects/:projectId/keys')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_KEY_LIST) ->label('sdk.response.model', Response::MODEL_KEY_LIST)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->inject('response') ->inject('response')
->inject('dbForConsole') ->inject('dbForConsole')
->action(function (string $projectId, Response $response, Database $dbForConsole) { ->action(function (string $projectId, Response $response, Database $dbForConsole) {
@ -922,8 +922,8 @@ App::get('/v1/projects/:projectId/keys/:keyId')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_KEY) ->label('sdk.response.model', Response::MODEL_KEY)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('keyId', null, new UID(), 'Key unique ID.') ->param('keyId', '', new UID(), 'Key unique ID.')
->inject('response') ->inject('response')
->inject('dbForConsole') ->inject('dbForConsole')
->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) { ->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) {
@ -956,8 +956,8 @@ App::put('/v1/projects/:projectId/keys/:keyId')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_KEY) ->label('sdk.response.model', Response::MODEL_KEY)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('keyId', null, new UID(), 'Key unique ID.') ->param('keyId', '', new UID(), 'Key unique ID.')
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.') ->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
->param('expire', null, new DatetimeValidator(), 'Expiration time in ISO 8601 format. Use null for unlimited expiration.', true) ->param('expire', null, new DatetimeValidator(), 'Expiration time in ISO 8601 format. Use null for unlimited expiration.', true)
@ -1002,8 +1002,8 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
->label('sdk.method', 'deleteKey') ->label('sdk.method', 'deleteKey')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('keyId', null, new UID(), 'Key unique ID.') ->param('keyId', '', new UID(), 'Key unique ID.')
->inject('response') ->inject('response')
->inject('dbForConsole') ->inject('dbForConsole')
->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) { ->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) {
@ -1042,7 +1042,7 @@ App::post('/v1/projects/:projectId/platforms')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PLATFORM) ->label('sdk.response.model', Response::MODEL_PLATFORM)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('type', null, new WhiteList([Origin::CLIENT_TYPE_WEB, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_LINUX, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_APPLE_IOS, Origin::CLIENT_TYPE_APPLE_MACOS, Origin::CLIENT_TYPE_APPLE_WATCHOS, Origin::CLIENT_TYPE_APPLE_TVOS, Origin::CLIENT_TYPE_ANDROID, Origin::CLIENT_TYPE_UNITY], true), 'Platform type.') ->param('type', null, new WhiteList([Origin::CLIENT_TYPE_WEB, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_LINUX, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_APPLE_IOS, Origin::CLIENT_TYPE_APPLE_MACOS, Origin::CLIENT_TYPE_APPLE_WATCHOS, Origin::CLIENT_TYPE_APPLE_TVOS, Origin::CLIENT_TYPE_ANDROID, Origin::CLIENT_TYPE_UNITY], true), 'Platform type.')
->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.') ->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.')
->param('key', '', new Text(256), 'Package name for Android or bundle ID for iOS or macOS. Max length: 256 chars.', true) ->param('key', '', new Text(256), 'Package name for Android or bundle ID for iOS or macOS. Max length: 256 chars.', true)
@ -1124,8 +1124,8 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PLATFORM) ->label('sdk.response.model', Response::MODEL_PLATFORM)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('platformId', null, new UID(), 'Platform unique ID.') ->param('platformId', '', new UID(), 'Platform unique ID.')
->inject('response') ->inject('response')
->inject('dbForConsole') ->inject('dbForConsole')
->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) { ->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) {
@ -1158,8 +1158,8 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PLATFORM) ->label('sdk.response.model', Response::MODEL_PLATFORM)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('platformId', null, new UID(), 'Platform unique ID.') ->param('platformId', '', new UID(), 'Platform unique ID.')
->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.') ->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.')
->param('key', '', new Text(256), 'Package name for android or bundle ID for iOS. Max length: 256 chars.', true) ->param('key', '', new Text(256), 'Package name for android or bundle ID for iOS. Max length: 256 chars.', true)
->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true) ->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true)
@ -1205,8 +1205,8 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
->label('sdk.method', 'deletePlatform') ->label('sdk.method', 'deletePlatform')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('platformId', null, new UID(), 'Platform unique ID.') ->param('platformId', '', new UID(), 'Platform unique ID.')
->inject('response') ->inject('response')
->inject('dbForConsole') ->inject('dbForConsole')
->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) { ->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) {
@ -1245,7 +1245,7 @@ App::post('/v1/projects/:projectId/domains')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOMAIN) ->label('sdk.response.model', Response::MODEL_DOMAIN)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('domain', null, new DomainValidator(), 'Domain name.') ->param('domain', null, new DomainValidator(), 'Domain name.')
->inject('response') ->inject('response')
->inject('dbForConsole') ->inject('dbForConsole')
@ -1342,8 +1342,8 @@ App::get('/v1/projects/:projectId/domains/:domainId')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOMAIN) ->label('sdk.response.model', Response::MODEL_DOMAIN)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('domainId', null, new UID(), 'Domain unique ID.') ->param('domainId', '', new UID(), 'Domain unique ID.')
->inject('response') ->inject('response')
->inject('dbForConsole') ->inject('dbForConsole')
->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole) { ->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole) {
@ -1376,8 +1376,8 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOMAIN) ->label('sdk.response.model', Response::MODEL_DOMAIN)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('domainId', null, new UID(), 'Domain unique ID.') ->param('domainId', '', new UID(), 'Domain unique ID.')
->inject('response') ->inject('response')
->inject('dbForConsole') ->inject('dbForConsole')
->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole) { ->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole) {
@ -1435,8 +1435,8 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
->label('sdk.method', 'deleteDomain') ->label('sdk.method', 'deleteDomain')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('projectId', null, new UID(), 'Project unique ID.') ->param('projectId', '', new UID(), 'Project unique ID.')
->param('domainId', null, new UID(), 'Domain unique ID.') ->param('domainId', '', new UID(), 'Domain unique ID.')
->inject('response') ->inject('response')
->inject('dbForConsole') ->inject('dbForConsole')
->inject('deletes') ->inject('deletes')

View file

@ -346,7 +346,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE) ->label('sdk.response.model', Response::MODEL_FILE)
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('fileId', '', new CustomId(), 'File ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('fileId', '', new CustomId(), 'File ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('file', [], new File(), 'Binary file.', false) ->param('file', [], new File(), 'Binary file.', false)
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission strings. By default the current user is granted with all permissions. [Learn more about permissions](/docs/permissions).', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission strings. By default the current user is granted with all permissions. [Learn more about permissions](/docs/permissions).', true)
@ -667,7 +667,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE_LIST) ->label('sdk.response.model', Response::MODEL_FILE_LIST)
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('queries', [], new Files(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Files::ALLOWED_ATTRIBUTES), true) ->param('queries', [], new Files(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Files::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response') ->inject('response')
@ -744,7 +744,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE) ->label('sdk.response.model', Response::MODEL_FILE)
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID.') ->param('fileId', '', new UID(), 'File ID.')
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')
@ -793,7 +793,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE) ->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE)
->label('sdk.methodType', 'location') ->label('sdk.methodType', 'location')
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID') ->param('fileId', '', new UID(), 'File ID')
->param('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true) ->param('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true)
->param('height', 0, new Range(0, 4000), 'Resize preview image height, Pass an integer between 0 to 4000.', true) ->param('height', 0, new Range(0, 4000), 'Resize preview image height, Pass an integer between 0 to 4000.', true)
@ -959,7 +959,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', '*/*') ->label('sdk.response.type', '*/*')
->label('sdk.methodType', 'location') ->label('sdk.methodType', 'location')
->param('bucketId', null, new UID(), 'Storage bucket ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('bucketId', '', new UID(), 'Storage bucket ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID.') ->param('fileId', '', new UID(), 'File ID.')
->inject('request') ->inject('request')
->inject('response') ->inject('response')
@ -1099,7 +1099,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', '*/*') ->label('sdk.response.type', '*/*')
->label('sdk.methodType', 'location') ->label('sdk.methodType', 'location')
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID.') ->param('fileId', '', new UID(), 'File ID.')
->inject('response') ->inject('response')
->inject('request') ->inject('request')
@ -1256,7 +1256,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE) ->label('sdk.response.model', Response::MODEL_FILE)
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File unique ID.') ->param('fileId', '', new UID(), 'File unique ID.')
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission string. By default the current permissions are inherited. [Learn more about permissions](/docs/permissions).', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission string. By default the current permissions are inherited. [Learn more about permissions](/docs/permissions).', true)
->inject('response') ->inject('response')
@ -1358,7 +1358,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
->label('sdk.description', '/docs/references/storage/delete-file.md') ->label('sdk.description', '/docs/references/storage/delete-file.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID.') ->param('fileId', '', new UID(), 'File ID.')
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')
@ -1518,7 +1518,7 @@ App::get('/v1/storage/usage')
}; };
$stats[$metric][] = [ $stats[$metric][] = [
'value' => 0, 'value' => 0,
'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
]; ];
$backfill--; $backfill--;
} }
@ -1629,7 +1629,7 @@ App::get('/v1/storage/:bucketId/usage')
}; };
$stats[$metric][] = [ $stats[$metric][] = [
'value' => 0, 'value' => 0,
'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
]; ];
$backfill--; $backfill--;
} }

View file

@ -553,7 +553,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
->label('sdk.description', '/docs/references/teams/get-team-member.md') ->label('sdk.description', '/docs/references/teams/get-team-member.md')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MEMBERSHIP_LIST) ->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
->param('teamId', '', new UID(), 'Team ID.') ->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.') ->param('membershipId', '', new UID(), 'Membership ID.')
->inject('response') ->inject('response')
@ -865,7 +865,7 @@ App::get('/v1/teams/:teamId/logs')
->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_LOG_LIST) ->label('sdk.response.model', Response::MODEL_LOG_LIST)
->param('teamId', null, new UID(), 'Team ID.') ->param('teamId', '', new UID(), 'Team ID.')
->param('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true) ->param('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true)
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')

View file

@ -290,10 +290,10 @@ App::post('/v1/users/scrypt')
->param('email', '', new Email(), 'User email.') ->param('email', '', new Email(), 'User email.')
->param('password', '', new Password(), 'User password hashed using Scrypt.') ->param('password', '', new Password(), 'User password hashed using Scrypt.')
->param('passwordSalt', '', new Text(128), 'Optional salt used to hash password.') ->param('passwordSalt', '', new Text(128), 'Optional salt used to hash password.')
->param('passwordCpu', '', new Integer(), 'Optional CPU cost used to hash password.') ->param('passwordCpu', 8, new Integer(), 'Optional CPU cost used to hash password.')
->param('passwordMemory', '', new Integer(), 'Optional memory cost used to hash password.') ->param('passwordMemory', 14, new Integer(), 'Optional memory cost used to hash password.')
->param('passwordParallel', '', new Integer(), 'Optional parallelization cost used to hash password.') ->param('passwordParallel', 1, new Integer(), 'Optional parallelization cost used to hash password.')
->param('passwordLength', '', new Integer(), 'Optional hash length used to hash password.') ->param('passwordLength', 64, new Integer(), 'Optional hash length used to hash password.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')
@ -981,7 +981,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('userId', '', new UID(), 'User ID.') ->param('userId', '', new UID(), 'User ID.')
->param('sessionId', null, new UID(), 'Session ID.') ->param('sessionId', '', new UID(), 'Session ID.')
->inject('response') ->inject('response')
->inject('dbForProject') ->inject('dbForProject')
->inject('events') ->inject('events')
@ -1176,7 +1176,7 @@ App::get('/v1/users/usage')
}; };
$stats[$metric][] = [ $stats[$metric][] = [
'value' => 0, 'value' => 0,
'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
]; ];
$backfill--; $backfill--;
} }

View file

@ -94,8 +94,8 @@ const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 500; const APP_CACHE_BUSTER = 501;
const APP_VERSION_STABLE = '1.0.1'; const APP_VERSION_STABLE = '1.0.2';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
const APP_DATABASE_ATTRIBUTE_IP = 'ip'; const APP_DATABASE_ATTRIBUTE_IP = 'ip';
@ -1029,7 +1029,7 @@ App::setResource('sms', function () {
$secret = $dsn->getPassword(); $secret = $dsn->getPassword();
return match ($dsn->getHost()) { return match ($dsn->getHost()) {
'mock' => new Mock('', ''), // used for tests 'mock' => new Mock($user, $secret), // used for tests
'twilio' => new Twilio($user, $secret), 'twilio' => new Twilio($user, $secret),
'text-magic' => new TextMagic($user, $secret), 'text-magic' => new TextMagic($user, $secret),
'telesign' => new Telesign($user, $secret), 'telesign' => new Telesign($user, $secret),

View file

@ -81,6 +81,8 @@ $escapedPermissions = \array_map(function ($perm) {
list="types" list="types"
type="text" type="text"
x-model="permission.role" x-model="permission.role"
@keydown.enter="prevent($event)"
@keydown="clearPermission(index)"
@keyup="updatePermission(index)"/> @keyup="updatePermission(index)"/>
</td> </td>
<?php foreach ($escapedPermissions as $permission): ?> <?php foreach ($escapedPermissions as $permission): ?>

View file

@ -391,9 +391,9 @@ sort($patterns);
<tr> <tr>
<th width="30"></th> <th width="30"></th>
<th width="160">Created</th> <th width="160">Created</th>
<th width="150">Status</th> <th width="100">Status</th>
<th width="120">Trigger</th> <th width="80">Trigger</th>
<th width="80">Runtime</th> <th width="60">Runtime</th>
<th width=""></th> <th width=""></th>
</tr> </tr>
</thead> </thead>
@ -422,11 +422,11 @@ sort($patterns);
<td data-title=""> <td data-title="">
<div data-ls-if="{{execution.status}} === 'completed' || {{execution.status}} === 'failed'" data-title="" style="display: flex;"> <div data-ls-if="{{execution.status}} === 'completed' || {{execution.status}} === 'failed'" data-title="" style="display: flex;">
<button class="desktops-only pull-end link margin-start text-danger" data-ls-ui-trigger="execution-stderr-{{execution.$id}}">Stderr</button> <button class="desktops-only pull-end link margin-start text-danger" data-ls-ui-trigger="execution-stderr-{{execution.$id}}">Stderr</button>
<!--button class="desktops-only pull-end link margin-start" data-ls-ui-trigger="execution-stdout-{{execution.$id}}">Stdout</button--> <button class="desktops-only pull-end link margin-start" data-ls-ui-trigger="execution-stdout-{{execution.$id}}">Stdout</button>
<button class="desktops-only pull-end link margin-start" data-ls-ui-trigger="execution-response-{{execution.$id}}">Response</button> <button class="desktops-only pull-end link margin-start" data-ls-ui-trigger="execution-response-{{execution.$id}}">Response</button>
<button class="phones-only-inline tablets-only-inline link margin-end-small" data-ls-ui-trigger="execution-response-{{execution.$id}}">Response</button> <button class="phones-only-inline tablets-only-inline link margin-end-small" data-ls-ui-trigger="execution-response-{{execution.$id}}">Response</button>
<!--button class="phones-only-inline tablets-only-inline link margin-end-small" data-ls-ui-trigger="execution-stdout-{{execution.$id}}">Stdout</button--> <button class="phones-only-inline tablets-only-inline link margin-end-small" data-ls-ui-trigger="execution-stdout-{{execution.$id}}">Stdout</button>
<button class="phones-only-inline tablets-only-inline link text-danger" data-ls-ui-trigger="execution-stderr-{{execution.$id}}">Stderr</button> <button class="phones-only-inline tablets-only-inline link text-danger" data-ls-ui-trigger="execution-stderr-{{execution.$id}}">Stderr</button>
<div data-ui-modal class="modal width-large box close" data-button-alias="none" data-open-event="execution-response-{{execution.$id}}"> <div data-ui-modal class="modal width-large box close" data-button-alias="none" data-open-event="execution-response-{{execution.$id}}">
@ -841,6 +841,8 @@ sort($patterns);
data-failure-param-alert-text="Failed to execute function" data-failure-param-alert-text="Failed to execute function"
data-failure-param-alert-classname="error"> data-failure-param-alert-classname="error">
<input name="async" data-cast-to="bool" value="true" type="hidden" />
<label for="execution-data">Custom Data</label> <label for="execution-data">Custom Data</label>
<textarea id="execution-data" name="data" autocomplete="off" class="margin-bottom" placeholder="Data string (optional)"></textarea> <textarea id="execution-data" name="data" autocomplete="off" class="margin-bottom" placeholder="Data string (optional)"></textarea>

View file

@ -109,6 +109,7 @@ class BuildsV1 extends Worker
/** Trigger Webhook */ /** Trigger Webhook */
$deploymentModel = new Deployment(); $deploymentModel = new Deployment();
$deploymentUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME); $deploymentUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME);
$deploymentUpdate $deploymentUpdate
->setProject($project) ->setProject($project)

View file

@ -1,6 +1,7 @@
<?php <?php
use Appwrite\Event\Event; use Appwrite\Event\Event;
use Appwrite\Event\Mail;
use Appwrite\Network\Validator\CNAME; use Appwrite\Network\Validator\CNAME;
use Appwrite\Resque\Worker; use Appwrite\Resque\Worker;
use Utopia\App; use Utopia\App;
@ -374,19 +375,19 @@ class CertificatesV1 extends Worker
Console::warning('Cannot renew domain (' . $domain . ') on attempt no. ' . $attempt . ' certificate: ' . $errorMessage); Console::warning('Cannot renew domain (' . $domain . ') on attempt no. ' . $attempt . ' certificate: ' . $errorMessage);
// Send mail to administratore mail // Send mail to administratore mail
Resque::enqueue(Event::MAILS_QUEUE_NAME, Event::MAILS_CLASS_NAME, [ $mail = new Mail();
'from' => 'console', $mail
'project' => 'console', ->setType(MAIL_TYPE_CERTIFICATE)
'name' => 'Appwrite Administrator', ->setRecipient(App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'))
'recipient' => App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'), ->setUrl('https://' . $domain)
'url' => 'https://' . $domain, ->setLocale(App::getEnv('_APP_LOCALE', 'en'))
'locale' => App::getEnv('_APP_LOCALE', 'en'), ->setName('Appwrite Administrator')
'type' => MAIL_TYPE_CERTIFICATE, ->setPayload([
'domain' => $domain,
'domain' => $domain, 'error' => $errorMessage,
'error' => $errorMessage, 'attempt' => $attempt
'attempt' => $attempt ])
]); ->trigger();
} }
/** /**

View file

@ -410,6 +410,14 @@ class DeletesV1 extends Worker
$dbForProject = $this->getProjectDB($projectId); $dbForProject = $this->getProjectDB($projectId);
$functionId = $document->getId(); $functionId = $document->getId();
/**
* Delete Variables
*/
Console::info("Deleting variables for function " . $functionId);
$this->deleteByGroup('variables', [
Query::equal('functionId', [$functionId])
], $dbForProject);
/** /**
* Delete Deployments * Delete Deployments
*/ */

View file

@ -271,17 +271,17 @@ class FunctionsV1 extends Worker
/** Collect environment variables */ /** Collect environment variables */
$vars = \array_merge($vars, [ $vars = \array_merge($vars, [
'APPWRITE_FUNCTION_ID' => $functionId, 'APPWRITE_FUNCTION_ID' => $functionId,
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''), 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deploymentId, 'APPWRITE_FUNCTION_DEPLOYMENT' => $deploymentId,
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'],
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'],
'APPWRITE_FUNCTION_TRIGGER' => $trigger, 'APPWRITE_FUNCTION_TRIGGER' => $trigger,
'APPWRITE_FUNCTION_EVENT' => $event,
'APPWRITE_FUNCTION_EVENT_DATA' => $eventData,
'APPWRITE_FUNCTION_DATA' => $data,
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
'APPWRITE_FUNCTION_USER_ID' => $user->getId(), 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
'APPWRITE_FUNCTION_JWT' => $jwt, 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
'APPWRITE_FUNCTION_EVENT' => $event ?? '',
'APPWRITE_FUNCTION_EVENT_DATA' => $eventData ?? '',
'APPWRITE_FUNCTION_DATA' => $data ?? '',
'APPWRITE_FUNCTION_USER_ID' => $user->getId() ?? '',
'APPWRITE_FUNCTION_JWT' => $jwt ?? '',
]); ]);
/** Execute function */ /** Execute function */

View file

@ -32,9 +32,10 @@ class MailsV1 extends Worker
return; return;
} }
$project = new Document($this->args['project']); $project = new Document($this->args['project'] ?? []);
$user = new Document($this->args['user'] ?? []); $user = new Document($this->args['user'] ?? []);
$team = new Document($this->args['team'] ?? []); $team = new Document($this->args['team'] ?? []);
$payload = $this->args['payload'] ?? [];
$recipient = $this->args['recipient']; $recipient = $this->args['recipient'];
$url = $this->args['url']; $url = $this->args['url'];
@ -42,20 +43,20 @@ class MailsV1 extends Worker
$type = $this->args['type']; $type = $this->args['type'];
$prefix = $this->getPrefix($type); $prefix = $this->getPrefix($type);
$locale = new Locale($this->args['locale']); $locale = new Locale($this->args['locale']);
$projectName = $project->getAttribute('name', '[APP-NAME]'); $projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]');
if (!$this->doesLocaleExist($locale, $prefix)) { if (!$this->doesLocaleExist($locale, $prefix)) {
$locale->setDefault('en'); $locale->setDefault('en');
} }
$from = $project->getId() === 'console' ? '' : \sprintf($locale->getText('emails.sender'), $projectName); $from = $project->isEmpty() || $project->getId() === 'console' ? '' : \sprintf($locale->getText('emails.sender'), $projectName);
$body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl'); $body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl');
$subject = ''; $subject = '';
switch ($type) { switch ($type) {
case MAIL_TYPE_CERTIFICATE: case MAIL_TYPE_CERTIFICATE:
$domain = $this->args['domain']; $domain = $payload['domain'];
$error = $this->args['error']; $error = $payload['error'];
$attempt = $this->args['attempt']; $attempt = $payload['attempt'];
$subject = \sprintf($locale->getText("$prefix.subject"), $domain); $subject = \sprintf($locale->getText("$prefix.subject"), $domain);
$body->setParam('{{domain}}', $domain); $body->setParam('{{domain}}', $domain);

View file

@ -1,6 +1,5 @@
<?php <?php
use Appwrite\Auth\SMS;
use Appwrite\SMS\Adapter\Mock; use Appwrite\SMS\Adapter\Mock;
use Appwrite\SMS\Adapter\Telesign; use Appwrite\SMS\Adapter\Telesign;
use Appwrite\SMS\Adapter\TextMagic; use Appwrite\SMS\Adapter\TextMagic;
@ -9,6 +8,7 @@ use Appwrite\SMS\Adapter\Msg91;
use Appwrite\SMS\Adapter\Vonage; use Appwrite\SMS\Adapter\Vonage;
use Appwrite\DSN\DSN; use Appwrite\DSN\DSN;
use Appwrite\Resque\Worker; use Appwrite\Resque\Worker;
use Appwrite\SMS\Adapter;
use Utopia\App; use Utopia\App;
use Utopia\CLI\Console; use Utopia\CLI\Console;
@ -19,7 +19,7 @@ Console::success(APP_NAME . ' messaging worker v1 has started' . "\n");
class MessagingV1 extends Worker class MessagingV1 extends Worker
{ {
protected ?SMS $sms = null; protected ?Adapter $sms = null;
protected ?string $from = null; protected ?string $from = null;
public function getName(): string public function getName(): string
@ -34,7 +34,7 @@ class MessagingV1 extends Worker
$secret = $dsn->getPassword(); $secret = $dsn->getPassword();
$this->sms = match ($dsn->getHost()) { $this->sms = match ($dsn->getHost()) {
'mock' => new Mock('', ''), // used for tests 'mock' => new Mock($user, $secret), // used for tests
'twilio' => new Twilio($user, $secret), 'twilio' => new Twilio($user, $secret),
'text-magic' => new TextMagic($user, $secret), 'text-magic' => new TextMagic($user, $secret),
'telesign' => new Telesign($user, $secret), 'telesign' => new Telesign($user, $secret),

12
composer.lock generated
View file

@ -2060,16 +2060,16 @@
}, },
{ {
"name": "utopia-php/database", "name": "utopia-php/database",
"version": "0.25.4", "version": "0.25.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/database.git", "url": "https://github.com/utopia-php/database.git",
"reference": "2883de82eee99e5744bf6e4123095a530c48a194" "reference": "6d1c1d46d66553154975a3e8e72d30b5bd2413d9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/2883de82eee99e5744bf6e4123095a530c48a194", "url": "https://api.github.com/repos/utopia-php/database/zipball/6d1c1d46d66553154975a3e8e72d30b5bd2413d9",
"reference": "2883de82eee99e5744bf6e4123095a530c48a194", "reference": "6d1c1d46d66553154975a3e8e72d30b5bd2413d9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2118,9 +2118,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/database/issues", "issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.25.4" "source": "https://github.com/utopia-php/database/tree/0.25.5"
}, },
"time": "2022-09-14T06:22:33+00:00" "time": "2022-09-30T15:01:32+00:00"
}, },
{ {
"name": "utopia-php/domains", "name": "utopia-php/domains",

48
docker-compose.ci.yml Normal file
View file

@ -0,0 +1,48 @@
services:
appwrite:
image: appwrite-dev
appwrite-realtime:
image: appwrite-dev
appwrite-worker-audits:
image: appwrite-dev
appwrite-worker-webhooks:
image: appwrite-dev
appwrite-worker-deletes:
image: appwrite-dev
appwrite-worker-databases:
image: appwrite-dev
appwrite-worker-builds:
image: appwrite-dev
appwrite-worker-certificates:
image: appwrite-dev
appwrite-worker-functions:
image: appwrite-dev
appwrite-executor:
image: appwrite-dev
appwrite-worker-mails:
image: appwrite-dev
appwrite-worker-messaging:
image: appwrite-dev
appwrite-maintenance:
image: appwrite-dev
appwrite-usage-timeseries:
image: appwrite-dev
appwrite-usage-database:
image: appwrite-dev
appwrite-schedule:
image: appwrite-dev

View file

@ -73,9 +73,9 @@ services:
build: build:
context: . context: .
args: args:
- DEBUG=false DEBUG: false
- TESTING=true TESTING: true
- VERSION=dev VERSION: dev
ports: ports:
- 9501:80 - 9501:80
networks: networks:

View file

@ -1,6 +1,11 @@
## 7.0.0-dev.2 ## 7.1.0
* Role helper update
## 7.0.0
### NEW ### NEW
* Support for Appwrite 1.0.0-RC1 * Support for Appwrite 1.0.0
* More verbose headers have been included in the Clients - `x-sdk-name`, `x-sdk-platform`, `x-sdk-language`, `x-sdk-version` * More verbose headers have been included in the Clients - `x-sdk-name`, `x-sdk-platform`, `x-sdk-language`, `x-sdk-version`
* Helper classes and methods for Permissions, Roles and IDs * Helper classes and methods for Permissions, Roles and IDs
* Helper methods to suport new queries * Helper methods to suport new queries
@ -26,7 +31,7 @@
3. `greater` renamed to `greaterThan` 3. `greater` renamed to `greaterThan`
4. `greaterEqual` renamed to `greaterThanEqual` 4. `greaterEqual` renamed to `greaterThanEqual`
**Full Changelog for Appwrite 1.0.0-RC1 can be found here**: https://github.com/appwrite/appwrite/blob/master/CHANGES.md **Full Changelog for Appwrite 1.0.0 can be found here**: https://github.com/appwrite/appwrite/blob/master/CHANGES.md
## 6.0.1 ## 6.0.1
* Dependency upgrades * Dependency upgrades

View file

@ -1,7 +1,11 @@
## 8.0.0-dev.2 Latest ## 8.1.0
* Role helper update
## 8.0.0
### NEW ### NEW
* Support for Appwrite 1.0.0-RC1 * Support for Appwrite 1.0.0
* More verbose headers have been included in the Clients - `x-sdk-name`, `x-sdk-platform`, `x-sdk-language`, `x-sdk-version` * More verbose headers have been included in the Clients - `x-sdk-name`, `x-sdk-platform`, `x-sdk-language`, `x-sdk-version`
* Helper classes and methods for Permissions, Roles and IDs * Helper classes and methods for Permissions, Roles and IDs
* Helper methods to suport new queries * Helper methods to suport new queries
@ -21,7 +25,7 @@
4. `greaterEqual` renamed to `greaterThanEqual` 4. `greaterEqual` renamed to `greaterThanEqual`
* `User` response model is now renamed to `Account` * `User` response model is now renamed to `Account`
**Full Changelog for Appwrite 1.0.0-RC1 can be found here**: **Full Changelog for Appwrite 1.0.0 can be found here**:
https://github.com/appwrite/appwrite/blob/master/CHANGES.md https://github.com/appwrite/appwrite/blob/master/CHANGES.md
## 7.0.0 ## 7.0.0

View file

@ -4017,7 +4017,7 @@ this.rawPermissions=permissions;permissions.map(p=>{let{type,role}=this.parsePer
if(existing===undefined){let newPermission={role,create:false,read:false,update:false,xdelete:false,};newPermission[type]=true;this.permissions.push(newPermission);} if(existing===undefined){let newPermission={role,create:false,read:false,update:false,xdelete:false,};newPermission[type]=true;this.permissions.push(newPermission);}
if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId){if(this.permissions.length>0&&!this.validate(formId,this.permissions.length-1)){return;} if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId){if(this.permissions.length>0&&!this.validate(formId,this.permissions.length-1)){return;}
this.permissions.push({role:'',create:false,read:false,update:false,xdelete:false,});},updatePermission(index){setTimeout(()=>{const permission=this.permissions[index];Object.keys(permission).forEach(key=>{if(key==='role'){return;} this.permissions.push({role:'',create:false,read:false,update:false,xdelete:false,});},updatePermission(index){setTimeout(()=>{const permission=this.permissions[index];Object.keys(permission).forEach(key=>{if(key==='role'){return;}
const parsedKey=this.parseOutputPermission(key);const permissionString=this.buildPermission(parsedKey,permission.role);if(permission[key]){if(!this.rawPermissions.includes(permissionString)){this.rawPermissions.push(permissionString);}}else{this.rawPermissions=this.rawPermissions.filter(p=>{return!p.includes(permissionString);});}});});},removePermission(index){let row=this.permissions.splice(index,1);if(row.length===1){this.rawPermissions=this.rawPermissions.filter(p=>!p.includes(row[0].role));}},parsePermission(permission){let parts=permission.split('(');let type=parts[0];let role=parts[1].replace(')','').replace(' ','').replaceAll('"','');return{type,role};},buildPermission(type,role){return`${type}("${role}")`},parseInputPermission(key){if(key==='delete'){return'xdelete';} const parsedKey=this.parseOutputPermission(key);const permissionString=this.buildPermission(parsedKey,permission.role);if(permission[key]){if(!this.rawPermissions.includes(permissionString)){this.rawPermissions.push(permissionString);}}else{this.rawPermissions=this.rawPermissions.filter(p=>{return!p.includes(permissionString);});}});});},clearPermission(index){let currentRole=this.permissions[index].role;this.rawPermissions=this.rawPermissions.filter(p=>{let{type,role}=this.parsePermission(p);return role!==currentRole;});},removePermission(index){let row=this.permissions.splice(index,1);if(row.length===1){this.rawPermissions=this.rawPermissions.filter(p=>!p.includes(row[0].role));}},parsePermission(permission){let parts=permission.split('(');let type=parts[0];let role=parts[1].replace(')','').replace(' ','').replaceAll('"','');return{type,role};},buildPermission(type,role){return`${type}("${role}")`},parseInputPermission(key){if(key==='delete'){return'xdelete';}
return key;},parseOutputPermission(key){if(key==='xdelete'){return'delete';} return key;},parseOutputPermission(key){if(key==='xdelete'){return'delete';}
return key;},validate(formId,index){const form=document.getElementById(formId);const input=document.getElementById(`${formId}Input${index}`);const permission=this.permissions[index];input.setCustomValidity('');if(permission.role===''){input.setCustomValidity('Role is required');}else if(!Object.entries(permission).some(([k,v])=>!k.includes('role')&&v)){input.setCustomValidity('No permissions selected');}else if(this.permissions.some(p=>p.role===permission.role&&p!==permission)){input.setCustomValidity('Role entry already exists');} return key;},validate(formId,index){const form=document.getElementById(formId);const input=document.getElementById(`${formId}Input${index}`);const permission=this.permissions[index];input.setCustomValidity('');if(permission.role===''){input.setCustomValidity('Role is required');}else if(!Object.entries(permission).some(([k,v])=>!k.includes('role')&&v)){input.setCustomValidity('No permissions selected');}else if(this.permissions.some(p=>p.role===permission.role&&p!==permission)){input.setCustomValidity('Role entry already exists');}
return form.reportValidity();},prevent(event){event.preventDefault();event.stopPropagation();}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();} return form.reportValidity();},prevent(event){event.preventDefault();event.stopPropagation();}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}

View file

@ -607,7 +607,7 @@ this.rawPermissions=permissions;permissions.map(p=>{let{type,role}=this.parsePer
if(existing===undefined){let newPermission={role,create:false,read:false,update:false,xdelete:false,};newPermission[type]=true;this.permissions.push(newPermission);} if(existing===undefined){let newPermission={role,create:false,read:false,update:false,xdelete:false,};newPermission[type]=true;this.permissions.push(newPermission);}
if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId){if(this.permissions.length>0&&!this.validate(formId,this.permissions.length-1)){return;} if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId){if(this.permissions.length>0&&!this.validate(formId,this.permissions.length-1)){return;}
this.permissions.push({role:'',create:false,read:false,update:false,xdelete:false,});},updatePermission(index){setTimeout(()=>{const permission=this.permissions[index];Object.keys(permission).forEach(key=>{if(key==='role'){return;} this.permissions.push({role:'',create:false,read:false,update:false,xdelete:false,});},updatePermission(index){setTimeout(()=>{const permission=this.permissions[index];Object.keys(permission).forEach(key=>{if(key==='role'){return;}
const parsedKey=this.parseOutputPermission(key);const permissionString=this.buildPermission(parsedKey,permission.role);if(permission[key]){if(!this.rawPermissions.includes(permissionString)){this.rawPermissions.push(permissionString);}}else{this.rawPermissions=this.rawPermissions.filter(p=>{return!p.includes(permissionString);});}});});},removePermission(index){let row=this.permissions.splice(index,1);if(row.length===1){this.rawPermissions=this.rawPermissions.filter(p=>!p.includes(row[0].role));}},parsePermission(permission){let parts=permission.split('(');let type=parts[0];let role=parts[1].replace(')','').replace(' ','').replaceAll('"','');return{type,role};},buildPermission(type,role){return`${type}("${role}")`},parseInputPermission(key){if(key==='delete'){return'xdelete';} const parsedKey=this.parseOutputPermission(key);const permissionString=this.buildPermission(parsedKey,permission.role);if(permission[key]){if(!this.rawPermissions.includes(permissionString)){this.rawPermissions.push(permissionString);}}else{this.rawPermissions=this.rawPermissions.filter(p=>{return!p.includes(permissionString);});}});});},clearPermission(index){let currentRole=this.permissions[index].role;this.rawPermissions=this.rawPermissions.filter(p=>{let{type,role}=this.parsePermission(p);return role!==currentRole;});},removePermission(index){let row=this.permissions.splice(index,1);if(row.length===1){this.rawPermissions=this.rawPermissions.filter(p=>!p.includes(row[0].role));}},parsePermission(permission){let parts=permission.split('(');let type=parts[0];let role=parts[1].replace(')','').replace(' ','').replaceAll('"','');return{type,role};},buildPermission(type,role){return`${type}("${role}")`},parseInputPermission(key){if(key==='delete'){return'xdelete';}
return key;},parseOutputPermission(key){if(key==='xdelete'){return'delete';} return key;},parseOutputPermission(key){if(key==='xdelete'){return'delete';}
return key;},validate(formId,index){const form=document.getElementById(formId);const input=document.getElementById(`${formId}Input${index}`);const permission=this.permissions[index];input.setCustomValidity('');if(permission.role===''){input.setCustomValidity('Role is required');}else if(!Object.entries(permission).some(([k,v])=>!k.includes('role')&&v)){input.setCustomValidity('No permissions selected');}else if(this.permissions.some(p=>p.role===permission.role&&p!==permission)){input.setCustomValidity('Role entry already exists');} return key;},validate(formId,index){const form=document.getElementById(formId);const input=document.getElementById(`${formId}Input${index}`);const permission=this.permissions[index];input.setCustomValidity('');if(permission.role===''){input.setCustomValidity('Role is required');}else if(!Object.entries(permission).some(([k,v])=>!k.includes('role')&&v)){input.setCustomValidity('No permissions selected');}else if(this.permissions.some(p=>p.role===permission.role&&p!==permission)){input.setCustomValidity('Role entry already exists');}
return form.reportValidity();},prevent(event){event.preventDefault();event.stopPropagation();}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();} return form.reportValidity();},prevent(event){event.preventDefault();event.stopPropagation();}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}

View file

@ -75,6 +75,14 @@
}); });
}); });
}, },
clearPermission(index) {
let currentRole = this.permissions[index].role;
this.rawPermissions = this.rawPermissions.filter(p => {
let {type, role} = this.parsePermission(p);
return role !== currentRole;
});
},
removePermission(index) { removePermission(index) {
let row = this.permissions.splice(index, 1); let row = this.permissions.splice(index, 1);
if (row.length === 1) { if (row.length === 1) {

View file

@ -44,6 +44,7 @@ abstract class Migration
'1.0.0-RC1' => 'V15', '1.0.0-RC1' => 'V15',
'1.0.0' => 'V15', '1.0.0' => 'V15',
'1.0.1' => 'V15', '1.0.1' => 'V15',
'1.0.2' => 'V15'
]; ];
/** /**

View file

@ -504,6 +504,7 @@ class V15 extends Migration
$this->createPermissionsColumn($id); $this->createPermissionsColumn($id);
$this->migrateDateTimeAttribute($id, '_createdAt'); $this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt'); $this->migrateDateTimeAttribute($id, '_updatedAt');
$this->migrateDateTimeAttribute($id, 'time');
break; break;
case 'buckets': case 'buckets':

View file

@ -4,12 +4,13 @@ namespace Appwrite\SMS\Adapter;
use Appwrite\SMS\Adapter; use Appwrite\SMS\Adapter;
// Mock adapter used to E2E test worker
class Mock extends Adapter class Mock extends Adapter
{ {
/** /**
* @var string * @var string
*/ */
public static string $digits = '123456'; private string $endpoint = 'http://request-catcher:5000/mock-sms';
/** /**
* @param string $from * @param string $from
@ -19,6 +20,19 @@ class Mock extends Adapter
*/ */
public function send(string $from, string $to, string $message): void public function send(string $from, string $to, string $message): void
{ {
return; $this->request(
method: 'POST',
url: $this->endpoint,
payload: \json_encode([
'message' => $message,
'from' => $from,
'to' => $to
]),
headers: [
"content-type: application/json",
"x-username: {$this->user}",
"x-key: {$this->secret}",
]
);
} }
} }

View file

@ -91,20 +91,17 @@ class V15 extends Filter
$parsedResponse[$listKey] = array_map(fn ($content) => $this->parseCreatedAtUpdatedAt($content), $parsedResponse[$listKey]); $parsedResponse[$listKey] = array_map(fn ($content) => $this->parseCreatedAtUpdatedAt($content), $parsedResponse[$listKey]);
break; break;
case Response::MODEL_DOCUMENT: case Response::MODEL_DOCUMENT:
$parsedResponse = $this->parseDocument($parsedResponse);
break;
case Response::MODEL_FILE: case Response::MODEL_FILE:
$parsedResponse = $this->parsePermissionsCreatedAtUpdatedAt($parsedResponse); $parsedResponse = $this->parsePermissionsCreatedAtUpdatedAt($parsedResponse);
break; break;
case Response::MODEL_DOCUMENT_LIST: case Response::MODEL_DOCUMENT_LIST:
$listKey = 'documents';
$parsedResponse[$listKey] = array_map(fn ($content) => $this->parseDocument($content), $parsedResponse[$listKey]);
break;
case Response::MODEL_FILE_LIST: case Response::MODEL_FILE_LIST:
$listKey = ''; $listKey = 'files';
switch ($model) {
case Response::MODEL_DOCUMENT_LIST:
$listKey = 'documents';
break;
case Response::MODEL_FILE_LIST:
$listKey = 'files';
break;
}
$parsedResponse[$listKey] = array_map(fn ($content) => $this->parsePermissionsCreatedAtUpdatedAt($content), $parsedResponse[$listKey]); $parsedResponse[$listKey] = array_map(fn ($content) => $this->parsePermissionsCreatedAtUpdatedAt($content), $parsedResponse[$listKey]);
break; break;
case Response::MODEL_EXECUTION: case Response::MODEL_EXECUTION:
@ -210,7 +207,11 @@ class V15 extends Filter
protected function parseDatetimeAttributes(array $content, array $attributes): array protected function parseDatetimeAttributes(array $content, array $attributes): array
{ {
foreach ($attributes as $attribute) { foreach ($attributes as $attribute) {
if (isset($content[$attribute])) { if (array_key_exists($attribute, $content)) {
if (empty($content[$attribute])) {
$content[$attribute] = 0;
continue;
}
$content[$attribute] = strtotime($content[$attribute]); $content[$attribute] = strtotime($content[$attribute]);
} }
} }
@ -314,6 +315,19 @@ class V15 extends Filter
return $content; return $content;
} }
protected function parseDocument(array $content)
{
if (isset($content['$collectionId'])) {
$content['$collection'] = $content['$collectionId'];
unset($content['$collectionId']);
}
unset($content['$databaseId']);
$content = $this->parsePermissionsCreatedAtUpdatedAt($content);
return $content;
}
private function parseExecution($content) private function parseExecution($content)
{ {
unset($content['stdout']); unset($content['stdout']);

View file

@ -13,26 +13,26 @@ class AlgoScrypt extends Model
->addRule('costCpu', [ ->addRule('costCpu', [
'type' => self::TYPE_INTEGER, 'type' => self::TYPE_INTEGER,
'description' => 'CPU complexity of computed hash.', 'description' => 'CPU complexity of computed hash.',
'default' => '', 'default' => 8,
'example' => 8, 'example' => 8,
]) ])
->addRule('costMemory', [ ->addRule('costMemory', [
'type' => self::TYPE_INTEGER, 'type' => self::TYPE_INTEGER,
'description' => 'Memory complexity of computed hash.', 'description' => 'Memory complexity of computed hash.',
'default' => '', 'default' => 14,
'example' => 14, 'example' => 14,
]) ])
->addRule('costParallel', [ ->addRule('costParallel', [
'type' => self::TYPE_INTEGER, 'type' => self::TYPE_INTEGER,
'description' => 'Parallelization of computed hash.', 'description' => 'Parallelization of computed hash.',
'default' => '', 'default' => 1,
'example' => 1, 'example' => 1,
]) ])
->addRule('length', [ ->addRule('length', [
'type' => self::TYPE_INTEGER, 'type' => self::TYPE_INTEGER,
'description' => 'Length used to compute hash.', 'description' => 'Length used to compute hash.',
'default' => '', 'default' => 64,
'example' => 1, 'example' => 64,
]) ])
; ;
} }

View file

@ -26,6 +26,15 @@ class UsageTest extends Scope
parent::setUp(); parent::setUp();
} }
protected static string $formatTz = 'Y-m-d\TH:i:s.vP';
protected function validateDates(array $metrics): void
{
foreach ($metrics as $metric) {
$this->assertIsObject(\DateTime::createFromFormat("Y-m-d\TH:i:s.vP", $metric['date']));
}
}
public function testPrepareUsersStats(): array public function testPrepareUsersStats(): array
{ {
$project = $this->getProject(true); $project = $this->getProject(true);
@ -97,7 +106,9 @@ class UsageTest extends Scope
$this->assertEquals(30, count($res['requests'])); $this->assertEquals(30, count($res['requests']));
$this->assertEquals(30, count($res['users'])); $this->assertEquals(30, count($res['users']));
$this->assertEquals($usersCount, $res['users'][array_key_last($res['users'])]['value']); $this->assertEquals($usersCount, $res['users'][array_key_last($res['users'])]['value']);
$this->validateDates($res['users']);
$this->assertEquals($requestsCount, $res['requests'][array_key_last($res['requests'])]['value']); $this->assertEquals($requestsCount, $res['requests'][array_key_last($res['requests'])]['value']);
$this->validateDates($res['requests']);
$res = $this->client->call(Client::METHOD_GET, '/users/usage?range=30d', array_merge($cheaders, [ $res = $this->client->call(Client::METHOD_GET, '/users/usage?range=30d', array_merge($cheaders, [
'x-appwrite-project' => $projectId, 'x-appwrite-project' => $projectId,
@ -105,8 +116,11 @@ class UsageTest extends Scope
])); ]));
$res = $res['body']; $res = $res['body'];
$this->assertEquals(10, $res['usersCreate'][array_key_last($res['usersCreate'])]['value']); $this->assertEquals(10, $res['usersCreate'][array_key_last($res['usersCreate'])]['value']);
$this->validateDates($res['usersCreate']);
$this->assertEquals(5, $res['usersRead'][array_key_last($res['usersRead'])]['value']); $this->assertEquals(5, $res['usersRead'][array_key_last($res['usersRead'])]['value']);
$this->validateDates($res['usersRead']);
$this->assertEquals(5, $res['usersDelete'][array_key_last($res['usersDelete'])]['value']); $this->assertEquals(5, $res['usersDelete'][array_key_last($res['usersDelete'])]['value']);
$this->validateDates($res['usersDelete']);
return ['projectId' => $projectId, 'headers' => $headers, 'requestsCount' => $requestsCount]; return ['projectId' => $projectId, 'headers' => $headers, 'requestsCount' => $requestsCount];
} }
@ -176,7 +190,7 @@ class UsageTest extends Scope
'path' => realpath(__DIR__ . '/../../resources/disk-a/kitten-1.jpg'), 'path' => realpath(__DIR__ . '/../../resources/disk-a/kitten-1.jpg'),
'name' => 'kitten-1.jpg', 'name' => 'kitten-1.jpg',
], ],
]; ];
for ($i = 0; $i < 10; $i++) { for ($i = 0; $i < 10; $i++) {
$file = $files[$i % count($files)]; $file = $files[$i % count($files)];
@ -257,7 +271,9 @@ class UsageTest extends Scope
$this->assertEquals(30, count($res['requests'])); $this->assertEquals(30, count($res['requests']));
$this->assertEquals(30, count($res['storage'])); $this->assertEquals(30, count($res['storage']));
$this->assertEquals($requestsCount, $res['requests'][array_key_last($res['requests'])]['value']); $this->assertEquals($requestsCount, $res['requests'][array_key_last($res['requests'])]['value']);
$this->validateDates($res['requests']);
$this->assertEquals($storageTotal, $res['storage'][array_key_last($res['storage'])]['value']); $this->assertEquals($storageTotal, $res['storage'][array_key_last($res['storage'])]['value']);
$this->validateDates($res['storage']);
$res = $this->client->call(Client::METHOD_GET, '/storage/usage?range=30d', array_merge($headers, [ $res = $this->client->call(Client::METHOD_GET, '/storage/usage?range=30d', array_merge($headers, [
'x-appwrite-project' => $projectId, 'x-appwrite-project' => $projectId,
@ -265,14 +281,23 @@ class UsageTest extends Scope
])); ]));
$res = $res['body']; $res = $res['body'];
$this->assertEquals($storageTotal, $res['storage'][array_key_last($res['storage'])]['value']); $this->assertEquals($storageTotal, $res['storage'][array_key_last($res['storage'])]['value']);
$this->validateDates($res['storage']);
$this->assertEquals($bucketsCount, $res['bucketsCount'][array_key_last($res['bucketsCount'])]['value']); $this->assertEquals($bucketsCount, $res['bucketsCount'][array_key_last($res['bucketsCount'])]['value']);
$this->validateDates($res['bucketsCount']);
$this->assertEquals($bucketsRead, $res['bucketsRead'][array_key_last($res['bucketsRead'])]['value']); $this->assertEquals($bucketsRead, $res['bucketsRead'][array_key_last($res['bucketsRead'])]['value']);
$this->validateDates($res['bucketsRead']);
$this->assertEquals($bucketsCreate, $res['bucketsCreate'][array_key_last($res['bucketsCreate'])]['value']); $this->assertEquals($bucketsCreate, $res['bucketsCreate'][array_key_last($res['bucketsCreate'])]['value']);
$this->validateDates($res['bucketsCreate']);
$this->assertEquals($bucketsDelete, $res['bucketsDelete'][array_key_last($res['bucketsDelete'])]['value']); $this->assertEquals($bucketsDelete, $res['bucketsDelete'][array_key_last($res['bucketsDelete'])]['value']);
$this->validateDates($res['bucketsDelete']);
$this->assertEquals($filesCount, $res['filesCount'][array_key_last($res['filesCount'])]['value']); $this->assertEquals($filesCount, $res['filesCount'][array_key_last($res['filesCount'])]['value']);
$this->validateDates($res['filesCount']);
$this->assertEquals($filesRead, $res['filesRead'][array_key_last($res['filesRead'])]['value']); $this->assertEquals($filesRead, $res['filesRead'][array_key_last($res['filesRead'])]['value']);
$this->validateDates($res['filesRead']);
$this->assertEquals($filesCreate, $res['filesCreate'][array_key_last($res['filesCreate'])]['value']); $this->assertEquals($filesCreate, $res['filesCreate'][array_key_last($res['filesCreate'])]['value']);
$this->validateDates($res['filesCreate']);
$this->assertEquals($filesDelete, $res['filesDelete'][array_key_last($res['filesDelete'])]['value']); $this->assertEquals($filesDelete, $res['filesDelete'][array_key_last($res['filesDelete'])]['value']);
$this->validateDates($res['filesDelete']);
$res = $this->client->call(Client::METHOD_GET, '/storage/' . $bucketId . '/usage?range=30d', array_merge($headers, [ $res = $this->client->call(Client::METHOD_GET, '/storage/' . $bucketId . '/usage?range=30d', array_merge($headers, [
'x-appwrite-project' => $projectId, 'x-appwrite-project' => $projectId,
@ -483,8 +508,11 @@ class UsageTest extends Scope
$this->assertEquals(30, count($res['requests'])); $this->assertEquals(30, count($res['requests']));
$this->assertEquals(30, count($res['storage'])); $this->assertEquals(30, count($res['storage']));
$this->assertEquals($requestsCount, $res['requests'][array_key_last($res['requests'])]['value']); $this->assertEquals($requestsCount, $res['requests'][array_key_last($res['requests'])]['value']);
$this->validateDates($res['requests']);
$this->assertEquals($collectionsCount, $res['collections'][array_key_last($res['collections'])]['value']); $this->assertEquals($collectionsCount, $res['collections'][array_key_last($res['collections'])]['value']);
$this->validateDates($res['collections']);
$this->assertEquals($documentsCount, $res['documents'][array_key_last($res['documents'])]['value']); $this->assertEquals($documentsCount, $res['documents'][array_key_last($res['documents'])]['value']);
$this->validateDates($res['documents']);
$res = $this->client->call(Client::METHOD_GET, '/databases/usage?range=30d', array_merge($headers, [ $res = $this->client->call(Client::METHOD_GET, '/databases/usage?range=30d', array_merge($headers, [
'x-appwrite-project' => $projectId, 'x-appwrite-project' => $projectId,
@ -492,21 +520,34 @@ class UsageTest extends Scope
])); ]));
$res = $res['body']; $res = $res['body'];
$this->assertEquals($databasesCount, $res['databasesCount'][array_key_last($res['databasesCount'])]['value']); $this->assertEquals($databasesCount, $res['databasesCount'][array_key_last($res['databasesCount'])]['value']);
$this->validateDates($res['databasesCount']);
$this->assertEquals($collectionsCount, $res['collectionsCount'][array_key_last($res['collectionsCount'])]['value']); $this->assertEquals($collectionsCount, $res['collectionsCount'][array_key_last($res['collectionsCount'])]['value']);
$this->validateDates($res['collectionsCount']);
$this->assertEquals($documentsCount, $res['documentsCount'][array_key_last($res['documentsCount'])]['value']); $this->assertEquals($documentsCount, $res['documentsCount'][array_key_last($res['documentsCount'])]['value']);
$this->validateDates($res['documentsCount']);
$this->assertEquals($databasesCreate, $res['databasesCreate'][array_key_last($res['databasesCreate'])]['value']); $this->assertEquals($databasesCreate, $res['databasesCreate'][array_key_last($res['databasesCreate'])]['value']);
$this->validateDates($res['databasesCreate']);
$this->assertEquals($databasesRead, $res['databasesRead'][array_key_last($res['databasesRead'])]['value']); $this->assertEquals($databasesRead, $res['databasesRead'][array_key_last($res['databasesRead'])]['value']);
$this->validateDates($res['databasesRead']);
$this->assertEquals($databasesDelete, $res['databasesDelete'][array_key_last($res['databasesDelete'])]['value']); $this->assertEquals($databasesDelete, $res['databasesDelete'][array_key_last($res['databasesDelete'])]['value']);
$this->validateDates($res['databasesDelete']);
$this->assertEquals($collectionsCreate, $res['collectionsCreate'][array_key_last($res['collectionsCreate'])]['value']); $this->assertEquals($collectionsCreate, $res['collectionsCreate'][array_key_last($res['collectionsCreate'])]['value']);
$this->validateDates($res['collectionsCreate']);
$this->assertEquals($collectionsRead, $res['collectionsRead'][array_key_last($res['collectionsRead'])]['value']); $this->assertEquals($collectionsRead, $res['collectionsRead'][array_key_last($res['collectionsRead'])]['value']);
$this->validateDates($res['collectionsRead']);
$this->assertEquals($collectionsUpdate, $res['collectionsUpdate'][array_key_last($res['collectionsUpdate'])]['value']); $this->assertEquals($collectionsUpdate, $res['collectionsUpdate'][array_key_last($res['collectionsUpdate'])]['value']);
$this->validateDates($res['collectionsUpdate']);
$this->assertEquals($collectionsDelete, $res['collectionsDelete'][array_key_last($res['collectionsDelete'])]['value']); $this->assertEquals($collectionsDelete, $res['collectionsDelete'][array_key_last($res['collectionsDelete'])]['value']);
$this->validateDates($res['collectionsDelete']);
$this->assertEquals($documentsCreate, $res['documentsCreate'][array_key_last($res['documentsCreate'])]['value']); $this->assertEquals($documentsCreate, $res['documentsCreate'][array_key_last($res['documentsCreate'])]['value']);
$this->validateDates($res['documentsCreate']);
$this->assertEquals($documentsRead, $res['documentsRead'][array_key_last($res['documentsRead'])]['value']); $this->assertEquals($documentsRead, $res['documentsRead'][array_key_last($res['documentsRead'])]['value']);
$this->validateDates($res['documentsRead']);
$this->assertEquals($documentsDelete, $res['documentsDelete'][array_key_last($res['documentsDelete'])]['value']); $this->assertEquals($documentsDelete, $res['documentsDelete'][array_key_last($res['documentsDelete'])]['value']);
$this->validateDates($res['documentsDelete']);
$res = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/usage?range=30d', array_merge($headers, [ $res = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/usage?range=30d', array_merge($headers, [
'x-appwrite-project' => $projectId, 'x-appwrite-project' => $projectId,
@ -514,16 +555,25 @@ class UsageTest extends Scope
])); ]));
$res = $res['body']; $res = $res['body'];
$this->assertEquals($collectionsCount, $res['collectionsCount'][array_key_last($res['collectionsCount'])]['value']); $this->assertEquals($collectionsCount, $res['collectionsCount'][array_key_last($res['collectionsCount'])]['value']);
$this->validateDates($res['collectionsCount']);
$this->assertEquals($documentsCount, $res['documentsCount'][array_key_last($res['documentsCount'])]['value']); $this->assertEquals($documentsCount, $res['documentsCount'][array_key_last($res['documentsCount'])]['value']);
$this->validateDates($res['documentsCount']);
$this->assertEquals($collectionsCreate, $res['collectionsCreate'][array_key_last($res['collectionsCreate'])]['value']); $this->assertEquals($collectionsCreate, $res['collectionsCreate'][array_key_last($res['collectionsCreate'])]['value']);
$this->validateDates($res['collectionsCreate']);
$this->assertEquals($collectionsRead, $res['collectionsRead'][array_key_last($res['collectionsRead'])]['value']); $this->assertEquals($collectionsRead, $res['collectionsRead'][array_key_last($res['collectionsRead'])]['value']);
$this->validateDates($res['collectionsRead']);
$this->assertEquals($collectionsUpdate, $res['collectionsUpdate'][array_key_last($res['collectionsUpdate'])]['value']); $this->assertEquals($collectionsUpdate, $res['collectionsUpdate'][array_key_last($res['collectionsUpdate'])]['value']);
$this->validateDates($res['collectionsUpdate']);
$this->assertEquals($collectionsDelete, $res['collectionsDelete'][array_key_last($res['collectionsDelete'])]['value']); $this->assertEquals($collectionsDelete, $res['collectionsDelete'][array_key_last($res['collectionsDelete'])]['value']);
$this->validateDates($res['collectionsDelete']);
$this->assertEquals($documentsCreate, $res['documentsCreate'][array_key_last($res['documentsCreate'])]['value']); $this->assertEquals($documentsCreate, $res['documentsCreate'][array_key_last($res['documentsCreate'])]['value']);
$this->validateDates($res['documentsCreate']);
$this->assertEquals($documentsRead, $res['documentsRead'][array_key_last($res['documentsRead'])]['value']); $this->assertEquals($documentsRead, $res['documentsRead'][array_key_last($res['documentsRead'])]['value']);
$this->validateDates($res['documentsRead']);
$this->assertEquals($documentsDelete, $res['documentsDelete'][array_key_last($res['documentsDelete'])]['value']); $this->assertEquals($documentsDelete, $res['documentsDelete'][array_key_last($res['documentsDelete'])]['value']);
$this->validateDates($res['documentsDelete']);
$res = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/usage?range=30d', array_merge($headers, [ $res = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/usage?range=30d', array_merge($headers, [
'x-appwrite-project' => $projectId, 'x-appwrite-project' => $projectId,
@ -531,10 +581,14 @@ class UsageTest extends Scope
])); ]));
$res = $res['body']; $res = $res['body'];
$this->assertEquals($documentsCount, $res['documentsCount'][array_key_last($res['documentsCount'])]['value']); $this->assertEquals($documentsCount, $res['documentsCount'][array_key_last($res['documentsCount'])]['value']);
$this->validateDates($res['documentsCount']);
$this->assertEquals($documentsCreate, $res['documentsCreate'][array_key_last($res['documentsCreate'])]['value']); $this->assertEquals($documentsCreate, $res['documentsCreate'][array_key_last($res['documentsCreate'])]['value']);
$this->validateDates($res['documentsCreate']);
$this->assertEquals($documentsRead, $res['documentsRead'][array_key_last($res['documentsRead'])]['value']); $this->assertEquals($documentsRead, $res['documentsRead'][array_key_last($res['documentsRead'])]['value']);
$this->validateDates($res['documentsRead']);
$this->assertEquals($documentsDelete, $res['documentsDelete'][array_key_last($res['documentsDelete'])]['value']); $this->assertEquals($documentsDelete, $res['documentsDelete'][array_key_last($res['documentsDelete'])]['value']);
$this->validateDates($res['documentsDelete']);
$data['requestsCount'] = $requestsCount; $data['requestsCount'] = $requestsCount;
return $data; return $data;
@ -667,8 +721,11 @@ class UsageTest extends Scope
$response = $response['body']; $response = $response['body'];
$this->assertEquals($executions, $response['executionsTotal'][array_key_last($response['executionsTotal'])]['value']); $this->assertEquals($executions, $response['executionsTotal'][array_key_last($response['executionsTotal'])]['value']);
$this->validateDates($response['executionsTotal']);
$this->assertEquals($executionTime, $response['executionsTime'][array_key_last($response['executionsTime'])]['value']); $this->assertEquals($executionTime, $response['executionsTime'][array_key_last($response['executionsTime'])]['value']);
$this->validateDates($response['executionsTime']);
$this->assertEquals($failures, $response['executionsFailure'][array_key_last($response['executionsFailure'])]['value']); $this->assertEquals($failures, $response['executionsFailure'][array_key_last($response['executionsFailure'])]['value']);
$this->validateDates($response['executionsFailure']);
$response = $this->client->call(Client::METHOD_GET, '/functions/usage', $headers, [ $response = $this->client->call(Client::METHOD_GET, '/functions/usage', $headers, [
'range' => '30d' 'range' => '30d'
@ -688,9 +745,13 @@ class UsageTest extends Scope
$response = $response['body']; $response = $response['body'];
$this->assertEquals($executions, $response['executionsTotal'][array_key_last($response['executionsTotal'])]['value']); $this->assertEquals($executions, $response['executionsTotal'][array_key_last($response['executionsTotal'])]['value']);
$this->validateDates($response['executionsTotal']);
$this->assertEquals($executionTime, $response['executionsTime'][array_key_last($response['executionsTime'])]['value']); $this->assertEquals($executionTime, $response['executionsTime'][array_key_last($response['executionsTime'])]['value']);
$this->validateDates($response['executionsTime']);
$this->assertGreaterThan(0, $response['buildsTime'][array_key_last($response['buildsTime'])]['value']); $this->assertGreaterThan(0, $response['buildsTime'][array_key_last($response['buildsTime'])]['value']);
$this->validateDates($response['buildsTime']);
$this->assertEquals($failures, $response['executionsFailure'][array_key_last($response['executionsFailure'])]['value']); $this->assertEquals($failures, $response['executionsFailure'][array_key_last($response['executionsFailure'])]['value']);
$this->validateDates($response['executionsFailure']);
} }
protected function tearDown(): void protected function tearDown(): void

View file

@ -716,7 +716,19 @@ class AccountCustomClientTest extends Scope
$this->assertEquals(400, $response['headers']['status-code']); $this->assertEquals(400, $response['headers']['status-code']);
$data['token'] = Mock::$digits; \sleep(2);
$smsRequest = $this->getLastRequest();
$this->assertEquals('http://request-catcher:5000/mock-sms', $smsRequest['url']);
$this->assertEquals('Appwrite Phone Authentication', $smsRequest['headers']['User-Agent']);
$this->assertEquals('username', $smsRequest['headers']['X-Username']);
$this->assertEquals('password', $smsRequest['headers']['X-Key']);
$this->assertEquals('POST', $smsRequest['method']);
$this->assertEquals('+123456789', $smsRequest['data']['from']);
$this->assertEquals($number, $smsRequest['data']['to']);
$data['token'] = $smsRequest['data']['message'];
$data['id'] = $userId; $data['id'] = $userId;
$data['number'] = $number; $data['number'] = $number;
@ -931,7 +943,13 @@ class AccountCustomClientTest extends Scope
$this->assertEmpty($response['body']['secret']); $this->assertEmpty($response['body']['secret']);
$this->assertEquals(true, DateTime::isValid($response['body']['expire'])); $this->assertEquals(true, DateTime::isValid($response['body']['expire']));
return $data; \sleep(2);
$smsRequest = $this->getLastRequest();
return \array_merge($data, [
'token' => $smsRequest['data']['message']
]);
} }
/** /**
@ -941,6 +959,7 @@ class AccountCustomClientTest extends Scope
{ {
$id = $data['id'] ?? ''; $id = $data['id'] ?? '';
$session = $data['session'] ?? ''; $session = $data['session'] ?? '';
$secret = $data['token'] ?? '';
/** /**
* Test for SUCCESS * Test for SUCCESS
@ -952,7 +971,7 @@ class AccountCustomClientTest extends Scope
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [ ]), [
'userId' => $id, 'userId' => $id,
'secret' => Mock::$digits, 'secret' => $secret,
]); ]);
$this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(200, $response['headers']['status-code']);
@ -967,7 +986,7 @@ class AccountCustomClientTest extends Scope
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [ ]), [
'userId' => ID::custom('ewewe'), 'userId' => ID::custom('ewewe'),
'secret' => Mock::$digits, 'secret' => $secret,
]); ]);
$this->assertEquals(404, $response['headers']['status-code']); $this->assertEquals(404, $response['headers']['status-code']);

View file

@ -2968,4 +2968,52 @@ trait DatabasesBase
return []; return [];
} }
/**
* @depends testCreateDatabase
*/
public function testAttributeBooleanDefault(array $data): void
{
$databaseId = $data['databaseId'];
/**
* Test for SUCCESS
*/
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::unique(),
'name' => 'Boolean'
]);
$this->assertEquals(201, $collection['headers']['status-code']);
$collectionId = $collection['body']['$id'];
$true = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/boolean', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'true',
'required' => false,
'default' => true
]);
$this->assertEquals(202, $true['headers']['status-code']);
$false = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/boolean', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'false',
'required' => false,
'default' => false
]);
$this->assertEquals(202, $false['headers']['status-code']);
}
} }

View file

@ -116,7 +116,7 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(202, $deployment['headers']['status-code']); $this->assertEquals(202, $deployment['headers']['status-code']);
// Wait for deployment to be built. // Wait for deployment to be built.
sleep(10); sleep(20);
$function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [ $function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -228,7 +228,7 @@ class FunctionsCustomClientTest extends Scope
$deploymentId = $deployment['body']['$id'] ?? ''; $deploymentId = $deployment['body']['$id'] ?? '';
// Wait for deployment to be built. // Wait for deployment to be built.
sleep(10); sleep(20);
$this->assertEquals(202, $deployment['headers']['status-code']); $this->assertEquals(202, $deployment['headers']['status-code']);
@ -252,7 +252,7 @@ class FunctionsCustomClientTest extends Scope
$executionId = $execution['body']['$id'] ?? ''; $executionId = $execution['body']['$id'] ?? '';
sleep(10); sleep(20);
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, [ $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, [
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -326,7 +326,7 @@ class FunctionsCustomClientTest extends Scope
$deploymentId = $deployment['body']['$id'] ?? ''; $deploymentId = $deployment['body']['$id'] ?? '';
// Wait for deployment to be built. // Wait for deployment to be built.
sleep(10); sleep(20);
$this->assertEquals(202, $deployment['headers']['status-code']); $this->assertEquals(202, $deployment['headers']['status-code']);
@ -395,7 +395,7 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(202, $execution['headers']['status-code']); $this->assertEquals(202, $execution['headers']['status-code']);
sleep(10); sleep(20);
$base = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [ $base = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -556,7 +556,7 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(202, $deployment['headers']['status-code']); $this->assertEquals(202, $deployment['headers']['status-code']);
// Wait for deployment to be built. // Wait for deployment to be built.
sleep(10); sleep(20);
$function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ $function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, [
'content-type' => 'application/json', 'content-type' => 'application/json',

View file

@ -373,7 +373,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals('index.php', $deployment['body']['entrypoint']); $this->assertEquals('index.php', $deployment['body']['entrypoint']);
// Wait for deployment to build. // Wait for deployment to build.
sleep(30); sleep(60);
return array_merge($data, ['deploymentId' => $deploymentId]); return array_merge($data, ['deploymentId' => $deploymentId]);
} }
@ -610,7 +610,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals('', $execution['body']['stderr']); $this->assertEquals('', $execution['body']['stderr']);
$this->assertEquals(0, $execution['body']['duration']); $this->assertEquals(0, $execution['body']['duration']);
sleep(5); sleep(10);
$execution = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions/' . $executionId, array_merge([ $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions/' . $executionId, array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -637,7 +637,7 @@ class FunctionsCustomServerTest extends Scope
* Test for FAILURE * Test for FAILURE
*/ */
sleep(10); sleep(20);
return array_merge($data, ['executionId' => $executionId]); return array_merge($data, ['executionId' => $executionId]);
} }
@ -881,7 +881,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(202, $deployment['headers']['status-code']); $this->assertEquals(202, $deployment['headers']['status-code']);
// Allow build step to run // Allow build step to run
sleep(20); sleep(40);
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -894,7 +894,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(202, $execution['headers']['status-code']); $this->assertEquals(202, $execution['headers']['status-code']);
sleep(10); sleep(20);
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([ $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -965,7 +965,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(202, $deployment['headers']['status-code']); $this->assertEquals(202, $deployment['headers']['status-code']);
// Allow build step to run // Allow build step to run
sleep(10); sleep(20);
$deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -988,7 +988,7 @@ class FunctionsCustomServerTest extends Scope
$executionId = $execution['body']['$id'] ?? ''; $executionId = $execution['body']['$id'] ?? '';
sleep(10); sleep(20);
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([ $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -1087,7 +1087,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(202, $deployment['headers']['status-code']); $this->assertEquals(202, $deployment['headers']['status-code']);
// Allow build step to run // Allow build step to run
sleep(10); sleep(20);
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -1103,7 +1103,7 @@ class FunctionsCustomServerTest extends Scope
$executionId = $execution['body']['$id'] ?? ''; $executionId = $execution['body']['$id'] ?? '';
sleep(10); sleep(20);
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([ $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -1200,7 +1200,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(202, $deployment['headers']['status-code']); $this->assertEquals(202, $deployment['headers']['status-code']);
// Allow build step to run // Allow build step to run
sleep(30); sleep(60);
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -1216,7 +1216,7 @@ class FunctionsCustomServerTest extends Scope
$executionId = $execution['body']['$id'] ?? ''; $executionId = $execution['body']['$id'] ?? '';
sleep(30); sleep(60);
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([ $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -1314,7 +1314,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(202, $deployment['headers']['status-code']); $this->assertEquals(202, $deployment['headers']['status-code']);
// Allow build step to run // Allow build step to run
sleep(40); sleep(80);
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -1330,7 +1330,7 @@ class FunctionsCustomServerTest extends Scope
$executionId = $execution['body']['$id'] ?? ''; $executionId = $execution['body']['$id'] ?? '';
sleep(10); sleep(20);
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([ $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -1428,7 +1428,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(202, $deployment['headers']['status-code']); $this->assertEquals(202, $deployment['headers']['status-code']);
// Allow build step to run // Allow build step to run
sleep(30); sleep(60);
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -1444,7 +1444,7 @@ class FunctionsCustomServerTest extends Scope
$executionId = $execution['body']['$id'] ?? ''; $executionId = $execution['body']['$id'] ?? '';
sleep(10); sleep(20);
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([ $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',

View file

@ -126,6 +126,52 @@ trait TeamsBaseClient
return $data; return $data;
} }
/**
* @depends testCreateTeamMembership
*/
public function testGetTeamMembership($data): void
{
$teamUid = $data['teamUid'] ?? '';
$membershipUid = $data['membershipUid'] ?? '';
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships/' . $membershipUid, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertNotEmpty($response['body']['userId']);
$this->assertNotEmpty($response['body']['userName']);
$this->assertNotEmpty($response['body']['userEmail']);
$this->assertNotEmpty($response['body']['teamId']);
$this->assertNotEmpty($response['body']['teamName']);
$this->assertCount(2, $response['body']['roles']);
$this->assertEquals(false, DateTime::isValid($response['body']['joined'])); // is null in DB
$this->assertEquals(false, $response['body']['confirm']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships/' . $membershipUid . 'dasdasd', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(404, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships/' . $membershipUid, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]);
$this->assertEquals(401, $response['headers']['status-code']);
}
/** /**
* @depends testCreateTeam * @depends testCreateTeam
*/ */

View file

@ -33,6 +33,51 @@ trait TeamsBaseServer
return []; return [];
} }
/**
* @depends testCreateTeamMembership
*/
public function testGetTeamMembership($data): void
{
$teamUid = $data['teamUid'] ?? '';
$membershipUid = $data['membershipUid'] ?? '';
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships/' . $membershipUid, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertNotEmpty($response['body']['userId']);
$this->assertNotEmpty($response['body']['userName']);
$this->assertNotEmpty($response['body']['userEmail']);
$this->assertNotEmpty($response['body']['teamId']);
$this->assertNotEmpty($response['body']['teamName']);
$this->assertCount(2, $response['body']['roles']);
$this->assertEquals(true, DateTime::isValid($response['body']['joined'])); // is null in DB
$this->assertEquals(true, $response['body']['confirm']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships/' . $membershipUid . 'dasdasd', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(404, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships/' . $membershipUid, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]);
$this->assertEquals(401, $response['headers']['status-code']);
}
/** /**
* @depends testCreateTeam * @depends testCreateTeam

View file

@ -180,89 +180,51 @@ trait UsersBase
*/ */
public function testCreateUserSessionHashed(array $data): void public function testCreateUserSessionHashed(array $data): void
{ {
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([ $userIds = [ 'md5', 'bcrypt', 'argon2', 'sha512', 'scrypt', 'phpass', 'scrypt-modified' ];
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'email' => 'md5@appwrite.io',
'password' => 'appwrite',
]);
$this->assertEquals($response['headers']['status-code'], 201); foreach ($userIds as $userId) {
$this->assertEquals($response['body']['userId'], 'md5'); // Ensure sessions can be created with hashed passwords
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'email' => $userId . '@appwrite.io',
'password' => 'appwrite',
]);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([ $this->assertEquals(201, $response['headers']['status-code']);
'origin' => 'http://localhost', $this->assertEquals($userId, $response['body']['userId']);
'content-type' => 'application/json', }
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'email' => 'bcrypt@appwrite.io',
'password' => 'appwrite',
]);
$this->assertEquals($response['headers']['status-code'], 201); foreach ($userIds as $userId) {
$this->assertEquals($response['body']['userId'], 'bcrypt'); // Ensure all passwords were re-hashed
$response = $this->client->call(Client::METHOD_GET, '/users/' . $userId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([ $this->assertEquals(200, $response['headers']['status-code']);
'origin' => 'http://localhost', $this->assertEquals($userId, $response['body']['$id']);
'content-type' => 'application/json', $this->assertEquals($userId . '@appwrite.io', $response['body']['email']);
'x-appwrite-project' => $this->getProject()['$id'], $this->assertEquals('argon2', $response['body']['hash']);
]), [ $this->assertStringStartsWith('$argon2', $response['body']['password']);
'email' => 'argon2@appwrite.io', }
'password' => 'appwrite',
]);
$this->assertEquals($response['headers']['status-code'], 201); foreach ($userIds as $userId) {
$this->assertEquals($response['body']['userId'], 'argon2'); // Ensure sessions can be created after re-hashing of passwords
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'email' => $userId . '@appwrite.io',
'password' => 'appwrite',
]);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([ $this->assertEquals(201, $response['headers']['status-code']);
'origin' => 'http://localhost', $this->assertEquals($userId, $response['body']['userId']);
'content-type' => 'application/json', }
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'email' => 'sha512@appwrite.io',
'password' => 'appwrite',
]);
$this->assertEquals($response['headers']['status-code'], 201);
$this->assertEquals($response['body']['userId'], 'sha512');
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'email' => 'scrypt@appwrite.io',
'password' => 'appwrite',
]);
$this->assertEquals($response['headers']['status-code'], 201);
$this->assertEquals($response['body']['userId'], 'scrypt');
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'email' => 'phpass@appwrite.io',
'password' => 'appwrite',
]);
$this->assertEquals($response['headers']['status-code'], 201);
$this->assertEquals($response['body']['userId'], 'phpass');
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'email' => 'scrypt-modified@appwrite.io',
'password' => 'appwrite',
]);
$this->assertEquals($response['headers']['status-code'], 201);
$this->assertEquals($response['body']['userId'], 'scrypt-modified');
} }
/** /**

View file

@ -634,7 +634,7 @@ class WebhooksCustomServerTest extends Scope
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
// wait for timeout function to complete // wait for timeout function to complete
sleep(10); sleep(20);
$webhook = $this->getLastRequest(); $webhook = $this->getLastRequest();
$signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']); $signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']);

View file

@ -502,9 +502,34 @@ class V15Test extends TestCase
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
} }
public function documentProvider(): array
{
return [
'basic document' => [
[
'$id' => '5e5ea5c16897e',
'$collectionId' => '5e5ea5c15117e',
'$databaseId' => '5e5ea5c15117e',
'$createdAt' => '2020-06-24T06:47:30.000Z',
'$updatedAt' => '2020-06-24T06:47:30.000Z',
'$permissions' => [Permission::read(Role::any())]
],
[
'$id' => '5e5ea5c16897e',
'$collection' => '5e5ea5c15117e',
'$createdAt' => 1592981250,
'$updatedAt' => 1592981250,
'$read' => ['role:all'],
'$write' => [],
],
],
];
}
/** /**
* @dataProvider createdAtUpdatedAtProvider * @dataProvider createdAtUpdatedAtProvider
* @dataProvider permissionsProvider * @dataProvider permissionsProvider
* @dataProvider documentProvider
*/ */
public function testDocument(array $content, array $expected): void public function testDocument(array $content, array $expected): void
{ {
@ -1077,6 +1102,14 @@ class V15Test extends TestCase
'providerAccessTokenExpiry' => 1592981250, 'providerAccessTokenExpiry' => 1592981250,
], ],
], ],
'empty values' => [
[
'providerAccessTokenExpiry' => '',
],
[
'providerAccessTokenExpiry' => 0,
],
],
]; ];
} }
@ -1089,7 +1122,7 @@ class V15Test extends TestCase
$result = $this->filter->parse($content, $model); $result = $this->filter->parse($content, $model);
$this->assertEquals($expected, $result); $this->assertSame($expected, $result);
} }
/** /**