Merge pull request #8546 from appwrite/fix-seconds-precision-exception

Feat: Add seconds precision to scheduledAt
This commit is contained in:
Christy Jacob 2024-08-16 20:21:27 +04:00 committed by GitHub
commit b115b6fb67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 82 additions and 44 deletions

View file

@ -1643,7 +1643,7 @@ App::post('/v1/functions/:functionId/executions')
->param('path', '/', new Text(2048), 'HTTP path of execution. Path can include query params. Default value is /', true)
->param('method', 'POST', new Whitelist(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], true), 'HTTP method of execution. Default value is GET.', true)
->param('headers', [], new AnyOf([new Assoc(), new Text(65535)], AnyOf::TYPE_MIXED), 'HTTP headers of execution. Defaults to empty.', true)
->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled execution time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->param('scheduledAt', null, new DatetimeValidator(true, DateTimeValidator::PRECISION_MINUTES, 60), 'Scheduled execution time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.', true)
->inject('response')
->inject('request')
->inject('project')

View file

@ -16,7 +16,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Projects;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use PHPMailer\PHPMailer\PHPMailer;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\Cache\Cache;
@ -209,7 +209,7 @@ App::post('/v1/projects')
$audit = new Audit($dbForProject);
$audit->setup();
$abuse = new TimeLimit('', 0, 1, $dbForProject);
$abuse = new AbuseDatabase('', 0, 1, $dbForProject);
$abuse->setup();
/** @var array $collections */

View file

@ -18,7 +18,7 @@ use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
use Utopia\App;
use Utopia\Cache\Adapter\Filesystem;
use Utopia\Cache\Cache;
@ -380,7 +380,7 @@ App::init()
foreach ($abuseKeyLabel as $abuseKey) {
$start = $request->getContentRangeStart();
$end = $request->getContentRangeEnd();
$timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject);
$timeLimit = new AbuseDatabase($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject);
$timeLimit
->setParam('{projectId}', $project->getId())
->setParam('{userId}', $user->getId())

View file

