Merge branch 'feat-functions-refactor' of https://github.com/appwrite/appwrite into feat-func-storage-sync
This commit is contained in:
commit
5eae00df2c
|
@ -1541,6 +1541,17 @@ $collections = [
|
|||
'array' => false,
|
||||
'filters' => ['encrypt'],
|
||||
],
|
||||
[
|
||||
'$id' => 'search',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
|
@ -1564,6 +1575,13 @@ $collections = [
|
|||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -144,7 +144,7 @@ App::get('/v1/avatars/image')
|
|||
->label('sdk.description', '/docs/references/avatars/get-image.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE)
|
||||
->param('url', '', new URL(), 'Image URL which you want to crop.')
|
||||
->param('url', '', new URL(['http', 'https']), 'Image URL which you want to crop.')
|
||||
->param('width', 400, new Range(0, 2000), 'Resize preview image width, Pass an integer between 0 to 2000.', true)
|
||||
->param('height', 400, new Range(0, 2000), 'Resize preview image height, Pass an integer between 0 to 2000.', true)
|
||||
->inject('response')
|
||||
|
@ -213,7 +213,7 @@ App::get('/v1/avatars/favicon')
|
|||
->label('sdk.description', '/docs/references/avatars/get-favicon.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE)
|
||||
->param('url', '', new URL(), 'Website URL which you want to fetch the favicon from.')
|
||||
->param('url', '', new URL(['http', 'https']), 'Website URL which you want to fetch the favicon from.')
|
||||
->inject('response')
|
||||
->action(function ($url, $response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
|
|
|
@ -585,7 +585,7 @@ App::post('/v1/projects/:projectId/webhooks')
|
|||
->param('projectId', null, new UID(), 'Project unique ID.')
|
||||
->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.')
|
||||
->param('events', null, new ArrayList(new WhiteList(array_keys(Config::getParam('events'), true), true)), 'Events list.')
|
||||
->param('url', null, new URL(), 'Webhook URL.')
|
||||
->param('url', null, new URL(['http', 'https']), 'Webhook URL.')
|
||||
->param('security', false, new Boolean(true), 'Certificate verification, false for disabled or true for enabled.')
|
||||
->param('httpUser', '', new Text(256), 'Webhook HTTP user. Max length: 256 chars.', true)
|
||||
->param('httpPass', '', new Text(256), 'Webhook HTTP password. Max length: 256 chars.', true)
|
||||
|
@ -707,7 +707,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
|||
->param('webhookId', null, new UID(), 'Webhook unique ID.')
|
||||
->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.')
|
||||
->param('events', null, new ArrayList(new WhiteList(array_keys(Config::getParam('events'), true), true)), 'Events list.')
|
||||
->param('url', null, new URL(), 'Webhook URL.')
|
||||
->param('url', null, new URL(['http', 'https']), 'Webhook URL.')
|
||||
->param('security', false, new Boolean(true), 'Certificate verification, false for disabled or true for enabled.')
|
||||
->param('httpUser', '', new Text(256), 'Webhook HTTP user. Max length: 256 chars.', true)
|
||||
->param('httpPass', '', new Text(256), 'Webhook HTTP password. Max length: 256 chars.', true)
|
||||
|
|
|
@ -63,7 +63,9 @@ App::post('/v1/teams')
|
|||
])));
|
||||
|
||||
if (!$isPrivilegedUser && !$isAppUser) { // Don't add user on server mode
|
||||
$membershipId = $dbForProject->getId();
|
||||
$membership = new Document([
|
||||
'$id' => $membershipId,
|
||||
'$read' => ['user:'.$user->getId(), 'team:'.$team->getId()],
|
||||
'$write' => ['user:'.$user->getId(), 'team:'.$team->getId().'/owner'],
|
||||
'userId' => $user->getId(),
|
||||
|
@ -73,6 +75,7 @@ App::post('/v1/teams')
|
|||
'joined' => \time(),
|
||||
'confirm' => true,
|
||||
'secret' => '',
|
||||
'search' => implode(' ', [$membershipId, $user->getId()])
|
||||
]);
|
||||
|
||||
$membership = $dbForProject->createDocument('memberships', $membership);
|
||||
|
@ -353,8 +356,9 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
|
||||
$secret = Auth::tokenGenerator();
|
||||
|
||||
$membershipId = $dbForProject->getId();
|
||||
$membership = new Document([
|
||||
'$id' => $dbForProject->getId(),
|
||||
'$id' => $membershipId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['user:'.$invitee->getId(), 'team:'.$team->getId().'/owner'],
|
||||
'userId' => $invitee->getId(),
|
||||
|
@ -364,6 +368,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
'joined' => ($isPrivilegedUser || $isAppUser) ? \time() : 0,
|
||||
'confirm' => ($isPrivilegedUser || $isAppUser),
|
||||
'secret' => Auth::hash($secret),
|
||||
'search' => implode(' ', [$membershipId, $invitee->getId()])
|
||||
]);
|
||||
|
||||
if ($isPrivilegedUser || $isAppUser) { // Allow admin to create membership
|
||||
|
@ -458,8 +463,27 @@ App::get('/v1/teams/:teamId/memberships')
|
|||
}
|
||||
}
|
||||
|
||||
$memberships = $dbForProject->find('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], $limit, $offset, [], [$orderType], $cursorMembership ?? null, $cursorDirection);
|
||||
$sum = $dbForProject->count('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], APP_LIMIT_COUNT);
|
||||
$queries = [new Query('teamId', Query::TYPE_EQUAL, [$teamId])];
|
||||
|
||||
if (!empty($search)) {
|
||||
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
|
||||
}
|
||||
|
||||
$memberships = $dbForProject->find(
|
||||
collection: 'memberships',
|
||||
queries: $queries,
|
||||
limit: $limit,
|
||||
offset: $offset,
|
||||
orderTypes: [$orderType],
|
||||
cursor: $cursorMembership ?? null,
|
||||
cursorDirection: $cursorDirection
|
||||
);
|
||||
|
||||
$sum = $dbForProject->count(
|
||||
collection:'memberships',
|
||||
queries: $queries,
|
||||
max: APP_LIMIT_COUNT
|
||||
);
|
||||
|
||||
$memberships = array_filter($memberships, fn(Document $membership) => !empty($membership->getAttribute('userId')));
|
||||
|
||||
|
@ -565,25 +589,40 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
|||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
$isOwner = Authorization::isRole('team:'.$team->getId().'/owner');;
|
||||
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');;
|
||||
|
||||
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
|
||||
throw new Exception('User is not allowed to modify roles', 401);
|
||||
}
|
||||
|
||||
// Update the roles
|
||||
/**
|
||||
* Update the roles
|
||||
*/
|
||||
$membership->setAttribute('roles', $roles);
|
||||
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership);
|
||||
|
||||
// TODO sync updated membership in the user $profile object using TYPE_REPLACE
|
||||
/**
|
||||
* Replace membership on profile
|
||||
*/
|
||||
$memberships = array_filter($profile->getAttribute('memberships'), fn (Document $m) => $m->getId() !== $membership->getId());
|
||||
|
||||
$profile
|
||||
->setAttribute('memberships', $memberships)
|
||||
->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND);
|
||||
|
||||
Authorization::skip(fn () => $dbForProject->updateDocument('users', $profile->getId(), $profile));
|
||||
|
||||
$audits
|
||||
->setParam('userId', $user->getId())
|
||||
->setParam('event', 'teams.memberships.update')
|
||||
->setParam('resource', 'team/'.$teamId)
|
||||
;
|
||||
->setParam('resource', 'team/' . $teamId);
|
||||
|
||||
$response->dynamic($membership, Response::MODEL_MEMBERSHIP);
|
||||
$response->dynamic(
|
||||
$membership
|
||||
->setAttribute('email', $profile->getAttribute('email'))
|
||||
->setAttribute('name', $profile->getAttribute('name')),
|
||||
Response::MODEL_MEMBERSHIP
|
||||
);
|
||||
});
|
||||
|
||||
App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
|
|
|
@ -21,6 +21,7 @@ use Utopia\Storage\Device\S3;
|
|||
use Utopia\Storage\Storage;
|
||||
use Utopia\Swoole\Request;
|
||||
use Utopia\Swoole\Response;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Range as ValidatorRange;
|
||||
use Utopia\Validator\Text;
|
||||
|
@ -142,13 +143,14 @@ App::post('/v1/runtimes')
|
|||
->param('runtimeId', '', new Text(62), 'Unique runtime ID.')
|
||||
->param('source', '', new Text(0), 'Path to source files.')
|
||||
->param('destination', '', new Text(0), 'Destination folder to store build files into.')
|
||||
->param('vars', '', new Assoc(), 'Environment Variables required for the build')
|
||||
->param('vars', [], new Assoc(), 'Environment Variables required for the build')
|
||||
->param('commands', [], new ArrayList(new Text(0)), 'Commands required to build the container')
|
||||
->param('runtime', '', new Text(128), 'Runtime for the cloud function')
|
||||
->param('baseImage', '', new Text(128), 'Base image name of the runtime')
|
||||
->inject('orchestrationPool')
|
||||
->inject('activeRuntimes')
|
||||
->inject('response')
|
||||
->action(function (string $runtimeId, string $source, string $destination, array $vars, string $runtime, string $baseImage, $orchestrationPool, $activeRuntimes, Response $response) {
|
||||
->action(function (string $runtimeId, string $source, string $destination, array $vars, array $commands, string $runtime, string $baseImage, $orchestrationPool, $activeRuntimes, Response $response) {
|
||||
|
||||
$container = 'r-' . $runtimeId;
|
||||
|
||||
|
@ -197,7 +199,7 @@ App::post('/v1/runtimes')
|
|||
$container = 'b-' . $runtimeId;
|
||||
$vars = array_map(fn ($v) => strval($v), $vars);
|
||||
$orchestration
|
||||
->setCpus(App::getEnv('_APP_FUNCTIONS_CPUS', 0))
|
||||
->setCpus(App::getEnv('_APP_FUNCTIONS_CPUS', 1))
|
||||
->setMemory(App::getEnv('_APP_FUNCTIONS_MEMORY', 256))
|
||||
->setSwap(App::getEnv('_APP_FUNCTIONS_MEMORY_SWAP', 256));
|
||||
|
||||
|
@ -231,61 +233,21 @@ App::post('/v1/runtimes')
|
|||
/**
|
||||
* Extract user code into build container
|
||||
*/
|
||||
$untarStdout = '';
|
||||
$untarStderr = '';
|
||||
$untarSuccess = $orchestration->execute(
|
||||
$status = $orchestration->execute(
|
||||
name: $container,
|
||||
command: [
|
||||
'sh',
|
||||
'-c',
|
||||
'mkdir -p /usr/code && cp /tmp/code.tar.gz /usr/workspace/code.tar.gz && cd /usr/workspace/ && tar -zxf /usr/workspace/code.tar.gz -C /usr/code && rm /usr/workspace/code.tar.gz'
|
||||
],
|
||||
stdout: $untarStdout,
|
||||
stderr: $untarStderr,
|
||||
timeout: 60
|
||||
);
|
||||
|
||||
if (!$untarSuccess) {
|
||||
throw new Exception('Failed to extract tarfile ' . $untarStderr, 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build code and install dependenices
|
||||
*/
|
||||
$buildSuccess = $orchestration->execute(
|
||||
name: $container,
|
||||
command: ['sh', '-c', 'cd /usr/local/src && ./build.sh'],
|
||||
command: $commands,
|
||||
stdout: $buildStdout,
|
||||
stderr: $buildStderr,
|
||||
timeout: App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900)
|
||||
timeout: App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)
|
||||
);
|
||||
|
||||
if (!$buildSuccess) {
|
||||
throw new Exception('Failed to build dependencies: ' . $buildStderr, 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repackage code and save
|
||||
*/
|
||||
$compressStdout = '';
|
||||
$compressStderr = '';
|
||||
$compressSuccess = $orchestration->execute(
|
||||
name: $container,
|
||||
command: [
|
||||
'tar', '-C', '/usr/code', '-czvf', '/usr/builds/code.tar.gz', './'
|
||||
],
|
||||
stdout: $compressStdout,
|
||||
stderr: $compressStderr,
|
||||
timeout: 60
|
||||
);
|
||||
|
||||
if (!$compressSuccess) {
|
||||
throw new Exception('Failed to compress built code: ' . $compressStderr, 500);
|
||||
if (!$status) {
|
||||
throw new Exception('Failed to build dependenices ' . $buildStderr, 500);
|
||||
}
|
||||
|
||||
// Check if the build was successful by checking if file exists
|
||||
if (!\file_exists($tmpBuild)) {
|
||||
throw new Exception('Something went wrong during the build process');
|
||||
throw new Exception('Something went wrong during the build process', 500);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -311,8 +273,8 @@ App::post('/v1/runtimes')
|
|||
$build = [
|
||||
'outputPath' => $outputPath,
|
||||
'status' => 'ready',
|
||||
'stdout' => \utf8_encode(\mb_substr($buildStdout, -4096)),
|
||||
'stderr' => \utf8_encode(\mb_substr($buildStderr, -4096)),
|
||||
'stdout' => \utf8_encode($buildStdout),
|
||||
'stderr' => \utf8_encode($buildStderr),
|
||||
'startTime' => $buildStart,
|
||||
'endTime' => $buildEnd,
|
||||
'duration' => $buildEnd - $buildStart,
|
||||
|
@ -356,9 +318,9 @@ App::post('/v1/runtimes')
|
|||
$vars = array_map(fn ($v) => strval($v), $vars);
|
||||
|
||||
$orchestration
|
||||
->setCpus(App::getEnv('_APP_FUNCTIONS_CPUS', '1'))
|
||||
->setMemory(App::getEnv('_APP_FUNCTIONS_MEMORY', '256'))
|
||||
->setSwap(App::getEnv('_APP_FUNCTIONS_MEMORY_SWAP', '256'));
|
||||
->setCpus(App::getEnv('_APP_FUNCTIONS_CPUS', 1))
|
||||
->setMemory(App::getEnv('_APP_FUNCTIONS_MEMORY', 256))
|
||||
->setSwap(App::getEnv('_APP_FUNCTIONS_MEMORY_SWAP', 256));
|
||||
|
||||
$id = $orchestration->run(
|
||||
image: $baseImage,
|
||||
|
@ -487,7 +449,7 @@ App::post('/v1/execution')
|
|||
->desc('Create an execution')
|
||||
->param('runtimeId', '', new Text(62), 'The runtimeID to execute')
|
||||
->param('path', '', new Text(0), 'Path containing the built files.', false)
|
||||
->param('vars', '', new Assoc(), 'Environment variables required for the build', false)
|
||||
->param('vars', [], new Assoc(), 'Environment variables required for the build', false)
|
||||
->param('data', '', new Text(8192), 'Data to be forwarded to the function, this is user specified.', true)
|
||||
->param('runtime', '', new Text(128), 'Runtime for the cloud function', false)
|
||||
->param('entrypoint', '', new Text(256), 'Entrypoint of the code file')
|
||||
|
|
|
@ -119,7 +119,18 @@ class BuildsV1 extends Worker
|
|||
source: $source,
|
||||
vars: $vars,
|
||||
runtime: $key,
|
||||
baseImage: $baseImage
|
||||
baseImage: $baseImage,
|
||||
commands: [
|
||||
'sh', '-c',
|
||||
'mkdir -p /usr/code && \
|
||||
cp /tmp/code.tar.gz /usr/workspace/code.tar.gz && \
|
||||
cd /usr/workspace/ && \
|
||||
tar -zxf /usr/workspace/code.tar.gz -C /usr/code && \
|
||||
rm /usr/workspace/code.tar.gz && \
|
||||
cd /usr/local/src && \
|
||||
./build.sh && \
|
||||
tar -C /usr/code -czf /usr/builds/code.tar.gz ./'
|
||||
]
|
||||
);
|
||||
|
||||
/** Update the build document */
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
"utopia-php/storage": "0.7.*",
|
||||
"utopia-php/websocket": "0.1.0",
|
||||
"utopia-php/image": "0.5.*",
|
||||
"utopia-php/orchestration": "0.3.*",
|
||||
"utopia-php/orchestration": "0.4.*",
|
||||
"resque/php-resque": "1.3.6",
|
||||
"matomo/device-detector": "5.0.1",
|
||||
"dragonmantank/cron-expression": "3.1.0",
|
||||
|
|
38
composer.lock
generated
38
composer.lock
generated
|
@ -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": "be98fbe479fad1146fb54411ebfc9a57",
|
||||
"content-hash": "70ce232aec13929c9cc7901fa34282a0",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
@ -119,11 +119,11 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/runtimes.git",
|
||||
"reference": "85053974a0d17415c0bd955ac8d6758c0d186d2b"
|
||||
"reference": "ea44eb19af851a3fe4ecf73e789f374f70d778c5"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"utopia-php/orchestration": "0.3.x",
|
||||
"utopia-php/orchestration": "0.4.*",
|
||||
"utopia-php/system": "0.4.*"
|
||||
},
|
||||
"require-dev": {
|
||||
|
@ -160,7 +160,7 @@
|
|||
"php",
|
||||
"runtimes"
|
||||
],
|
||||
"time": "2022-02-09T12:31:08+00:00"
|
||||
"time": "2022-02-17T12:06:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "chillerlan/php-qrcode",
|
||||
|
@ -2470,16 +2470,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/orchestration",
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/orchestration.git",
|
||||
"reference": "5398361b39fdb7a15b561ebf243c4cb75a68432a"
|
||||
"reference": "6f25a47e6d6b14540853b62db43c676aecf80519"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/orchestration/zipball/5398361b39fdb7a15b561ebf243c4cb75a68432a",
|
||||
"reference": "5398361b39fdb7a15b561ebf243c4cb75a68432a",
|
||||
"url": "https://api.github.com/repos/utopia-php/orchestration/zipball/6f25a47e6d6b14540853b62db43c676aecf80519",
|
||||
"reference": "6f25a47e6d6b14540853b62db43c676aecf80519",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2519,9 +2519,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/orchestration/issues",
|
||||
"source": "https://github.com/utopia-php/orchestration/tree/0.3.0"
|
||||
"source": "https://github.com/utopia-php/orchestration/tree/0.4.0"
|
||||
},
|
||||
"time": "2021-12-20T14:08:08+00:00"
|
||||
"time": "2022-02-17T11:48:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/preloader",
|
||||
|
@ -3077,11 +3077,17 @@
|
|||
},
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "dev-feat-preps-for-0.13",
|
||||
"version": "0.17.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator",
|
||||
"reference": "b977fcf357a267f41299539ac9095aa7bbcc2600"
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "37bc6fc1b4b4940c7659748d7d2d5110da5457a4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/37bc6fc1b4b4940c7659748d7d2d5110da5457a4",
|
||||
"reference": "37bc6fc1b4b4940c7659748d7d2d5110da5457a4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
|
@ -3116,7 +3122,11 @@
|
|||
}
|
||||
],
|
||||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||
"time": "2022-02-15T11:09:40+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.17.2"
|
||||
},
|
||||
"time": "2022-01-28T08:25:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/pcre",
|
||||
|
|
5
public/dist/scripts/app-all.js
vendored
5
public/dist/scripts/app-all.js
vendored
File diff suppressed because one or more lines are too long
5
public/dist/scripts/app-dep.js
vendored
5
public/dist/scripts/app-dep.js
vendored
File diff suppressed because one or more lines are too long
|
@ -764,6 +764,25 @@
|
|||
'content-type': 'application/json',
|
||||
}, payload);
|
||||
}),
|
||||
/**
|
||||
* Update Session (Refresh Tokens)
|
||||
*
|
||||
*
|
||||
* @param {string} sessionId
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
updateSession: (sessionId) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof sessionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "sessionId"');
|
||||
}
|
||||
let path = '/account/sessions/{sessionId}'.replace('{sessionId}', sessionId);
|
||||
let payload = {};
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('patch', uri, {
|
||||
'content-type': 'application/json',
|
||||
}, payload);
|
||||
}),
|
||||
/**
|
||||
* Delete Account Session
|
||||
*
|
||||
|
@ -1463,9 +1482,9 @@
|
|||
* @param {string} collectionId
|
||||
* @param {string} key
|
||||
* @param {boolean} required
|
||||
* @param {string} min
|
||||
* @param {string} max
|
||||
* @param {string} xdefault
|
||||
* @param {number} min
|
||||
* @param {number} max
|
||||
* @param {number} xdefault
|
||||
* @param {boolean} array
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
|
|
|
@ -290,7 +290,7 @@ class Realtime extends Adapter
|
|||
|
||||
$channels[] = 'documents';
|
||||
$channels[] = 'collections.' . $payload->getAttribute('$collection') . '.documents';
|
||||
$channels[] = 'documents.' . $payload->getId();
|
||||
$channels[] = 'collections.' . $payload->getAttribute('$collection') . '.documents.' . $payload->getId();
|
||||
|
||||
$roles = ($collection->getAttribute('permission') === 'collection') ? $collection->getRead() : $payload->getRead();
|
||||
|
||||
|
|
|
@ -9,10 +9,20 @@ use Utopia\Validator;
|
|||
*
|
||||
* Validate that an variable is a valid URL
|
||||
*
|
||||
* @package Utopia\Validator
|
||||
* @package Appwrite\Network\Validator
|
||||
*/
|
||||
class URL extends Validator
|
||||
{
|
||||
protected array $allowedSchemes;
|
||||
|
||||
/**
|
||||
* @param array $allowedSchemes
|
||||
*/
|
||||
public function __construct(array $allowedSchemes = [])
|
||||
{
|
||||
$this->allowedSchemes = $allowedSchemes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Description
|
||||
*
|
||||
|
@ -22,6 +32,10 @@ class URL extends Validator
|
|||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
if (!empty($this->allowedSchemes)) {
|
||||
return 'Value must be a valid URL with following schemes (' . \implode(', ', $this->allowedSchemes) . ')';
|
||||
}
|
||||
|
||||
return 'Value must be a valid URL';
|
||||
}
|
||||
|
||||
|
@ -39,6 +53,10 @@ class URL extends Validator
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!empty($this->allowedSchemes) && !\in_array(\parse_url($value, PHP_URL_SCHEME), $this->allowedSchemes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ class Func extends Model
|
|||
->addRule('execute', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Execution permissions.',
|
||||
'default' => '',
|
||||
'default' => [],
|
||||
'example' => 'role:member',
|
||||
'array' => false,
|
||||
'array' => true,
|
||||
])
|
||||
->addRule('name', [
|
||||
'type' => self::TYPE_STRING,
|
||||
|
|
|
@ -37,8 +37,9 @@ class Executor
|
|||
string $source,
|
||||
array $vars,
|
||||
string $runtime,
|
||||
string $baseImage)
|
||||
{
|
||||
string $baseImage,
|
||||
array $commands
|
||||
) {
|
||||
$route = "/runtimes";
|
||||
$headers = [
|
||||
'content-type' => 'application/json',
|
||||
|
@ -51,7 +52,8 @@ class Executor
|
|||
'destination' => APP_STORAGE_BUILDS . "/app-$projectId",
|
||||
'vars' => $vars,
|
||||
'runtime' => $runtime,
|
||||
'baseImage' => $baseImage
|
||||
'baseImage' => $baseImage,
|
||||
'commands' => $commands
|
||||
];
|
||||
|
||||
$response = $this->call(self::METHOD_POST, $route, $headers, $params, true, 30);
|
||||
|
|
|
@ -259,6 +259,14 @@ trait AvatarsBase
|
|||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/image', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'invalid://appwrite.io/images/apple.png'
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// TODO Add test for non-image file (PDF, WORD)
|
||||
|
||||
return [];
|
||||
|
|
|
@ -25,6 +25,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'execute' => ['role:all'],
|
||||
'name' => 'Test',
|
||||
'runtime' => 'php-8.0',
|
||||
'vars' => [
|
||||
|
@ -58,6 +59,9 @@ class FunctionsCustomServerTest extends Scope
|
|||
'account.create',
|
||||
'account.delete',
|
||||
], $response1['body']['events']);
|
||||
$this->assertEquals([
|
||||
'role:all'
|
||||
], $response1['body']['execute']);
|
||||
$this->assertEquals('0 0 1 1 *', $response1['body']['schedule']);
|
||||
$this->assertEquals(10, $response1['body']['timeout']);
|
||||
|
||||
|
|
|
@ -834,6 +834,17 @@ class ProjectsConsoleClientTest extends Scope
|
|||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/'.$id.'/webhooks', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Webhook Test',
|
||||
'events' => ['account.create', 'account.update.email'],
|
||||
'url' => 'invalid://appwrite.io',
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -979,6 +990,17 @@ class ProjectsConsoleClientTest extends Scope
|
|||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/projects/'.$id.'/webhooks/'.$webhookId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Webhook Test Update',
|
||||
'events' => ['account.delete', 'account.sessions.delete', 'storage.files.create'],
|
||||
'url' => 'invalid://appwrite.io/new',
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
|
|
@ -80,8 +80,8 @@ class RealtimeCustomClientTest extends Scope
|
|||
'collections.1.documents',
|
||||
'collections.2.documents',
|
||||
'documents',
|
||||
'documents.1',
|
||||
'documents.2',
|
||||
'collections.1.documents.1',
|
||||
'collections.2.documents.2',
|
||||
], $headers);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
@ -100,8 +100,8 @@ class RealtimeCustomClientTest extends Scope
|
|||
$this->assertContains('collections.1.documents', $response['data']['channels']);
|
||||
$this->assertContains('collections.2.documents', $response['data']['channels']);
|
||||
$this->assertContains('documents', $response['data']['channels']);
|
||||
$this->assertContains('documents.1', $response['data']['channels']);
|
||||
$this->assertContains('documents.2', $response['data']['channels']);
|
||||
$this->assertContains('collections.1.documents.1', $response['data']['channels']);
|
||||
$this->assertContains('collections.2.documents.2', $response['data']['channels']);
|
||||
$this->assertEquals($userId, $response['data']['user']['$id']);
|
||||
|
||||
$client->close();
|
||||
|
@ -606,7 +606,7 @@ class RealtimeCustomClientTest extends Scope
|
|||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(3, $response['data']['channels']);
|
||||
$this->assertContains('documents', $response['data']['channels']);
|
||||
$this->assertContains('documents.' . $document['body']['$id'], $response['data']['channels']);
|
||||
$this->assertContains('collections.' . $data['actorsId'] . '.documents.' . $document['body']['$id'], $response['data']['channels']);
|
||||
$this->assertContains('collections.' . $actors['body']['$id'] . '.documents', $response['data']['channels']);
|
||||
$this->assertEquals('database.documents.create', $response['data']['event']);
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
|
@ -638,7 +638,7 @@ class RealtimeCustomClientTest extends Scope
|
|||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(3, $response['data']['channels']);
|
||||
$this->assertContains('documents', $response['data']['channels']);
|
||||
$this->assertContains('documents.' . $data['documentId'], $response['data']['channels']);
|
||||
$this->assertContains('collections.' . $data['actorsId'] . '.documents.' . $data['documentId'], $response['data']['channels']);
|
||||
$this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
|
||||
$this->assertEquals('database.documents.update', $response['data']['event']);
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
|
@ -676,7 +676,7 @@ class RealtimeCustomClientTest extends Scope
|
|||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(3, $response['data']['channels']);
|
||||
$this->assertContains('documents', $response['data']['channels']);
|
||||
$this->assertContains('documents.' . $document['body']['$id'], $response['data']['channels']);
|
||||
$this->assertContains('collections.' . $data['actorsId'] . '.documents.' . $document['body']['$id'], $response['data']['channels']);
|
||||
$this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
|
||||
$this->assertEquals('database.documents.delete', $response['data']['event']);
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
|
@ -767,7 +767,7 @@ class RealtimeCustomClientTest extends Scope
|
|||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(3, $response['data']['channels']);
|
||||
$this->assertContains('documents', $response['data']['channels']);
|
||||
$this->assertContains('documents.' . $document['body']['$id'], $response['data']['channels']);
|
||||
$this->assertContains('collections.' . $data['actorsId'] . '.documents.' . $document['body']['$id'], $response['data']['channels']);
|
||||
$this->assertContains('collections.' . $actors['body']['$id'] . '.documents', $response['data']['channels']);
|
||||
$this->assertEquals('database.documents.create', $response['data']['event']);
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
|
@ -798,7 +798,7 @@ class RealtimeCustomClientTest extends Scope
|
|||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(3, $response['data']['channels']);
|
||||
$this->assertContains('documents', $response['data']['channels']);
|
||||
$this->assertContains('documents.' . $data['documentId'], $response['data']['channels']);
|
||||
$this->assertContains('collections.' . $data['actorsId'] . '.documents.' . $data['documentId'], $response['data']['channels']);
|
||||
$this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
|
||||
$this->assertEquals('database.documents.update', $response['data']['event']);
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
|
@ -836,7 +836,7 @@ class RealtimeCustomClientTest extends Scope
|
|||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(3, $response['data']['channels']);
|
||||
$this->assertContains('documents', $response['data']['channels']);
|
||||
$this->assertContains('documents.' . $document['body']['$id'], $response['data']['channels']);
|
||||
$this->assertContains('collections.' . $data['actorsId'] . '.documents.' . $document['body']['$id'], $response['data']['channels']);
|
||||
$this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
|
||||
$this->assertEquals('database.documents.delete', $response['data']['event']);
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
|
|
|
@ -28,6 +28,48 @@ trait TeamsBaseClient
|
|||
$this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['email']);
|
||||
$this->assertEquals('owner', $response['body']['memberships'][0]['roles'][0]);
|
||||
|
||||
$membershipId = $response['body']['memberships'][0]['$id'];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamUid.'/memberships', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => $this->getUser()['$id']
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertIsInt($response['body']['sum']);
|
||||
$this->assertNotEmpty($response['body']['memberships'][0]);
|
||||
$this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['name']);
|
||||
$this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['email']);
|
||||
$this->assertEquals('owner', $response['body']['memberships'][0]['roles'][0]);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamUid.'/memberships', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => $membershipId
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertIsInt($response['body']['sum']);
|
||||
$this->assertNotEmpty($response['body']['memberships'][0]);
|
||||
$this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['name']);
|
||||
$this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['email']);
|
||||
$this->assertEquals('owner', $response['body']['memberships'][0]['roles'][0]);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamUid.'/memberships', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'unknown'
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertIsInt($response['body']['sum']);
|
||||
$this->assertEmpty($response['body']['memberships']);
|
||||
$this->assertEquals(0, $response['body']['sum']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
|
|
@ -17,10 +17,7 @@ use PHPUnit\Framework\TestCase;
|
|||
|
||||
class URLTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var Domain
|
||||
*/
|
||||
protected $url = null;
|
||||
protected ?URL $url;
|
||||
|
||||
public function setUp():void
|
||||
{
|
||||
|
@ -32,9 +29,9 @@ class URLTest extends TestCase
|
|||
$this->url = null;
|
||||
}
|
||||
|
||||
public function testIsValid()
|
||||
public function testIsValid(): void
|
||||
{
|
||||
// Assertions
|
||||
$this->assertEquals('Value must be a valid URL', $this->url->getDescription());
|
||||
$this->assertEquals(true, $this->url->isValid('http://example.com'));
|
||||
$this->assertEquals(true, $this->url->isValid('https://example.com'));
|
||||
$this->assertEquals(true, $this->url->isValid('htts://example.com')); // does not validate protocol
|
||||
|
@ -45,4 +42,13 @@ class URLTest extends TestCase
|
|||
$this->assertEquals(true, $this->url->isValid('http://www.example.com/foo%2\u00c2\u00a9zbar'));
|
||||
$this->assertEquals(true, $this->url->isValid('http://www.example.com/?q=%3Casdf%3E'));
|
||||
}
|
||||
|
||||
public function testIsValidAllowedSchemes(): void
|
||||
{
|
||||
$this->url = new URL(['http', 'https']);
|
||||
$this->assertEquals('Value must be a valid URL with following schemes (http, https)', $this->url->getDescription());
|
||||
$this->assertEquals(true, $this->url->isValid('http://example.com'));
|
||||
$this->assertEquals(true, $this->url->isValid('https://example.com'));
|
||||
$this->assertEquals(false, $this->url->isValid('gopher://www.example.com'));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue