1
0
Fork 0
mirror of synced 2024-05-20 12:42:39 +12:00

Removed the tasks service

This commit is contained in:
Eldad Fux 2021-07-27 17:16:12 +03:00
parent 5dfa6217d8
commit 95dacecf3a
32 changed files with 21 additions and 1693 deletions

View file

@ -238,7 +238,6 @@ RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/worker-deletes && \
chmod +x /usr/local/bin/worker-functions && \
chmod +x /usr/local/bin/worker-mails && \
chmod +x /usr/local/bin/worker-tasks && \
chmod +x /usr/local/bin/worker-usage && \
chmod +x /usr/local/bin/worker-webhooks

View file

@ -807,16 +807,6 @@ $collections = [
'array' => true,
'list' => [Database::SYSTEM_COLLECTION_KEYS],
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Tasks',
'key' => 'tasks',
'type' => Database::SYSTEM_VAR_TYPE_DOCUMENT,
'default' => [],
'required' => false,
'array' => true,
'list' => [Database::SYSTEM_COLLECTION_TASKS],
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Platforms',
@ -939,160 +929,6 @@ $collections = [
],
],
],
Database::SYSTEM_COLLECTION_TASKS => [
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
'$id' => Database::SYSTEM_COLLECTION_TASKS,
'$permissions' => ['read' => ['role:all']],
'name' => 'Task',
'structure' => true,
'rules' => [
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Name',
'key' => 'name',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => null,
'required' => true,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Status',
'key' => 'status',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Updated',
'key' => 'updated',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Schedule',
'key' => 'schedule',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Previous',
'key' => 'previous',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Next',
'key' => 'next',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Duration',
'key' => 'duration',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Delay',
'key' => 'delay',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Security',
'key' => 'security',
'type' => Database::SYSTEM_VAR_TYPE_BOOLEAN,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'HTTP Method',
'key' => 'httpMethod',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'HTTP URL',
'key' => 'httpUrl',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'HTTP Headers',
'key' => 'httpHeaders',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => null,
'required' => false,
'array' => true,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'HTTP User',
'key' => 'httpUser',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'HTTP Password',
'key' => 'httpPass',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
'filter' => ['encrypt'],
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Log',
'key' => 'log',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Failures',
'key' => 'failures',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => '',
'required' => false,
'array' => false,
],
],
],
Database::SYSTEM_COLLECTION_PLATFORMS => [
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
'$id' => Database::SYSTEM_COLLECTION_PLATFORMS,

View file

@ -188,17 +188,6 @@ $collections = [
'array' => true,
'filters' => ['json'],
],
[
'$id' => 'tasks',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => true,
'filters' => ['json'],
],
[
'$id' => 'domains',
'type' => Database::VAR_STRING,

View file

@ -152,7 +152,7 @@ return [
'name' => 'Console',
'enabled' => false,
'beta' => false,
'languages' => [ // TODO change key to 'sdks'
'languages' => [
[
'key' => 'web',
'name' => 'Console',
@ -179,7 +179,7 @@ return [
'description' => 'Libraries for integrating with Appwrite to build server side integrations. Read the [getting started for server](/docs/getting-started-for-server) tutorial to start building your first server integration.',
'enabled' => true,
'beta' => false,
'languages' => [ // TODO change key to 'sdks'
'languages' => [
[
'key' => 'nodejs',
'name' => 'Node.js',

View file

@ -40,8 +40,6 @@ $admins = [
'platforms.write',
'keys.read',
'keys.write',
'tasks.read',
'tasks.write',
'webhooks.read',
'webhooks.write',
'locale.read',

View file

@ -1156,8 +1156,8 @@ App::delete('/v1/account')
$protocol = $request->getProtocol();
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('status', false));
//TODO delete all tokens or only current session?
//TODO delete all user data according to GDPR. Make sure everything is backed up and backups are deleted later
// TODO delete all tokens or only current session?
// TODO delete all user data according to GDPR. Make sure everything is backed up and backups are deleted later
/*
* Data to delete
* * Tokens
@ -1550,7 +1550,7 @@ App::post('/v1/account/verification')
->label('sdk.response.model', Response::MODEL_TOKEN)
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},email:{param-email}')
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add built-in confirm page
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients'])
->inject('request')
->inject('response')
->inject('project')

View file

@ -133,21 +133,6 @@ App::get('/v1/health/queue/webhooks')
$response->json(['size' => Resque::size(Event::WEBHOOK_QUEUE_NAME)]);
}, ['response']);
App::get('/v1/health/queue/tasks')
->desc('Get Tasks Queue')
->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueTasks')
->label('sdk.description', '/docs/references/health/get-queue-tasks.md')
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
$response->json(['size' => Resque::size(Event::TASK_QUEUE_NAME)]);
}, ['response']);
App::get('/v1/health/queue/logs')
->desc('Get Logs Queue')
->groups(['api', 'health'])

View file

@ -80,7 +80,6 @@ App::post('/v1/projects')
'platforms' => [],
'webhooks' => [],
'keys' => [],
'tasks' => [],
'domains' => [],
'usersAuthEmailPassword' => true,
'usersAuthAnonymous' => true,
@ -332,9 +331,6 @@ App::get('/v1/projects/:projectId/usage')
$documents[] = ['name' => $collection['name'], 'total' => $projectDB->getSum()];
}
// Tasks
$tasksTotal = \count($project->getAttribute('tasks', []));
$response->json([
'range' => $range,
'requests' => [
@ -369,10 +365,6 @@ App::get('/v1/projects/:projectId/usage')
'data' => [],
'total' => $usersTotal,
],
'tasks' => [
'data' => [],
'total' => $tasksTotal,
],
'storage' => [
'total' => $projectDB->getCount(
[
@ -966,238 +958,6 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
$response->noContent();
});
// Tasks
App::post('/v1/projects/:projectId/tasks')
->desc('Create Task')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createTask')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TASK)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('name', null, new Text(128), 'Task name. Max length: 128 chars.')
->param('status', null, new WhiteList(['play', 'pause'], true), 'Task status.')
->param('schedule', null, new Cron(), 'Task schedule CRON syntax.')
->param('security', false, new Boolean(true), 'Certificate verification, false for disabled or true for enabled.')
->param('httpMethod', '', new WhiteList(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS', 'TRACE', 'CONNECT'], true), 'Task HTTP method.')
->param('httpUrl', '', new URL(), 'Task HTTP URL')
->param('httpHeaders', null, new ArrayList(new Text(256)), 'Task HTTP headers list.', true)
->param('httpUser', '', new Text(256), 'Task HTTP user. Max length: 256 chars.', true)
->param('httpPass', '', new Text(256), 'Task HTTP password. Max length: 256 chars.', true)
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $name, $status, $schedule, $security, $httpMethod, $httpUrl, $httpHeaders, $httpUser, $httpPass, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception('Project not found', 404);
}
$cron = new CronExpression($schedule);
$next = ($status == 'play') ? $cron->getNextRunDate()->format('U') : null;
$security = ($security === '1' || $security === 'true' || $security === 1 || $security === true);
$task = new Document([
'$id' => $dbForConsole->getId(),
'projectId' => $project->getId(),
'name' => $name,
'status' => $status,
'schedule' => $schedule,
'updated' => \time(),
'previous' => null,
'next' => $next,
'security' => $security,
'httpMethod' => $httpMethod,
'httpUrl' => $httpUrl,
'httpHeaders' => $httpHeaders,
'httpUser' => $httpUser,
'httpPass' => $httpPass,
'log' => '{}',
'failures' => 0,
]);
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('tasks', $task, Document::SET_TYPE_APPEND)
);
if ($next) {
ResqueScheduler::enqueueAt($next, 'v1-tasks', 'TasksV1', $task->getArrayCopy());
}
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($task, Response::MODEL_TASK);
});
App::get('/v1/projects/:projectId/tasks')
->desc('List Tasks')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listTasks')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TASK_LIST)
->param('projectId', '', new UID(), 'Project unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception('Project not found', 404);
}
$tasks = $project->getAttribute('tasks', []);
$response->dynamic(new Document([
'tasks' => $tasks,
'sum' => count($tasks),
]), Response::MODEL_TASK_LIST);
});
App::get('/v1/projects/:projectId/tasks/:taskId')
->desc('Get Task')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getTask')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TASK)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('taskId', null, new UID(), 'Task unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $taskId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception('Project not found', 404);
}
$task = $project->find('$id', $taskId, 'tasks');
if (empty($task) || !$task instanceof Document) {
throw new Exception('Task not found', 404);
}
$response->dynamic($task, Response::MODEL_TASK);
});
App::put('/v1/projects/:projectId/tasks/:taskId')
->desc('Update Task')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateTask')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TASK)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('taskId', null, new UID(), 'Task unique ID.')
->param('name', null, new Text(128), 'Task name. Max length: 128 chars.')
->param('status', null, new WhiteList(['play', 'pause'], true), 'Task status.')
->param('schedule', null, new Cron(), 'Task schedule CRON syntax.')
->param('security', false, new Boolean(true), 'Certificate verification, false for disabled or true for enabled.')
->param('httpMethod', '', new WhiteList(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS', 'TRACE', 'CONNECT'], true), 'Task HTTP method.')
->param('httpUrl', '', new URL(), 'Task HTTP URL.')
->param('httpHeaders', null, new ArrayList(new Text(256)), 'Task HTTP headers list.', true)
->param('httpUser', '', new Text(256), 'Task HTTP user. Max length: 256 chars.', true)
->param('httpPass', '', new Text(256), 'Task HTTP password. Max length: 256 chars.', true)
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $taskId, $name, $status, $schedule, $security, $httpMethod, $httpUrl, $httpHeaders, $httpUser, $httpPass, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception('Project not found', 404);
}
$task = $project->find('$id', $taskId, 'tasks');
if (empty($task) || !$task instanceof Document) {
throw new Exception('Task not found', 404);
}
$cron = new CronExpression($schedule);
$next = ($status == 'play') ? $cron->getNextRunDate()->format('U') : null;
$security = ($security === '1' || $security === 'true' || $security === 1 || $security === true);
$project->findAndReplace('$id', $task->getId(), $task
->setAttribute('name', $name)
->setAttribute('status', $status)
->setAttribute('schedule', $schedule)
->setAttribute('updated', \time())
->setAttribute('next', $next)
->setAttribute('security', $security)
->setAttribute('httpMethod', $httpMethod)
->setAttribute('httpUrl', $httpUrl)
->setAttribute('httpHeaders', $httpHeaders)
->setAttribute('httpUser', $httpUser)
->setAttribute('httpPass', $httpPass)
, 'tasks');
$dbForConsole->updateDocument('projects', $project->getId(), $project);
if ($next) {
ResqueScheduler::enqueueAt($next, 'v1-tasks', 'TasksV1', $task->getArrayCopy());
}
$response->dynamic($task, Response::MODEL_TASK);
});
App::delete('/v1/projects/:projectId/tasks/:taskId')
->desc('Delete Task')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteTask')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('taskId', null, new UID(), 'Task unique ID.')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $taskId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception('Project not found', 404);
}
if (!$project->findAndRemove('$id', $taskId, 'tasks')) {
throw new Exception('Task not found', 404);
}
$dbForConsole->updateDocument('projects', $project->getId(), $project);
$response->noContent();
});
// Platforms
App::post('/v1/projects/:projectId/platforms')

View file

@ -246,7 +246,7 @@ App::post('/v1/teams/:teamId/memberships')
->param('email', '', new Email(), 'New team member email.')
->param('name', '', new Text(128), 'New team member name. Max length: 128 chars.', true)
->param('roles', [], new ArrayList(new Key()), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Max length for each role is 32 chars.')
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add our own built-in confirm page
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients'])
->inject('response')
->inject('project')
->inject('user')
@ -517,7 +517,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
$membership->setAttribute('roles', $roles);
$membership = $dbForInternal->updateDocument('memberships', $membership->getId(), $membership);
//TODO sync updated membership in the user $profile object using TYPE_REPLACE
// TODO sync updated membership in the user $profile object using TYPE_REPLACE
$audits
->setParam('userId', $user->getId())

View file

@ -447,7 +447,6 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
}
}
// TODO : Response filter implementation
$response->noContent();
});
@ -538,6 +537,5 @@ App::delete('/v1/users/:userId')
->setParam('eventData', $response->output($user, Response::MODEL_USER))
;
// TODO : Response filter implementation
$response->noContent();
});

View file

@ -44,7 +44,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
->setParam('{url}', $request->getHostname().$route->getURL())
;
//TODO make sure we get array here
// TODO make sure we get array here
foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys
if(!empty($value)) {
@ -217,7 +217,7 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
$route = $utopia->match($request);
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled'
&& $project->getId()
&& $mode !== APP_MODE_ADMIN //TODO: add check to make sure user is admin
&& $mode !== APP_MODE_ADMIN // TODO: add check to make sure user is admin
&& !empty($route->getLabel('sdk.namespace', null))) { // Don't calculate console usage on admin mode
$usage

View file

@ -189,21 +189,6 @@ App::get('/console/keys')
->setParam('body', $page);
});
App::get('/console/tasks')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->inject('layout')
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/console/tasks/index.phtml');
$layout
->setParam('title', APP_NAME.' - Tasks')
->setParam('body', $page);
});
App::get('/console/database')
->groups(['web', 'console'])
->label('permission', 'public')

View file

@ -142,16 +142,6 @@
<b class="subtitle">MANAGE</b>
<ul class="links">
<li>
<a data-ls-attrs="href=/console/tasks?project={{router.params.project}}"
data-analytics
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Tasks Link">
<i class="icon-clock"></i>
Tasks
</a>
</li>
<li>
<a data-ls-attrs="href=/console/webhooks?project={{router.params.project}}"
data-analytics

View file

@ -18,7 +18,6 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
<li class="pull-start margin-end margin-bottom-small"><a data-ls-attrs="href=/console/settings?project={{router.params.project}}" class="link-animation-enabled"><i class="icon-cog"></i> &nbsp;Settings</a> &nbsp;&nbsp;</li>
<li class="pull-start margin-end margin-bottom-small"><a data-ls-attrs="href=/console/keys?project={{router.params.project}}" class="link-animation-enabled"><i class="icon-key-inv"></i> &nbsp;API Keys</a> &nbsp;&nbsp;</li>
<li class="pull-start margin-end margin-bottom-small"><a data-ls-attrs="href=/console/webhooks?project={{router.params.project}}" class="link-animation-enabled"><i class="icon-link"></i> &nbsp;Webhooks</a> &nbsp;&nbsp;</li>
<li class="pull-start margin-end margin-bottom-small"><a data-ls-attrs="href=/console/tasks?project={{router.params.project}}" class="link-animation-enabled"><i class="icon-clock"></i> &nbsp;Tasks</a> &nbsp;&nbsp;</li>
</ul>
<div class="margin-bottom phones-only">&nbsp;</div>
@ -115,25 +114,21 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
<div class="box dashboard">
<div class="row responsive">
<div class="col span-3">
<div class="col span-4">
<div class="value"><span class="sum" data-ls-bind="{{usage.documents.total|statsTotal}}" data-default="0">0</span></div>
<div class="margin-top-small"><b class="text-size-small unit">Documents</b></div>
</div>
<div class="col span-3">
<div class="col span-4">
<div class="value">
<span class="sum" data-ls-bind="{{usage.storage.total|humanFileSize}}" data-default="0">0</span>
<span data-ls-bind="{{usage.storage.total|humanFileUnit}}" class="text-size-small unit"></span>
</div>
<div class="margin-top-small"><b class="text-size-small unit">Storage</b></div>
</div>
<div class="col span-3">
<div class="col span-4">
<div class="value"><span class="sum" data-ls-bind="{{usage.users.total}}" data-default="0">0</span></div>
<div class="margin-top-small"><b class="text-size-small unit">Users</b></div>
</div>
<div class="col span-3">
<div class="value"><span class="sum" data-ls-bind="{{usage.tasks.total}}" data-default="0">0</span></div>
<div class="margin-top-small"><b class="text-size-small unit">Tasks</b></div>
</div>
</div>
</div>
</div>

View file

@ -1,355 +0,0 @@
<div class="cover margin-bottom-large">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Home</a>
<br />
<span>Tasks</span>
</h1>
</div>
<div class="zone xl"
data-service="projects.listTasks"
data-scope="console"
data-event="load,projects.createTask,projects.updateTask,projects.deleteTask"
data-name="console-tasks"
data-param-project-id="{{router.params.project}}"
data-success="trigger"
data-success-param-trigger-events="projects.listTasks">
<div data-ls-if="0 === {{console-tasks.sum}} || undefined === {{console-tasks.sum}}" class="box margin-top margin-bottom">
<h3 class="margin-bottom-small text-bold">No Tasks Found</h3>
<p class="margin-bottom-no">You haven't created any tasks for your project yet.</p>
</div>
<div class="box y-scroll margin-bottom" data-ls-if="0 != {{console-tasks.sum}}">
<table class="full vertical">
<thead>
<tr>
<th>Name</th>
<th width="140">Status</th>
<th width="180">Schedule</th>
<th width="140"></th>
</tr>
</thead>
<tbody data-ls-loop="console-tasks.tasks" data-ls-as="task" class="list">
<tr>
<td>
<div class="margin-bottom-tiny text-one-liner">
<span data-ls-attrs="title={{task.name}} ({{task.failures}} errors)" data-ls-bind="{{task.name}}"></span>
<span data-ls-if="false === {{task.security}}">
&nbsp; <span class="text-danger">SSL/TLS Disabled</span>
</span>
<span data-ls-if="0 !== {{task.failures}}">
&nbsp; <span class="text-danger" data-ls-bind="({{task.failures}} errors)"></span>
</span>
</div>
<a data-ls-attrs="href={{task.httpUrl}}" data-ls-bind="{{task.httpUrl}}" target="_blank" class="text-one-liner text-size-small"></a>
</td>
<td data-title="" style="vertical-align: middle">
<span data-ls-if="'play' === {{task.status}}">
<span class="tag green"><i class="icon-play"></i> Running &nbsp;</span>
</span>
<span data-ls-if="'play' !== {{task.status}}">
<span class="tag red"><i class="icon-pause"></i> Paused &nbsp;</span>
</span>
</td>
<td data-title="" style="vertical-align: middle" class="text-size-small text-fade">
<span data-ls-bind="Next: {{task.next|dateTime}}"></span>
<br />
<span data-ls-if="undefined !== {{task.previous}} && {{task.previous}}">
<span data-ls-bind="Prev: {{task.previous|dateTime}}"></span>
<!-- <span data-ls-if="undefined !== {{task.delay}} && 59 < {{task.delay}}" class="text-danger margin-top-tiny">
<small data-ls-bind="({{task.delay|ms2hum}} Delay)"></small>
</span> -->
</span>
<small data-ls-if="undefined === {{task.previous}}" class="text-size-small">
None.
</small>
</td>
<td data-title="">
<div data-ui-modal class="modal box sticky-footer close" data-button-alias="none" data-open-event="task-update-{{task.$id}}">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Update Task</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Project Task"
data-service="projects.updateTask"
data-scope="console"
data-event="submit"
data-success="alert,trigger"
data-success-param-alert-text="Updated task successfully"
data-success-param-trigger-events="projects.updateTask"
data-failure="alert"
data-failure-param-alert-text="Failed to update task"
data-failure-param-alert-classname="error">
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
<input type="hidden" name="taskId" data-ls-bind="{{task.$id}}" />
<label data-ls-attrs="for=name-{{task.$id}}">Name</label>
<input type="text" class="full-width" data-ls-attrs="id=name-{{task.$id}}" name="name" required autocomplete="off" data-ls-bind="{{task.name}}" maxlength="128" />
<label data-ls-attrs="for=status-{{task.$id}}" class="margin-bottom">Status
<div class="margin-top-small">
<input name="status" type="radio" checked="checked" required data-ls-bind="{{task.status}}" value="play" /> &nbsp; <span>Play</span> &nbsp;
<input name="status" type="radio" required data-ls-bind="{{task.status}}" value="pause" /> &nbsp; <span>Pause</span> &nbsp;
</div>
</label>
<label data-ls-attrs="for=schedule-{{task.$id}}">Schedule (CRON Syntax)</label>
<input type="text" class="full-width" data-ls-attrs="id=schedule-{{task.$id}}" name="schedule" required autocomplete="off" data-ls-bind="{{task.schedule}}" placeholder="* * * * *" />
<div class="row responsive thin">
<div class="col span-4">
<label data-ls-attrs="for=httpMethod-{{task.$id}}">HTTP Method</label>
<select data-ls-attrs="id=httpMethod-{{task.$id}}" name="httpMethod" required data-ls-bind="{{task.httpMethod}}">
<option value="POST">POST</option>
<option value="GET">GET</option>
<option value="PUT">PUT</option>
<option value="PATCH">PATCH</option>
<option value="DELETE">DELETE</option>
<option value="CONNECT">CONNECT</option>
<option value="HEAD">HEAD</option>
<option value="TRACE">TRACE</option>
<option value="OPTIONS">OPTIONS</option>
</select>
</div>
<div class="col span-8">
<label data-ls-attrs="for=httpUrl-{{task.$id}}">HTTP URL</label>
<input type="url" class="full-width" data-ls-attrs="id=httpUrl-{{task.$id}}" name="httpUrl" required autocomplete="off" placeholder="https://example.com/callback" data-ls-bind="{{task.httpUrl}}" />
</div>
</div>
<div class="margin-bottom toggle" data-ls-ui-open data-button-aria="Advanced Options">
<i class="icon-plus pull-end margin-top-tiny"></i>
<i class="icon-minus pull-end margin-top-tiny"></i>
<h2 class="margin-bottom">
Advanced Options
<small class="text-size-small">(optional)</small>
</h2>
<label for="httpHeaders" class="text-bold">HTTP Headers</label>
<input type="hidden" id="httpHeadersInit" name="httpHeaders" data-cast-to="array-empty">
<div class="margin-bottom-small">
<div data-ls-loop="task.httpHeaders" data-ls-as="header" style="overflow: hidden">
<div class="margin-bottom-small">
<div data-forms-remove class="row thin">
<div class="col span-10">
<input type="hidden" name="httpHeaders" data-forms-headers data-ls-bind="{{header}}" data-cast-to="array" />
</div>
<div class="col span-2">
<button type="button" data-remove class="reverse danger round pull-end"><i class="icon-cancel"></i></button>
</div>
</div>
</div>
</div>
<div data-forms-clone="" data-label="Add Header" data-first="0">
<div class="">
<div data-forms-remove class="row thin margin-bottom-small">
<div class="col span-10">
<input type="hidden" name="httpHeaders" data-forms-headers data-cast-to="array" />
</div>
<div class="col span-2">
<button type="button" data-remove class="reverse danger round pull-end"><i class="icon-cancel"></i></button>
</div>
</div>
</div>
</div>
</div>
<label data-ls-attrs="for=security-{{task.$id}}" class="margin-bottom">
<div class="margin-bottom-small text-bold">SSL / TLS (Certificate verification)</div>
<input name="security" type="radio" required data-ls-bind="{{task.security}}" value="true" data-cast-to="boolean" /> &nbsp; <span>Enabled</span>
<input name="security" type="radio" required data-ls-bind="{{task.security}}" value="false" data-cast-to="boolean" /> &nbsp; <span>Disabled</span>
</label>
<p class="margin-bottom text-size-small text-fade"><span class="text-red">Warning</span>: Untrusted or self-signed certificates may not be secure.
<a href="https://en.wikipedia.org/wiki/Self-signed_certificate" target="_blank" rel="noopener">Learn more<i class="icon-link-ext"></i></a>
</p>
<label class="text-bold">HTTP Authentication <span class="tooltip" data-tooltip="Use to secure your endpoint from untrusted sources"><i class="icon-question"></i></span> &nbsp;<small>(optional)</small></label>
<div class="row responsive thin">
<div class="col span-6 margin-bottom">
<label data-ls-attrs="for=httpUser-{{task.$id}}">User</label>
<input type="text" class="full-width margin-bottom-no" data-ls-attrs="id=httpUser-{{task.$id}}" name="httpUser" autocomplete="off" data-ls-bind="{{task.httpUser}}" />
</div>
<div class="col span-6 margin-bottom">
<label data-ls-attrs="for=httpPass-{{task.$id}}">Password</label>
<input data-forms-show-secret type="password" class="full-width margin-bottom-no" data-ls-attrs="id=httpPass-{{task.$id}}" name="httpPass" autocomplete="off" data-ls-bind="{{task.httpPass}}" />
</div>
</div>
</div>
<footer>
<button type="submit">Update</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</footer>
</form>
</div>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Project Task"
data-service="projects.deleteTask"
data-scope="console"
data-event="task-delete-{{task.$id}}"
data-confirm="Are you sure you want to delete this task?"
data-success="alert,trigger"
data-success-param-alert-text="Deleted task successfully"
data-success-param-trigger-events="projects.deleteTask"
data-failure="alert"
data-failure-param-alert-text="Failed to delete task"
data-failure-param-alert-classname="error">
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
<input type="hidden" name="taskId" data-ls-bind="{{task.$id}}" />
</form>
<button class="pull-end margin-start round desktops-only" data-ls-ui-trigger="task-update-{{task.$id}}"><i class="icon-cog"></i></button>
<button class="pull-end reverse danger round desktops-only" data-ls-ui-trigger="task-delete-{{task.$id}}"><i class="icon-trash"></i></button>
<button class="link pull-start margin-end-small tablets-only phones-only" data-ls-ui-trigger="task-update-{{task.$id}}">Update</button>
<button class="link pull-start margin-end text-danger tablets-only phones-only" data-ls-ui-trigger="task-delete-{{task.$id}}">Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="clear">
<div data-ui-modal class="modal box close sticky-footer" data-button-text="Add Task">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Add Task</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Project Task"
data-service="projects.createTask"
data-scope="console"
data-event="submit"
data-success="alert,trigger,reset"
data-success-param-alert-text="Created task successfully"
data-success-param-trigger-events="projects.createTask"
data-failure="alert"
data-failure-param-alert-text="Failed to create task"
data-failure-param-alert-classname="error">
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
<label for="name">Name</label>
<input type="text" class="full-width" id="name" name="name" required autocomplete="off" maxlength="128" />
<label data-ls-attrs="for=status" class="margin-bottom">Status
<div class="margin-top-small">
<input name="status" type="radio" value="play" checked="checked" required /> &nbsp; <span>Play</span> &nbsp;
<input name="status" type="radio" value="pause" required /> &nbsp; <span>Pause</span> &nbsp;
</div>
</label>
<label for="schedule">Schedule (CRON Syntax)</label>
<input type="text" class="full-width" id="schedule" name="schedule" required autocomplete="off" placeholder="* * * * *" />
<div class="row responsive thin">
<div class="col span-4">
<label for="httpMethod">HTTP Method</label>
<select id="httpMethod" name="httpMethod" required>
<option value="POST">POST</option>
<option value="GET">GET</option>
<option value="PUT">PUT</option>
<option value="PATCH">PATCH</option>
<option value="DELETE">DELETE</option>
<option value="CONNECT">CONNECT</option>
<option value="HEAD">HEAD</option>
<option value="TRACE">TRACE</option>
<option value="OPTIONS">OPTIONS</option>
</select>
</div>
<div class="col span-8">
<label for="httpUrl">HTTP URL</label>
<input type="url" class="full-width" id="httpUrl" name="httpUrl" required autocomplete="off" placeholder="https://example.com/callback" />
</div>
</div>
<div class="margin-bottom toggle" data-ls-ui-open data-button-aria="Advanced Options">
<i class="icon-plus pull-end margin-top-tiny"></i>
<i class="icon-minus pull-end margin-top-tiny"></i>
<h2 class="margin-bottom">
Advanced Options
<small class="text-size-small">(optional)</small>
</h2>
<label for="httpHeaders" class="text-bold">HTTP Headers</label>
<input type="hidden" id="httpHeadersInitAdd" name="httpHeaders" data-cast-to="array-empty">
<div class="margin-bottom-small">
<div data-forms-clone="" data-label="Add Header" data-first="0">
<div class="">
<div data-forms-remove class="row thin margin-bottom-small">
<div class="col span-10">
<input type="hidden" name="httpHeaders" data-forms-headers data-cast-to="array" />
</div>
<div class="col span-2">
<button type="button" data-remove class="reverse danger round pull-end"><i class="icon-cancel"></i></button>
</div>
</div>
</div>
</div>
</div>
<label for="secure" class="margin-bottom-small">
<div class="margin-bottom-small text-bold">SSL / TLS (Certificate verification)</div>
<input name="security" data-ls-attrs="id=secure-yes" type="radio" value="1" checked="checked" required /> &nbsp; <span>Enabled</span>
<input name="security" data-ls-attrs="id=secure-no" type="radio" value="0" required /> &nbsp; <span>Disabled</span>
</label>
<p class="margin-bottom text-size-small text-fade"><span class="text-red">Warning</span>: Untrusted or self-signed certificates may not be secure.
<a href="https://en.wikipedia.org/wiki/Self-signed_certificate" target="_blank" rel="noopener">Learn more<i class="icon-link-ext"></i></a>
</p>
<label class="text-bold">HTTP Authentication <span class="tooltip" data-tooltip="Use to secure your endpoint from untrusted sources"><i class="icon-question"></i></span> &nbsp;<small>(optional)</small></label>
<div class="row responsive thin">
<div class="col span-6 margin-bottom">
<label for="httpUser">User</label>
<input type="text" class="full-width margin-bottom-no" id="httpUser" name="httpUser" autocomplete="off" />
</div>
<div class="col span-6 margin-bottom">
<label for="httpPass">Password</label>
<input type="password" data-forms-show-secret class="full-width margin-bottom-no" id="httpPass" name="httpPass" autocomplete="off" />
</div>
</div>
</div>
<footer>
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</footer>
</form>
</div>
</div>
</div>

View file

@ -158,30 +158,6 @@ services:
- _APP_REDIS_USER
- _APP_REDIS_PASS
appwrite-worker-tasks:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-tasks
container_name: appwrite-worker-tasks
restart: unless-stopped
networks:
- appwrite
depends_on:
- redis
- mariadb
environment:
- _APP_ENV
- _APP_OPENSSL_KEY_V1
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
appwrite-worker-deletes:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-deletes

View file

@ -120,7 +120,7 @@ Console::info(count($list)." functions listed in " . ($executionEnd - $execution
* 7. Trigger usage log - DONE
*/
//TODO avoid scheduled execution if delay is bigger than X offest
// TODO avoid scheduled execution if delay is bigger than X offest
class FunctionsV1 extends Worker
{

View file

@ -1,214 +0,0 @@
<?php
use Appwrite\Resque\Worker;
use Cron\CronExpression;
use Utopia\App;
use Utopia\Cache\Adapter\Redis;
use Utopia\Cache\Cache;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Validator\Authorization;
require_once __DIR__.'/../workers.php';
Console::title('Tasks V1 Worker');
Console::success(APP_NAME.' tasks worker v1 has started');
class TasksV1 extends Worker
{
/**
* @var array
*/
public $args = [];
public function init(): void
{
}
public function run(): void
{
$projectId = $this->args['projectId'] ?? null;
$taskId = $this->args['$id'] ?? null;
$updated = $this->args['updated'] ?? null;
$next = $this->args['next'] ?? null;
$delay = \time() - $next;
$errors = [];
$timeout = 60 * 5; // 5 minutes
$errorLimit = 5;
$logLimit = 5;
$alert = '';
$dbForConsole = $this->getConsoleDB();
/*
* 1. Get Original Task
* 2. Check for updates
* If has updates skip task and don't reschedule
* If status not equal to play skip task
* 3. Check next run date, update task and add new job at the given date
* 4. Execute task (set optional timeout)
* 5. Update task response to log
* On success reset error count
* On failure add error count
* If error count bigger than allowed change status to pause
*/
if (empty($taskId)) {
throw new Exception('Missing task $id');
}
Authorization::disable();
$project = $dbForConsole->getDocument('projects', $projectId);
Authorization::reset();
// Find the task in the $project->getAttribute('tasks') array
$taskIndex = array_search($taskId, array_column($project->getAttributes()['tasks'], '$id'));
if ($taskIndex === false) {
throw new Exception('Task Not Found');
}
$task = $project->getAttribute('tasks')[$taskIndex];
if ($task->getAttribute('updated') !== $updated) { // Task have already been rescheduled by owner
return;
}
if ($task->getAttribute('status') !== 'play') { // Skip task and don't schedule again
return;
}
// Reschedule
$cron = new CronExpression($task->getAttribute('schedule'));
$next = (int) $cron->getNextRunDate()->format('U');
$headers = (\is_array($task->getAttribute('httpHeaders', []))) ? $task->getAttribute('httpHeaders', []) : [];
$task
->setAttribute('next', $next)
->setAttribute('previous', \time())
;
ResqueScheduler::enqueueAt($next, 'v1-tasks', 'TasksV1', $task->getArrayCopy()); // Async task rescheduale
$startTime = \microtime(true);
// Execute Task
$ch = \curl_init($task->getAttribute('httpUrl'));
\curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $task->getAttribute('httpMethod'));
\curl_setopt($ch, CURLOPT_POSTFIELDS, '');
\curl_setopt($ch, CURLOPT_HEADER, 0);
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
\curl_setopt($ch, CURLOPT_USERAGENT, \sprintf(APP_USERAGENT,
App::getEnv('_APP_VERSION', 'UNKNOWN'),
App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
));
\curl_setopt(
$ch,
CURLOPT_HTTPHEADER,
\array_merge($headers, [
'X-'.APP_NAME.'-Task-ID: '.$task->getAttribute('$id', ''),
'X-'.APP_NAME.'-Task-Name: '.$task->getAttribute('name', ''),
])
);
\curl_setopt($ch, CURLOPT_HEADER, true); // we want headers
\curl_setopt($ch, CURLOPT_NOBODY, true);
\curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
if (!$task->getAttribute('security', true)) {
\curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
\curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
}
$httpUser = $task->getAttribute('httpUser');
$httpPass = $task->getAttribute('httpPass');
if (!empty($httpUser) && !empty($httpPass)) {
\curl_setopt($ch, CURLOPT_USERPWD, "$httpUser:$httpPass");
\curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
}
$response = \curl_exec($ch);
if (false === $response) {
$errors[] = \curl_error($ch).'Failed to execute task';
}
$code = \curl_getinfo($ch, CURLINFO_HTTP_CODE);
$codeFamily = \mb_substr($code, 0, 1);
$headersSize = \curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = \substr($response, 0, $headersSize);
$body = \substr($response, $headersSize);
\curl_close($ch);
$totalTime = \round(\microtime(true) - $startTime, 2);
switch ($codeFamily) {
case '2':
case '3':
break;
default:
$errors[] = 'Request failed with status code '.$code;
}
if (empty($errors)) {
$task->setAttribute('failures', 0);
$alert = 'Task "'.$task->getAttribute('name').'" Executed Successfully';
} else {
$task
->setAttribute('failures', $task->getAttribute('failures', 0) + 1)
->setAttribute('status', ($task->getAttribute('failures') >= $errorLimit) ? 'pause' : 'play')
;
$alert = 'Task "'.$task->getAttribute('name').'" failed to execute with the following errors: '.\implode("\n", $errors);
}
$log = \json_decode($task->getAttribute('log', '{}'), true);
if (\count($log) >= $logLimit) {
\array_pop($log);
}
\array_unshift($log, [
'code' => $code,
'duration' => $totalTime,
'delay' => $delay,
'errors' => $errors,
'headers' => $headers,
'body' => $body,
]);
$task
->setAttribute('log', \json_encode($log))
->setAttribute('duration', $totalTime)
->setAttribute('delay', $delay)
;
$project->findAndReplace('$id', $task->getId(), $task);
Authorization::disable();
if (false === $dbForConsole->updateDocument('projects', $project->getId(), $project)) {
throw new Exception('Failed saving tasks to DB');
}
Authorization::reset();
// ResqueScheduler::enqueueAt($next, 'v1-tasks', 'TasksV1', $task->getArrayCopy()); // Sync task rescheduale
// Send alert if needed (use SMTP as default for now)
return;
}
public function shutdown(): void
{
}
}

View file

@ -1,10 +0,0 @@
#!/bin/sh
if [ -z "$_APP_REDIS_USER" ] && [ -z "$_APP_REDIS_PASS" ]
then
REDIS_BACKEND="${_APP_REDIS_HOST}:${_APP_REDIS_PORT}"
else
REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}"
fi
INTERVAL=0.1 QUEUE='v1-tasks' APP_INCLUDE='/usr/src/code/app/workers/tasks.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php

View file

@ -191,33 +191,6 @@ services:
- _APP_REDIS_USER
- _APP_REDIS_PASS
appwrite-worker-tasks:
entrypoint: worker-tasks
container_name: appwrite-worker-tasks
build:
context: .
networks:
- appwrite
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
environment:
- _APP_ENV
- _APP_OPENSSL_KEY_V1
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
appwrite-worker-deletes:
entrypoint: worker-deletes
container_name: appwrite-worker-deletes

View file

@ -304,7 +304,7 @@ class MySQL extends Adapter
$st2->execute();
}
//TODO remove this dependency (check if related to nested documents)
// TODO remove this dependency (check if related to nested documents)
$this->getRedis()->expire($this->getNamespace().':document-'.$data['$id'], 0);
$this->getRedis()->expire($this->getNamespace().':document-'.$data['$id'], 0);

View file

@ -18,9 +18,8 @@ class Database
const SYSTEM_COLLECTION_PROJECTS = 'projects';
const SYSTEM_COLLECTION_WEBHOOKS = 'webhooks';
const SYSTEM_COLLECTION_KEYS = 'keys';
const SYSTEM_COLLECTION_TASKS = 'tasks';
const SYSTEM_COLLECTION_PLATFORMS = 'platforms';
const SYSTEM_COLLECTION_USAGES = 'usages'; //TODO add structure
const SYSTEM_COLLECTION_USAGES = 'usages'; // TODO add structure
const SYSTEM_COLLECTION_DOMAINS = 'domains';
const SYSTEM_COLLECTION_CERTIFICATES = 'certificates';
const SYSTEM_COLLECTION_RESERVED = 'reserved';

View file

@ -28,7 +28,6 @@ class Event
const WEBHOOK_QUEUE_NAME = 'v1-webhooks';
const WEBHOOK_CLASS_NAME = 'WebhooksV1';
const TASK_QUEUE_NAME = 'v1-tasks';
const TASK_CLASS_NAME = 'TasksV1';
const CERTIFICATES_QUEUE_NAME = 'v1-certificates';

View file

@ -68,9 +68,9 @@ class V05 extends Migration
$document->setAttribute('security', $document->getAttribute('security') ? true : false);
break;
case Database::SYSTEM_COLLECTION_TASKS:
$document->setAttribute('security', $document->getAttribute('security') ? true : false);
break;
// case Database::SYSTEM_COLLECTION_TASKS:
// $document->setAttribute('security', $document->getAttribute('security') ? true : false);
// break;
case Database::SYSTEM_COLLECTION_USERS:
foreach ($providers as $key => $provider) {

View file

@ -120,8 +120,6 @@ class Response extends SwooleResponse
const MODEL_WEBHOOK_LIST = 'webhookList';
const MODEL_KEY = 'key';
const MODEL_KEY_LIST = 'keyList';
const MODEL_TASK = 'task';
const MODEL_TASK_LIST = 'taskList';
const MODEL_PLATFORM = 'platform';
const MODEL_PLATFORM_LIST = 'platformList';
const MODEL_DOMAIN = 'domain';
@ -170,7 +168,6 @@ class Response extends SwooleResponse
->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false))
->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK, true, false))
->setModel(new BaseList('API Keys List', self::MODEL_KEY_LIST, 'keys', self::MODEL_KEY, true, false))
->setModel(new BaseList('Tasks List', self::MODEL_TASK_LIST, 'tasks', self::MODEL_TASK, true, false))
->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM, true, false))
->setModel(new BaseList('Domains List', self::MODEL_DOMAIN_LIST, 'domains', self::MODEL_DOMAIN, true, false))
->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY))
@ -201,7 +198,6 @@ class Response extends SwooleResponse
->setModel(new Project())
->setModel(new Webhook())
->setModel(new Key())
->setModel(new Task())
->setModel(new Domain())
->setModel(new Platform())
->setModel(new Country())

View file

@ -123,13 +123,6 @@ class Project extends Model
'example' => new stdClass,
'array' => true,
])
->addRule('tasks', [
'type' => Response::MODEL_TASK,
'description' => 'List of Tasks.',
'default' => [],
'example' => new stdClass,
'array' => true,
])
;
$providers = Config::getParam('providers', []);