@ -9,7 +9,7 @@ use Swoole\Http\Request as SwooleRequest;
use Swoole\Http\Response as SwooleResponse;
use Swoole\Http\Server;
use Swoole\Process;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\CLI\Console;
@ -101,8 +101,8 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
$audit->setup();
}
if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) {
$adapter = new TimeLimit("", 0, 1, $dbForConsole);
if ($dbForConsole->getCollection(AbuseDatabase::COLLECTION)->isEmpty()) {
$adapter = new AbuseDatabase("", 0, 1, $dbForConsole);
$adapter->setup();
}

View file

@ -13,7 +13,7 @@ use Swoole\Runtime;
use Swoole\Table;
use Swoole\Timer;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
use Utopia\App;
use Utopia\Cache\Adapter\Sharding;
use Utopia\Cache\Cache;
@ -463,7 +463,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
*
* Abuse limits are connecting 128 times per minute and ip address.
*/
$timeLimit = new TimeLimit('url:{url},ip:{ip}', 128, 60, $dbForProject);
$timeLimit = new AbuseDatabase('url:{url},ip:{ip}', 128, 60, $dbForProject);
$timeLimit
->setParam('{ip}', $request->getIP())
->setParam('{url}', $request->getURI());
@ -563,7 +563,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
*
* Abuse limits are sending 32 times per minute and connection.
*/
$timeLimit = new TimeLimit('url:{url},connection:{connection}', 32, 60, $database);
$timeLimit = new AbuseDatabase('url:{url},connection:{connection}', 32, 60, $database);
$timeLimit
->setParam('{connection}', $connection)

View file

@ -44,13 +44,13 @@
"ext-sockets": "*",
"appwrite/php-runtimes": "0.14.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "0.38.*",
"utopia-php/abuse": "0.40.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "0.40.*",
"utopia-php/audit": "0.41.*",
"utopia-php/cache": "0.10.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.50.*",
"utopia-php/database": "0.51.*",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",

44
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "340aae0879435fc71eac688d47033eb4",
"content-hash": "cd0323e7303780f34b2f775a526de516",
"packages": [
{
"name": "adhocore/jwt",
@ -1429,26 +1429,28 @@
},
{
"name": "utopia-php/abuse",
"version": "0.38.0",
"version": "0.40.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/abuse.git",
"reference": "b7be9086c9d9b4561d810cbd42fdda798742f56c"
"reference": "69748a6741a9e44f0c9e430f3c2bd2b8a677c048"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/b7be9086c9d9b4561d810cbd42fdda798742f56c",
"reference": "b7be9086c9d9b4561d810cbd42fdda798742f56c",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/69748a6741a9e44f0c9e430f3c2bd2b8a677c048",
"reference": "69748a6741a9e44f0c9e430f3c2bd2b8a677c048",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-pdo": "*",
"ext-redis": "*",
"php": ">=8.0",
"utopia-php/database": "0.50.*"
"utopia-php/database": "0.51.*"
},
"require-dev": {
"laravel/pint": "1.5.*",
"phpbench/phpbench": "^1.2",
"phpstan/phpstan": "^1.9",
"phpunit/phpunit": "^9.4"
},
@ -1472,9 +1474,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/abuse/issues",
"source": "https://github.com/utopia-php/abuse/tree/0.38.0"
"source": "https://github.com/utopia-php/abuse/tree/0.40.0"
},
"time": "2024-06-24T00:52:02+00:00"
"time": "2024-08-16T06:08:24+00:00"
},
{
"name": "utopia-php/analytics",
@ -1524,21 +1526,21 @@
},
{
"name": "utopia-php/audit",
"version": "0.40.0",
"version": "0.41.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "735ae211ce5fee5b52b736731571b4030b1d7cdc"
"reference": "77f1d0a95ea791e38a38a8bc1b7728ffcedcd2d1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/735ae211ce5fee5b52b736731571b4030b1d7cdc",
"reference": "735ae211ce5fee5b52b736731571b4030b1d7cdc",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/77f1d0a95ea791e38a38a8bc1b7728ffcedcd2d1",
"reference": "77f1d0a95ea791e38a38a8bc1b7728ffcedcd2d1",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/database": "0.50.*"
"utopia-php/database": "0.51.*"
},
"require-dev": {
"laravel/pint": "1.5.*",
@ -1565,9 +1567,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/0.40.0"
"source": "https://github.com/utopia-php/audit/tree/0.41.0"
},
"time": "2024-06-24T00:52:17+00:00"
"time": "2024-08-16T06:08:00+00:00"
},
{
"name": "utopia-php/cache",
@ -1721,16 +1723,16 @@
},
{
"name": "utopia-php/database",
"version": "0.50.4",
"version": "0.51.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "fd3b856be77bd643bc8a9e3572ee11e4185b9230"
"reference": "845783a54cced784e00db084a29486fdb96d3d58"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/fd3b856be77bd643bc8a9e3572ee11e4185b9230",
"reference": "fd3b856be77bd643bc8a9e3572ee11e4185b9230",
"url": "https://api.github.com/repos/utopia-php/database/zipball/845783a54cced784e00db084a29486fdb96d3d58",
"reference": "845783a54cced784e00db084a29486fdb96d3d58",
"shasum": ""
},
"require": {
@ -1771,9 +1773,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.50.4"
"source": "https://github.com/utopia-php/database/tree/0.51.1"
},
"time": "2024-08-13T03:18:26+00:00"
"time": "2024-08-16T10:54:25+00:00"
},
{
"name": "utopia-php/domains",

View file

@ -7,7 +7,7 @@ use Appwrite\Extend\Exception;
use Executor\Executor;
use Throwable;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
use Utopia\Audit\Audit;
use Utopia\Cache\Adapter\Filesystem;
use Utopia\Cache\Cache;
@ -493,7 +493,7 @@ class Deletes extends Action
$projectCollectionIds = [
...\array_keys(Config::getParam('collections', [])['projects']),
Audit::COLLECTION,
TimeLimit::COLLECTION,
AbuseDatabase::COLLECTION,
];
$limit = \count($projectCollectionIds) + 25;
@ -686,7 +686,7 @@ class Deletes extends Action
{
$projectId = $project->getId();
$dbForProject = $getProjectDB($project);
$timeLimit = new TimeLimit("", 0, 1, $dbForProject);
$timeLimit = new AbuseDatabase("", 0, 1, $dbForProject);
$abuse = new Abuse($timeLimit);
try {

View file

@ -219,7 +219,8 @@ class FunctionsCustomClientTest extends Scope
// Schedule execution for the future
\date_default_timezone_set('UTC');
$futureTime = (new \DateTime())->add(new \DateInterval('PT10S'));
$futureTime = (new \DateTime())->add(new \DateInterval('PT2M'));
$futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0);
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', array_merge([
'content-type' => 'application/json',
@ -236,7 +237,7 @@ class FunctionsCustomClientTest extends Scope
$executionId = $execution['body']['$id'];
sleep(10);
sleep(60 + 60 + 15); // up to 1 minute round up, 1 minute schedule postpone, 15s cold start safety
$start = \microtime(true);
while (true) {
@ -251,7 +252,7 @@ class FunctionsCustomClientTest extends Scope
}
if (\microtime(true) - $start > 10) {
$this->fail('Execution did not complete within 10 seconds of schedule in status ' . $execution['body']['status'] . ': ' . \json_encode($execution));
$this->fail('Scheduled execution did not complete with status ' . $execution['body']['status'] . ': ' . \json_encode($execution));
}
usleep(500000); // 0.5 seconds
@ -267,7 +268,6 @@ class FunctionsCustomClientTest extends Scope
/* Test for FAILURE */
// Schedule synchronous execution
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@ -278,6 +278,41 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(400, $execution['headers']['status-code']);
// Execution with seconds precision
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'async' => true,
'scheduledAt' => (new \DateTime("2100-12-08 16:12:02"))->format(\DateTime::ATOM)
]);
$this->assertEquals(400, $execution['headers']['status-code']);
// Execution with milliseconds precision
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'async' => true,
'scheduledAt' => (new \DateTime("2100-12-08 16:12:02.255"))->format(\DateTime::ATOM)
]);
$this->assertEquals(400, $execution['headers']['status-code']);
// Execution too soon
$futureTime = (new \DateTime())->add(new \DateInterval('PT1M'));
$futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0);
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'async' => true,
'scheduledAt' => $futureTime->format(\DateTime::ATOM),
]);
$this->assertEquals(400, $execution['headers']['status-code']);
// Cleanup : Delete function
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $function['body']['$id'], [
'content-type' => 'application/json',

View file

@ -1286,14 +1286,15 @@ class FunctionsCustomServerTest extends Scope
*/
public function testDeleteScheduledExecution($data): array
{
$futureTime = (new \DateTime())->add(new \DateInterval('PT10H'))->format('Y-m-d H:i:s');
$futureTime = (new \DateTime())->add(new \DateInterval('PT10H'));
$futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0);
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'async' => true,
'scheduledAt' => $futureTime,
'scheduledAt' => $futureTime->format('Y-m-d H:i:s'),
]);
$executionId = $execution['body']['$id'] ?? '';