1
0
Fork 0
mirror of synced 2024-09-20 03:17:30 +12:00

Merge pull request #8425 from appwrite/feat-expose-execution-schedule-date

Feat: scheduledAt in execution response model
This commit is contained in:
Jake Barnby 2024-07-25 22:01:41 +12:00 committed by GitHub
commit 153736a020
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 127 additions and 3 deletions

View file

@ -3848,6 +3848,39 @@ $projectCollections = array_merge([
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
[
'$id' => ID::custom('scheduledAt'),
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('scheduleInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('scheduleId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
], ],
'indexes' => [ 'indexes' => [
[ [

View file

@ -1744,9 +1744,8 @@ App::post('/v1/functions/:functionId/executions')
->setContext('function', $function); ->setContext('function', $function);
if ($async) { if ($async) {
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
if(is_null($scheduledAt)) { if(is_null($scheduledAt)) {
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
$queueForFunctions $queueForFunctions
->setType('http') ->setType('http')
->setExecution($execution) ->setExecution($execution)
@ -1770,7 +1769,7 @@ App::post('/v1/functions/:functionId/executions')
'jwt' => $jwt, 'jwt' => $jwt,
]; ];
$dbForConsole->createDocument('schedules', new Document([ $schedule = $dbForConsole->createDocument('schedules', new Document([
'region' => System::getEnv('_APP_REGION', 'default'), 'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => ScheduleExecutions::getSupportedResource(), 'resourceType' => ScheduleExecutions::getSupportedResource(),
'resourceId' => $execution->getId(), 'resourceId' => $execution->getId(),
@ -1781,6 +1780,13 @@ App::post('/v1/functions/:functionId/executions')
'data' => $data, 'data' => $data,
'active' => true, 'active' => true,
])); ]));
$execution = $execution
->setAttribute('scheduleId', $schedule->getId())
->setAttribute('scheduleInternalId', $schedule->getInternalId())
->setAttribute('scheduledAt', $scheduledAt);
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
} }
return $response return $response

View file

@ -116,6 +116,33 @@ class V21 extends Migration
} catch (\Throwable $th) { } catch (\Throwable $th) {
Console::warning("'_key_deployment' from {$id}: {$th->getMessage()}"); Console::warning("'_key_deployment' from {$id}: {$th->getMessage()}");
} }
try {
/**
* Create 'scheduledAt' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'scheduledAt');
} catch (\Throwable $th) {
Console::warning("'scheduledAt' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create 'scheduleInternalId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'scheduleInternalId');
} catch (\Throwable $th) {
Console::warning("'scheduleInternalId' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create 'scheduleId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'scheduleId');
} catch (\Throwable $th) {
Console::warning("'scheduleId' from {$id}: {$th->getMessage()}");
}
} }
usleep(50000); usleep(50000);

View file

@ -14,6 +14,7 @@ class V18 extends Filter
$parsedResponse = match($model) { $parsedResponse = match($model) {
Response::MODEL_FUNCTION => $this->parseFunction($content), Response::MODEL_FUNCTION => $this->parseFunction($content),
Response::MODEL_EXECUTION => $this->parseExecution($content),
Response::MODEL_PROJECT => $this->parseProject($content), Response::MODEL_PROJECT => $this->parseProject($content),
default => $parsedResponse, default => $parsedResponse,
}; };
@ -21,6 +22,12 @@ class V18 extends Filter
return $parsedResponse; return $parsedResponse;
} }
protected function parseExecution(array $content)
{
unset($content['scheduledAt']);
return $content;
}
protected function parseFunction(array $content) protected function parseFunction(array $content)
{ {
unset($content['scopes']); unset($content['scopes']);

View file

@ -4,6 +4,7 @@ namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response; use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response\Model;
use Utopia\Database\DateTime;
use Utopia\Database\Helpers\Role; use Utopia\Database\Helpers\Role;
class Execution extends Model class Execution extends Model
@ -110,6 +111,13 @@ class Execution extends Model
'default' => 0, 'default' => 0,
'example' => 0.400, 'example' => 0.400,
]) ])
->addRule('scheduledAt', [
'type' => self::TYPE_DATETIME,
'description' => 'The scheduled time for execution. If left empty, execution will be queued immediately.',
'required' => false,
'default' => DateTime::now(),
'example' => self::TYPE_DATETIME_EXAMPLE,
])
; ;
} }

View file

@ -8,6 +8,7 @@ use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideClient; use Tests\E2E\Scopes\SideClient;
use Utopia\Database\DateTime;
use Utopia\Database\Document; use Utopia\Database\Document;
use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Role; use Utopia\Database\Helpers\Role;
@ -269,6 +270,7 @@ class FunctionsCustomClientTest extends Scope
// Schedule execution for the future // Schedule execution for the future
\date_default_timezone_set('UTC'); \date_default_timezone_set('UTC');
$futureTime = (new \DateTime())->add(new \DateInterval('PT10S'))->format('Y-m-d H:i:s'); $futureTime = (new \DateTime())->add(new \DateInterval('PT10S'))->format('Y-m-d H:i:s');
$futureTimeIso = DateTime::formatTz($futureTime);
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', array_merge([ $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -286,9 +288,23 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(202, $execution['headers']['status-code']); $this->assertEquals(202, $execution['headers']['status-code']);
$this->assertEquals('scheduled', $execution['body']['status']); $this->assertEquals('scheduled', $execution['body']['status']);
$this->assertEquals($futureTimeIso, $execution['body']['scheduledAt']);
$executionId = $execution['body']['$id']; $executionId = $execution['body']['$id'];
// List executions and ensure it has schedule date
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertGreaterThan(0, \count($response['body']['executions']));
$recentExecution = $response['body']['executions'][0];
$this->assertEquals($executionId, $recentExecution['$id']);
$this->assertEquals($futureTimeIso, $recentExecution['scheduledAt']);
sleep(20); sleep(20);
$execution = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions/' . $executionId, [ $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions/' . $executionId, [
@ -303,6 +319,7 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals('/custom', $execution['body']['requestPath']); $this->assertEquals('/custom', $execution['body']['requestPath']);
$this->assertEquals('GET', $execution['body']['requestMethod']); $this->assertEquals('GET', $execution['body']['requestMethod']);
$this->assertGreaterThan(0, $execution['body']['duration']); $this->assertGreaterThan(0, $execution['body']['duration']);
$this->assertEquals($futureTimeIso, $execution['body']['scheduledAt']);
/* Test for FAILURE */ /* Test for FAILURE */

View file

@ -50,6 +50,32 @@ class V18Test extends TestCase
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
} }
public function executionProvider(): array
{
return [
'remove scheduledAt' => [
[
'scheduledAt' => '2024-07-13T09:00:00.000Z',
],
[
]
]
];
}
/**
* @dataProvider executionProvider
*/
public function testExecution(array $content, array $expected): void
{
$model = Response::MODEL_EXECUTION;
$result = $this->filter->parse($content, $model);
$this->assertEquals($expected, $result);
}
public function projectProvider(): array public function projectProvider(): array
{ {
return [ return [