View file

@ -1,143 +0,0 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Task extends Model
{
/**
* @var bool
*/
protected $public = false;
public function __construct()
{
$this
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Task ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('projectId', [
'type' => self::TYPE_STRING,
'description' => 'Project ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'Task name.',
'default' => '',
'example' => 'My Task',
])
->addRule('security', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Indicated if SSL / TLS Certificate verification is enabled.',
'default' => true,
'example' => true,
])
->addRule('httpMethod', [
'type' => self::TYPE_STRING,
'description' => 'Task HTTP Method.',
'default' => '',
'example' => 'POST',
])
->addRule('httpUrl', [
'type' => self::TYPE_STRING,
'description' => 'Task HTTP URL.',
'default' => '',
'example' => 'https://example.com/task',
])
->addRule('httpHeaders', [
'type' => self::TYPE_STRING,
'description' => 'Task HTTP headers.',
'default' => [],
'example' => 'key:value',
'array' => true,
])
->addRule('httpUser', [
'type' => self::TYPE_STRING,
'description' => 'HTTP basic authentication username.',
'default' => '',
'example' => 'username',
])
->addRule('httpPass', [
'type' => self::TYPE_STRING,
'description' => 'HTTP basic authentication password.',
'default' => '',
'example' => 'password',
])
->addRule('duration', [
'type' => self::TYPE_FLOAT,
'description' => 'Task duration in seconds.',
'default' => 0,
'example' => 1.2,
])
->addRule('delay', [
'type' => self::TYPE_FLOAT,
'description' => 'Task delay time in seconds.',
'default' => 0,
'example' => 1.2,
])
->addRule('failures', [
'type' => self::TYPE_INTEGER,
'description' => 'Number of recurring task failures.',
'default' => 0,
'example' => 0,
])
->addRule('schedule', [
'type' => self::TYPE_STRING,
'description' => 'Task schedule in CRON syntax.',
'default' => '',
'example' => '* * * * *',
])
->addRule('status', [
'type' => self::TYPE_STRING,
'description' => 'Task status. Possible values: play, pause',
'default' => '',
'example' => 'enabled',
])
->addRule('updated', [
'type' => self::TYPE_INTEGER,
'description' => 'Task last updated time in Unix timestamp.',
'default' => 0,
'example' => 1592981250,
])
->addRule('previous', [
'type' => self::TYPE_INTEGER,
'description' => 'Task previous run time in Unix timestamp.',
'default' => 0,
'example' => 1592981250,
])
->addRule('next', [
'type' => self::TYPE_INTEGER,
'description' => 'Task next run time in Unix timestamp.',
'default' => 0,
'example' => 1592981650,
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Task';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_TASK;
}
}

View file

@ -379,7 +379,7 @@ trait AccountBase
return $data;
}
//TODO Add tests for OAuth2 session creation
// TODO Add tests for OAuth2 session creation
/**
* @depends testCreateAccountSession

View file

@ -119,27 +119,6 @@ class HealthCustomServerTest extends Scope
return [];
}
public function testTasksSuccess():array
{
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_GET, '/health/queue/tasks', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsInt($response['body']['size']);
$this->assertLessThan(100, $response['body']['size']);
/**
* Test for FAILURE
*/
return [];
}
public function testLogsSuccess():array
{
/**

View file

@ -45,7 +45,6 @@ class ProjectsConsoleClientTest extends Scope
$this->assertArrayHasKey('platforms', $response['body']);
$this->assertArrayHasKey('webhooks', $response['body']);
$this->assertArrayHasKey('keys', $response['body']);
$this->assertArrayHasKey('tasks', $response['body']);
$projectId = $response['body']['$id'];
@ -164,7 +163,6 @@ class ProjectsConsoleClientTest extends Scope
$this->assertArrayHasKey('network', $response['body']);
$this->assertArrayHasKey('requests', $response['body']);
$this->assertArrayHasKey('storage', $response['body']);
$this->assertArrayHasKey('tasks', $response['body']);
$this->assertArrayHasKey('users', $response['body']);
$this->assertIsArray($response['body']['collections']['data']);
$this->assertIsInt($response['body']['collections']['total']);
@ -175,8 +173,6 @@ class ProjectsConsoleClientTest extends Scope
$this->assertIsArray($response['body']['requests']['data']);
$this->assertIsInt($response['body']['requests']['total']);
$this->assertIsInt($response['body']['storage']['total']);
$this->assertIsArray($response['body']['tasks']['data']);
$this->assertIsInt($response['body']['tasks']['total']);
$this->assertIsArray($response['body']['users']['data']);
$this->assertIsInt($response['body']['users']['total']);
@ -224,7 +220,6 @@ class ProjectsConsoleClientTest extends Scope
$this->assertArrayHasKey('platforms', $response['body']);
$this->assertArrayHasKey('webhooks', $response['body']);
$this->assertArrayHasKey('keys', $response['body']);
$this->assertArrayHasKey('tasks', $response['body']);
$projectId = $response['body']['$id'];
@ -933,400 +928,6 @@ class ProjectsConsoleClientTest extends Scope
return $data;
}
// Tasks
/**
* @depends testCreateProject
*/
public function testCreateProjectTask($data): array
{
$id = $data['projectId'] ?? '';
$response = $this->client->call(Client::METHOD_POST, '/projects/'.$id.'/tasks', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Task Test',
'status' => 'play',
'schedule' => '* * * * *',
'security' => true,
'httpMethod' => 'GET',
'httpUrl' => 'http://example.com',
'httpHeaders' => ['demo:value'],
'httpUser' => 'username',
'httpPass' => 'password',
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals('Task Test', $response['body']['name']);
$this->assertEquals('play', $response['body']['status']);
$this->assertEquals(true, $response['body']['security']);
$this->assertEquals('* * * * *', $response['body']['schedule']);
$this->assertEquals('GET', $response['body']['httpMethod']);
$this->assertEquals('http://example.com', $response['body']['httpUrl']);
$this->assertContains('demo:value', $response['body']['httpHeaders']);
$this->assertCount(1, $response['body']['httpHeaders']);
$this->assertEquals('username', $response['body']['httpUser']);
$this->assertEquals('password', $response['body']['httpPass']);
$data = array_merge($data, ['taskId' => $response['body']['$id']]);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_POST, '/projects/'.$id.'/tasks', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Task Test',
'status' => 'unknown',
'schedule' => '* * * * *',
'security' => true,
'httpMethod' => 'GET',
'httpUrl' => 'http://example.com',
'httpHeaders' => ['demo:value'],
'httpUser' => 'username',
'httpPass' => 'password',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_POST, '/projects/'.$id.'/tasks', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Task Test',
'status' => 'play',
'schedule' => 'unknown',
'security' => true,
'httpMethod' => 'GET',
'httpUrl' => 'http://example.com',
'httpHeaders' => ['demo:value'],
'httpUser' => 'username',
'httpPass' => 'password',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_POST, '/projects/'.$id.'/tasks', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Task Test',
'status' => 'play',
'schedule' => '* * * * *',
'security' => 'string',
'httpMethod' => 'GET',
'httpUrl' => 'http://example.com',
'httpHeaders' => ['demo:value'],
'httpUser' => 'username',
'httpPass' => 'password',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_POST, '/projects/'.$id.'/tasks', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Task Test',
'status' => 'play',
'schedule' => '* * * * *',
'security' => true,
'httpMethod' => 'UNKNOWN',
'httpUrl' => 'http://example.com',
'httpHeaders' => ['demo:value'],
'httpUser' => 'username',
'httpPass' => 'password',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_POST, '/projects/'.$id.'/tasks', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Task Test',
'status' => 'play',
'schedule' => '* * * * *',
'security' => true,
'httpMethod' => 'GET',
'httpUrl' => 'http://example.com',
'httpHeaders' => 'string',
'httpUser' => 'username',
'httpPass' => 'password',
]);
$this->assertEquals(400, $response['headers']['status-code']);
return $data;
}
/**
* @depends testCreateProjectTask
*/
public function testListProjectTask($data): array
{
$id = $data['projectId'] ?? '';
$response = $this->client->call(Client::METHOD_GET, '/projects/'.$id.'/tasks', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['sum']);
/**
* Test for FAILURE
*/
return $data;
}
/**
* @depends testCreateProjectTask
*/
public function testGetProjectTask($data): array
{
$id = $data['projectId'] ?? '';
$taskId = $data['taskId'] ?? '';
$response = $this->client->call(Client::METHOD_GET, '/projects/'.$id.'/tasks/'.$taskId, 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->assertEquals($taskId, $response['body']['$id']);
$this->assertEquals('Task Test', $response['body']['name']);
$this->assertEquals('play', $response['body']['status']);
$this->assertEquals(true, $response['body']['security']);
$this->assertEquals('* * * * *', $response['body']['schedule']);
$this->assertEquals('GET', $response['body']['httpMethod']);
$this->assertEquals('http://example.com', $response['body']['httpUrl']);
$this->assertContains('demo:value', $response['body']['httpHeaders']);
$this->assertCount(1, $response['body']['httpHeaders']);
$this->assertEquals('username', $response['body']['httpUser']);
$this->assertEquals('password', $response['body']['httpPass']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/projects/'.$id.'/tasks/error', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
$this->assertEquals(404, $response['headers']['status-code']);
return $data;
}
/**
* @depends testCreateProjectTask
*/
public function testUpdateProjectTask($data): array
{
$id = $data['projectId'] ?? '';
$taskId = $data['taskId'] ?? '';
$response = $this->client->call(Client::METHOD_PUT, '/projects/'.$id.'/tasks/'.$taskId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Task Test 2',
'status' => 'pause',
'schedule' => '*/5 * * * *',
'security' => false,
'httpMethod' => 'POST',
'httpUrl' => 'http://example.com/demo',
'httpHeaders' => ['demo1: value1', 'demo2:value2'],
'httpUser' => 'username1',
'httpPass' => 'password1',
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals($taskId, $response['body']['$id']);
$this->assertEquals('Task Test 2', $response['body']['name']);
$this->assertEquals('pause', $response['body']['status']);
$this->assertEquals(false, $response['body']['security']);
$this->assertEquals('*/5 * * * *', $response['body']['schedule']);
$this->assertEquals('POST', $response['body']['httpMethod']);
$this->assertEquals('http://example.com/demo', $response['body']['httpUrl']);
$this->assertContains('demo1: value1', $response['body']['httpHeaders']);
$this->assertContains('demo2:value2', $response['body']['httpHeaders']);
$this->assertCount(2, $response['body']['httpHeaders']);
$this->assertEquals('username1', $response['body']['httpUser']);
$this->assertEquals('password1', $response['body']['httpPass']);
$response = $this->client->call(Client::METHOD_GET, '/projects/'.$id.'/tasks/'.$taskId, 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->assertEquals($taskId, $response['body']['$id']);
$this->assertEquals('Task Test 2', $response['body']['name']);
$this->assertEquals('pause', $response['body']['status']);
$this->assertEquals(false, $response['body']['security']);
$this->assertEquals('*/5 * * * *', $response['body']['schedule']);
$this->assertEquals('POST', $response['body']['httpMethod']);
$this->assertEquals('http://example.com/demo', $response['body']['httpUrl']);
$this->assertContains('demo1: value1', $response['body']['httpHeaders']);
$this->assertContains('demo2:value2', $response['body']['httpHeaders']);
$this->assertCount(2, $response['body']['httpHeaders']);
$this->assertEquals('username1', $response['body']['httpUser']);
$this->assertEquals('password1', $response['body']['httpPass']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_PUT, '/projects/'.$id.'/tasks/'.$taskId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Task Test 2',
'status' => 'pause1',
'schedule' => '* * * * *',
'security' => false,
'httpMethod' => 'POST',
'httpUrl' => 'http://example.com/demo',
'httpHeaders' => ['demo1: value1', 'demo2:value2'],
'httpUser' => 'username1',
'httpPass' => 'password1',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_PUT, '/projects/'.$id.'/tasks/'.$taskId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Task Test 2',
'status' => 'pause',
'schedule' => '* * * * *xxx',
'security' => false,
'httpMethod' => 'POST',
'httpUrl' => 'http://example.com/demo',
'httpHeaders' => ['demo1: value1', 'demo2:value2'],
'httpUser' => 'username1',
'httpPass' => 'password1',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_PUT, '/projects/'.$id.'/tasks/'.$taskId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Task Test 2',
'status' => 'pause',
'schedule' => '* * * * *',
'security' => 'string',
'httpMethod' => 'POST',
'httpUrl' => 'http://example.com/demo',
'httpHeaders' => ['demo1: value1', 'demo2:value2'],
'httpUser' => 'username1',
'httpPass' => 'password1',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_PUT, '/projects/'.$id.'/tasks/'.$taskId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Task Test 2',
'status' => 'pause',
'schedule' => '* * * * *',
'security' => false,
'httpMethod' => 'UNKNOWN',
'httpUrl' => 'http://example.com/demo',
'httpHeaders' => ['demo1: value1', 'demo2:value2'],
'httpUser' => 'username1',
'httpPass' => 'password1',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_PUT, '/projects/'.$id.'/tasks/'.$taskId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Task Test 2',
'status' => 'pause',
'schedule' => '* * * * *',
'security' => false,
'httpMethod' => 'POST',
'httpUrl' => 'example.com/demo',
'httpHeaders' => ['demo1: value1', 'demo2:value2'],
'httpUser' => 'username1',
'httpPass' => 'password1',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_PUT, '/projects/'.$id.'/tasks/'.$taskId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Task Test 2',
'status' => 'pause',
'schedule' => '* * * * *',
'security' => false,
'httpMethod' => 'POST',
'httpUrl' => 'http://example.com/demo',
'httpHeaders' => 'string',
'httpUser' => 'username1',
'httpPass' => 'password1',
]);
$this->assertEquals(400, $response['headers']['status-code']);
return $data;
}
/**
* @depends testCreateProjectTask
*/
public function testDeleteProjectTask($data): array
{
$id = $data['projectId'] ?? '';
$taskId = $data['taskId'] ?? '';
$response = $this->client->call(Client::METHOD_DELETE, '/projects/'.$id.'/tasks/'.$taskId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
$this->assertEquals(204, $response['headers']['status-code']);
$this->assertEmpty($response['body']);
$response = $this->client->call(Client::METHOD_GET, '/projects/'.$id.'/tasks/'.$taskId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
$this->assertEquals(404, $response['headers']['status-code']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_DELETE, '/projects/'.$id.'/tasks/error', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
$this->assertEquals(404, $response['headers']['status-code']);
return $data;
}
// Platforms
/**
@ -1659,7 +1260,7 @@ class ProjectsConsoleClientTest extends Scope
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_DELETE, '/projects/'.$id.'/tasks/error', array_merge([
$response = $this->client->call(Client::METHOD_DELETE, '/projects/'.$id.'/webhooks/error', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);

View file

@ -45,7 +45,6 @@ class WebhooksTest extends Scope
$this->assertArrayHasKey('platforms', $response['body']);
$this->assertArrayHasKey('webhooks', $response['body']);
$this->assertArrayHasKey('keys', $response['body']);
$this->assertArrayHasKey('tasks', $response['body']);
$projectId = $response['body']['$id'];

View file

@ -43,7 +43,7 @@ class MigrationV05Test extends MigrationTest
$document = $this->fixDocument(new Document([
'$uid' => 'unique',
'$collection' => Database::SYSTEM_COLLECTION_TASKS
'$collection' => Database::SYSTEM_COLLECTION_WEBHOOKS
]));
$this->assertEquals($document->getAttribute('$uid', null), null);