Merge pull request #1290 from lohanidamodar/feat-sb-list

Feature Create, List and Get buckets
This commit is contained in:
Damodar Lohani
2021-06-17 13:12:16 +05:45
committed by GitHub
15 changed files with 416 additions and 103 deletions
-91
View File
@@ -1436,97 +1436,6 @@ $collections = [
],
],
],
Database::SYSTEM_COLLECTION_BUCKETS => [
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
'$id' => Database::SYSTEM_COLLECTION_BUCKETS,
'$permissions' => ['read' => ['*']],
'name' => 'File',
'structure' => true,
'rules' => [
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Date Created',
'key' => 'dateCreated',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => 0,
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Enabled',
'key' => 'enabled',
'type' => Database::SYSTEM_VAR_TYPE_BOOLEAN,
'default' => true,
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Name',
'key' => 'name',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => true,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Adapter',
'key' => 'adapter',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => true,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Adapter Crednetials',
'key' => 'adapterCredentials',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
'filter' => ['json']
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Maximum File Size',
'key' => 'maximumFileSize',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => 0,
'required' => true,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Allowed File Extensions',
'key' => 'allowedFileExtensions',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => true,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Encryption',
'key' => 'encryption',
'type' => Database::SYSTEM_VAR_TYPE_BOOLEAN,
'default' => true,
'required' => true,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Virus Scan',
'key' => 'antiVirus',
'type' => Database::SYSTEM_VAR_TYPE_BOOLEAN,
'default' => true,
'required' => true,
'array' => false,
],
],
],
Database::SYSTEM_COLLECTION_FUNCTIONS => [
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
'$id' => Database::SYSTEM_COLLECTION_FUNCTIONS,
+119
View File
@@ -967,6 +967,125 @@ $collections = [
]
],
],
'buckets' => [
'$collection' => Database::COLLECTIONS,
'$id' => 'buckets',
'$permissions' => ['read' => ['*']],
'name' => 'Buckets',
'structure' => true,
'attributes' => [
[
'$id' => 'dateCreated',
'type' => Database::VAR_INTEGER,
'format' => '',
'signed' => false,
'size' => 0,
'required' => false,
'array' => false,
'filters' => [],
],
[
'$id' => 'dateUpdated',
'type' => Database::VAR_INTEGER,
'size' => 0,
'format' => '',
'signed' => true,
'required' => false,
'array' => false,
'filters' => [],
],
[
'$id' => 'enabled',
'type' => Database::VAR_BOOLEAN,
'signed' => true,
'size' => 0,
'format' => '',
'filters' => [],
'required' => true,
'array' => false,
],
[
'$id' => 'name',
'type' => Database::VAR_STRING,
'signed' => true,
'size' => 128,
'format' => '',
'filters' => [],
'required' => true,
'array' => false,
],
[
'$id' => 'adapter',
'type' => Database::VAR_STRING,
'signed' => true,
'size' => 64,
'format' => '',
'filters' => [],
'required' => true,
'array' => false,
],
[
'$id' => 'adapterCredentials',
'type' => Database::VAR_STRING,
'signed' => true,
'size' => 16384,
'format' => '',
'required' => false,
'array' => false,
'filters' => ['json']
],
[
'$id' => 'maximumFileSize',
'type' => Database::VAR_INTEGER,
'signed' => true,
'size' => 0,
'format' => '',
'filters' => [],
'required' => true,
'array' => false,
],
[
'$id' => 'allowedFileExtensions',
'type' => Database::VAR_STRING,
'signed' => true,
'size' => 64,
'format' => '',
'filters' => [],
'required' => true,
'array' => true,
],
[
'$id' => 'encryption',
'type' => Database::VAR_BOOLEAN,
'signed' => true,
'size' => 0,
'format' => '',
'filters' => [],
'required' => true,
'array' => false,
],
[
'$id' => 'antiVirus',
'type' => Database::VAR_BOOLEAN,
'signed' => true,
'size' => 0,
'format' => '',
'filters' => [],
'required' => true,
'array' => false,
],
],
'indexes' => [
[
'$id' => '_fulltext_name',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['name'],
'lengths' => [1024],
'orders' => [Database::ORDER_ASC],
]
]
]
];
/*
+5
View File
@@ -152,6 +152,11 @@ return [
'model' => Response::MODEL_FILE,
'note' => '',
],
'storage.buckets.create' => [
'description' => 'This event triggers when a storage bucket is created.',
'model' => Response::MODEL_BUCKET,
'note' => '',
],
'users.create' => [
'description' => 'This event triggers when a user is created from the users API.',
'model' => Response::MODEL_USER,
+4
View File
@@ -13,6 +13,8 @@ $member = [
'documents.write',
'files.read',
'files.write',
'buckets.read',
'buckets.write',
'projects.read',
'projects.write',
'locale.read',
@@ -28,6 +30,8 @@ $admins = [
'documents.write',
'files.read',
'files.write',
'buckets.read',
'buckets.write',
'users.read',
'users.write',
'collections.read',
+6
View File
@@ -31,6 +31,12 @@ return [ // List of publicly visible scopes
'files.write' => [
'description' => 'Access to create, update, and delete your project\'s storage files',
],
'buckets.read' => [
'description' => 'Access to read your project\'s storage buckets',
],
'buckets.write' => [
'description' => 'Access to create, update, and delete your project\'s storage buckets',
],
'functions.read' => [
'description' => 'Access to read your project\'s functions and code tags',
],
+116 -2
View File
@@ -6,6 +6,7 @@ use Utopia\Validator\ArrayList;
use Utopia\Validator\WhiteList;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\Boolean;
use Utopia\Validator\HexColor;
use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Filesystem;
@@ -21,8 +22,121 @@ use Utopia\Image\Image;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Utopia\Response;
use Utopia\Config\Config;
use Utopia\Validator\Integer;
use Utopia\Database\Query;
use Utopia\Validator\Numeric;
App::post('/v1/storage/buckets')
->desc('Create storage bucket')
->groups(['api', 'storage'])
->label('scope', 'buckets.write')
->label('event', 'storage.buckets.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'createBucket')
->label('sdk.description', '/docs/references/storage/create-bucket.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_BUCKET)
->param('name', '', new Text(128), 'Bucket name', false)
->param('read', [], new ArrayList(new Text(64)), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->param('write', [], new ArrayList(new Text(64)), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->param('maximumFileSize', 0, new Integer(), 'Maximum file size allowed.', true)
->param('allowedFileExtensions', ['*'], new ArrayList(new Text(64)), 'Allowed file extensions', true)
->param('enabled', true, new Boolean(), 'Is bucket enabled?', true)
->param('adapter', 'local', new WhiteList(['local']), 'Storage adapter.', true)
->param('encryption', true, new Boolean(), 'Is encryption enabled?', true)
->param('antiVirus', true, new Boolean(), 'Is virus scanning enabled?', true)
->inject('response')
->inject('dbForInternal')
->inject('user')
->inject('audits')
->action(function ($name, $read, $write, $maximumFileSize, $allowedFileExtensions, $enabled, $adapter, $encryption, $antiVirus, $response, $dbForInternal, $user, $audits) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Event\Event $audits */
$data = $dbForInternal->createDocument('buckets', new Document([
'$collection' => 'buckets',
'dateCreated' => \time(),
'dateUpdated' => \time(),
'name' => $name,
'maximumFileSize' => $maximumFileSize,
'allowedFileExtensions' => $allowedFileExtensions,
'enabled' => $enabled,
'adapter' => $adapter,
'encryption' => $encryption,
'antiVirus' => $antiVirus,
'$read' => $read,
'$write' => $write,
]));
$audits
->setParam('event', 'storage.buckets.create')
->setParam('resource', 'storage/buckets/' . $data->getId())
->setParam('data', $data->getArrayCopy())
;
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic2($data, Response::MODEL_BUCKET);
});
App::get('/v1/storage/buckets')
->desc('List buckets')
->groups(['api', 'storage'])
->label('scope', 'buckets.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'listBuckets')
->label('sdk.description', '/docs/references/storage/list-buckets.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_BUCKET_LIST)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->action(function ($search, $limit, $offset, $orderType, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, $search)] : [];
$response->dynamic2(new Document([
'buckets' => $dbForInternal->find('buckets', $queries, $limit, $offset, ['_id'], [$orderType]),
'sum' => $dbForInternal->count('buckets', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_BUCKET_LIST);
});
App::get('/v1/storage/buckets/:bucketId')
->desc('Get Bucket')
->groups(['api', 'storage'])
->label('scope', 'buckets.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getBucket')
->label('sdk.description', '/docs/references/storage/get-bucket.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_BUCKET)
->param('bucketId', '', new UID(), 'Bucket unique ID.')
->inject('response')
->inject('dbForInternal')
->action(function ($bucketId, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
$bucket = $dbForInternal->getDocument('buckets', $bucketId);
if ($bucket->isEmpty()) {
throw new Exception('Bucket not found', 404);
}
$response->dynamic2($bucket, Response::MODEL_BUCKET);
});
App::post('/v1/storage/files')
->desc('Create File')
@@ -586,4 +700,4 @@ App::delete('/v1/storage/files/:fileId')
;
$response->noContent();
});
});
Generated
+3 -3
View File
@@ -1704,7 +1704,7 @@
"source": {
"type": "git",
"url": "https://github.com/lohanidamodar/audit",
"reference": "b3ca9fa928fdec8c966596cbd85ee1141c79b6eb"
"reference": "d5591321161b81043c45be3c53ce9dcfa2d81d72"
},
"require": {
"ext-pdo": "*",
@@ -1738,7 +1738,7 @@
"upf",
"utopia"
],
"time": "2021-06-13T07:41:15+00:00"
"time": "2021-06-14T07:38:18+00:00"
},
{
"name": "utopia-php/cache",
@@ -6195,5 +6195,5 @@
"platform-overrides": {
"php": "8.0"
},
"plugin-api-version": "2.0.0"
"plugin-api-version": "2.1.0"
}
+1
View File
@@ -0,0 +1 @@
Create a new storage bucket.
+1
View File
@@ -0,0 +1 @@
Get a storage bucket by its unique ID. This endpoint response returns a JSON object with the storage bucket metadata.
+1
View File
@@ -0,0 +1 @@
Get a list of all the storage buckets. You can use the query params to filter your results.
+2
View File
@@ -21,6 +21,7 @@ use Appwrite\Utopia\Response\Model\Error;
use Appwrite\Utopia\Response\Model\ErrorDev;
use Appwrite\Utopia\Response\Model\Execution;
use Appwrite\Utopia\Response\Model\File;
use Appwrite\Utopia\Response\Model\Bucket;
use Appwrite\Utopia\Response\Model\Func;
use Appwrite\Utopia\Response\Model\JWT;
use Appwrite\Utopia\Response\Model\Key;
@@ -186,6 +187,7 @@ class Response extends SwooleResponse
->setModel(new JWT())
->setModel(new Locale())
->setModel(new File())
->setModel(new Bucket())
->setModel(new Team())
->setModel(new Membership())
->setModel(new Func())
+20 -7
View File
@@ -17,16 +17,29 @@ class Bucket extends Model
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('$permissions', [
'type' => Response::MODEL_PERMISSIONS,
'description' => 'File permissions.',
'default' => new \stdClass,
'example' => new \stdClass,
'array' => false,
->addRule('$read', [
'type' => self::TYPE_STRING,
'description' => 'File read permissions.',
'default' => [],
'example' => ['role:all'],
'array' => true,
])
->addRule('$write', [
'type' => self::TYPE_STRING,
'description' => 'File write permissions.',
'default' => [],
'example' => ['user:608f9da25e7e1'],
'array' => true,
])
->addRule('dateCreated', [
'type' => self::TYPE_INTEGER,
'description' => 'File creation date in Unix timestamp.',
'description' => 'Bucket creation date in Unix timestamp.',
'default' => 0,
'example' => 1592981250,
])
->addRule('dateUpdated', [
'type' => self::TYPE_INTEGER,
'description' => 'Bucket update date in Unix timestamp.',
'default' => 0,
'example' => 1592981250,
])
+3
View File
@@ -73,6 +73,8 @@ trait ProjectCustom
'documents.write',
'files.read',
'files.write',
'buckets.read',
'buckets.write',
'functions.read',
'functions.write',
'execution.read',
@@ -124,6 +126,7 @@ trait ProjectCustom
'storage.files.create',
'storage.files.update',
'storage.files.delete',
'storage.buckets.create',
'users.create',
'users.update.prefs',
'users.update.status',
@@ -5,10 +5,110 @@ namespace Tests\E2E\Services\Storage;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
use Tests\E2E\Client;
class StorageCustomServerTest extends Scope
{
use StorageBase;
use ProjectCustom;
use SideServer;
public function testCreateBucket():array
{
/**
* Test for SUCCESS
*/
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Test Bucket',
]);
$this->assertEquals(201, $bucket['headers']['status-code']);
$this->assertNotEmpty($bucket['body']['$id']);
$this->assertIsInt($bucket['body']['dateCreated']);
$this->assertIsArray($bucket['body']['$read']);
$this->assertIsArray($bucket['body']['$write']);
$this->assertIsArray($bucket['body']['allowedFileExtensions']);
$this->assertEquals('Test Bucket', $bucket['body']['name']);
$this->assertEquals(true, $bucket['body']['enabled']);
$this->assertEquals(true, $bucket['body']['encryption']);
$this->assertEquals(true, $bucket['body']['antiVirus']);
$this->assertEquals('local', $bucket['body']['adapter']);
$bucketId = $bucket['body']['$id'];
/**
* Test for FAILURE
*/
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => '',
]);
$this->assertEquals(400, $bucket['headers']['status-code']);
return ['bucketId' => $bucketId];
}
/**
* @depends testCreateBucket
*/
public function testListBucket($data): array
{
$id = $data['bucketId'] ?? '';
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_GET, '/storage/buckets',
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']);
$this->assertEquals($id, $response['body']['buckets'][0]['$id']);
$this->assertEquals('Test Bucket', $response['body']['buckets'][0]['name']);
return $data;
}
/**
* @depends testCreateBucket
*/
public function testGetBucket($data): array
{
$id = $data['bucketId'] ?? '';
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $id,
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']);
$this->assertEquals($id, $response['body']['$id']);
$this->assertEquals('Test Bucket', $response['body']['name']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/storage/buckets/empty',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(404, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_GET, '/storage/buckets/id-is-really-long-id-is-really-long-id-is-really-long-id-is-really-long',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(400, $response['headers']['status-code']);
return $data;
}
}
@@ -552,4 +552,39 @@ trait WebhooksBase
*/
return [];
}
public function testCreateStorageBucket(): array
{
/**
* Test for SUCCESS
*/
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'name' => 'Test Bucket',
]);
$this->assertEquals($bucket['headers']['status-code'], 201);
$this->assertNotEmpty($bucket['body']['$id']);
$webhook = $this->getLastRequest();
$this->assertEquals($webhook['method'], 'POST');
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Event'], 'storage.buckets.create');
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], 'not-yet-implemented');
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true);
$this->assertNotEmpty($webhook['data']['$id']);
$this->assertEquals('Test Bucket', $webhook['data']['name']);
$this->assertEquals(true, $webhook['data']['enabled']);
$this->assertIsArray($webhook['data']['$read']);
$this->assertIsArray($webhook['data']['$write']);
return array_merge(['bucketId' => $bucket['body']['$id']]);
}
}