Introduce code analysis
This commit is contained in:
parent
8a8638a817
commit
5a7c43ab32
16
.github/workflows/codeql-phpstan.yml
vendored
Normal file
16
.github/workflows/codeql-phpstan.yml
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on: [pull_request]
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: CodeQL
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Run CodeQL
|
||||||
|
run: |
|
||||||
|
docker run --rm -v $PWD:/app composer sh -c \
|
||||||
|
"composer install --profile --ignore-platform-reqs && composer check"
|
|
@ -23,6 +23,8 @@ use Utopia\Pools\Group;
|
||||||
use Utopia\Queue\Connection;
|
use Utopia\Queue\Connection;
|
||||||
use Utopia\Registry\Registry;
|
use Utopia\Registry\Registry;
|
||||||
|
|
||||||
|
global $register;
|
||||||
|
|
||||||
Authorization::disable();
|
Authorization::disable();
|
||||||
|
|
||||||
CLI::setResource('register', fn () => $register);
|
CLI::setResource('register', fn () => $register);
|
||||||
|
|
|
@ -48,7 +48,6 @@ use Utopia\Cache\Adapter\Redis as RedisCache;
|
||||||
use Utopia\Cache\Adapter\Sharding;
|
use Utopia\Cache\Adapter\Sharding;
|
||||||
use Utopia\Cache\Cache;
|
use Utopia\Cache\Cache;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\CLI\Console;
|
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Adapter\MariaDB;
|
use Utopia\Database\Adapter\MariaDB;
|
||||||
use Utopia\Database\Adapter\MariaDBProxy;
|
use Utopia\Database\Adapter\MariaDBProxy;
|
||||||
|
@ -62,7 +61,6 @@ use Utopia\Database\Validator\Authorization;
|
||||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||||
use Utopia\Database\Validator\Structure;
|
use Utopia\Database\Validator\Structure;
|
||||||
use Utopia\Domains\Validator\PublicDomain;
|
use Utopia\Domains\Validator\PublicDomain;
|
||||||
use Utopia\Domains\Validator\PublicDomain;
|
|
||||||
use Utopia\DSN\DSN;
|
use Utopia\DSN\DSN;
|
||||||
use Utopia\Locale\Locale;
|
use Utopia\Locale\Locale;
|
||||||
use Utopia\Logger\Log;
|
use Utopia\Logger\Log;
|
||||||
|
|
|
@ -36,6 +36,8 @@ use Utopia\Queue\Server;
|
||||||
use Utopia\Registry\Registry;
|
use Utopia\Registry\Registry;
|
||||||
use Utopia\Storage\Device\Local;
|
use Utopia\Storage\Device\Local;
|
||||||
|
|
||||||
|
global $register;
|
||||||
|
|
||||||
Authorization::disable();
|
Authorization::disable();
|
||||||
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,8 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vendor/bin/phpunit",
|
"test": "vendor/bin/phpunit",
|
||||||
"lint": "vendor/bin/pint --test",
|
"lint": "vendor/bin/pint --test",
|
||||||
"format": "vendor/bin/pint"
|
"format": "vendor/bin/pint",
|
||||||
|
"check": "./vendor/bin/phpstan analyse -c phpstan.neon --memory-limit 1G app src tests"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@ -84,7 +85,8 @@
|
||||||
"swoole/ide-helper": "5.0.2",
|
"swoole/ide-helper": "5.0.2",
|
||||||
"textalk/websocket": "1.5.7",
|
"textalk/websocket": "1.5.7",
|
||||||
"utopia-php/fetch": "0.1.*",
|
"utopia-php/fetch": "0.1.*",
|
||||||
"laravel/pint": "^1.14"
|
"laravel/pint": "^1.14",
|
||||||
|
"phpstan/phpstan": "1.8.*"
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
"ext-phpiredis": "*"
|
"ext-phpiredis": "*"
|
||||||
|
|
61
composer.lock
generated
61
composer.lock
generated
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "03de7dbb57fcecac386e5b710ec5fe49",
|
"content-hash": "1c3c0b518e1486c5770b57519da2a797",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "adhocore/jwt",
|
"name": "adhocore/jwt",
|
||||||
|
@ -3642,6 +3642,65 @@
|
||||||
},
|
},
|
||||||
"time": "2024-02-23T16:05:55+00:00"
|
"time": "2024-02-23T16:05:55+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "phpstan/phpstan",
|
||||||
|
"version": "1.8.11",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/phpstan/phpstan.git",
|
||||||
|
"reference": "46e223dd68a620da18855c23046ddb00940b4014"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e223dd68a620da18855c23046ddb00940b4014",
|
||||||
|
"reference": "46e223dd68a620da18855c23046ddb00940b4014",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2|^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"phpstan/phpstan-shim": "*"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"phpstan",
|
||||||
|
"phpstan.phar"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "PHPStan - PHP Static Analysis Tool",
|
||||||
|
"keywords": [
|
||||||
|
"dev",
|
||||||
|
"static analysis"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||||
|
"source": "https://github.com/phpstan/phpstan/tree/1.8.11"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/ondrejmirtes",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/phpstan",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2022-10-24T15:45:13+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
"version": "9.2.31",
|
"version": "9.2.31",
|
||||||
|
|
11
phpstan.neon
Normal file
11
phpstan.neon
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
parameters:
|
||||||
|
level: 0
|
||||||
|
scanDirectories:
|
||||||
|
- vendor/swoole/ide-helper
|
||||||
|
excludePaths:
|
||||||
|
- tests/resources
|
||||||
|
ignoreErrors:
|
||||||
|
- '#Parameter \$geodb of anonymous function has invalid type MaxMind\\Db\\Reader\.#'
|
||||||
|
- '#Parameter \$geodb of function router\(\) has invalid type MaxMind\\Db\\Reader\.#'
|
||||||
|
- '#Instantiated class MaxMind\\Db\\Reader not found.#'
|
||||||
|
- '#Function scrypt not found\.#'
|
|
@ -88,7 +88,7 @@ class Autodesk extends OAuth2
|
||||||
'client_id' => $this->appID,
|
'client_id' => $this->appID,
|
||||||
'client_secret' => $this->appSecret,
|
'client_secret' => $this->appSecret,
|
||||||
'grant_type' => 'refresh_token',
|
'grant_type' => 'refresh_token',
|
||||||
'code' => $code,
|
'code' => $refreshToken,
|
||||||
'redirect_uri' => $this->callback,
|
'redirect_uri' => $this->callback,
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
|
@ -152,9 +152,9 @@ class Func extends Event
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getData(): string
|
public function getBody(): string
|
||||||
{
|
{
|
||||||
return $this->data;
|
return $this->body;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -50,7 +50,7 @@ class SDKs extends Action
|
||||||
$message = ($git) ? Console::confirm('Please enter your commit message:') : '';
|
$message = ($git) ? Console::confirm('Please enter your commit message:') : '';
|
||||||
|
|
||||||
if (!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', '0.13.x', '0.14.x', '0.15.x', '1.0.x', '1.1.x', '1.2.x', '1.3.x', '1.4.x', '1.5.x', 'latest'])) {
|
if (!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', '0.13.x', '0.14.x', '0.15.x', '1.0.x', '1.1.x', '1.2.x', '1.3.x', '1.4.x', '1.5.x', 'latest'])) {
|
||||||
throw new Exception('Unknown version given');
|
throw new \Exception('Unknown version given');
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($platforms as $key => $platform) {
|
foreach ($platforms as $key => $platform) {
|
||||||
|
@ -196,7 +196,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
$config = new REST();
|
$config = new REST();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception('Language "' . $language['key'] . '" not supported');
|
throw new \Exception('Language "' . $language['key'] . '" not supported');
|
||||||
}
|
}
|
||||||
|
|
||||||
Console::info("Generating {$language['name']} SDK...");
|
Console::info("Generating {$language['name']} SDK...");
|
||||||
|
|
|
@ -73,7 +73,7 @@ class Builds extends Action
|
||||||
$payload = $message->getPayload() ?? [];
|
$payload = $message->getPayload() ?? [];
|
||||||
|
|
||||||
if (empty($payload)) {
|
if (empty($payload)) {
|
||||||
throw new Exception('Missing payload');
|
throw new \Exception('Missing payload');
|
||||||
}
|
}
|
||||||
|
|
||||||
$type = $payload['type'] ?? '';
|
$type = $payload['type'] ?? '';
|
||||||
|
@ -124,7 +124,7 @@ class Builds extends Action
|
||||||
|
|
||||||
$function = $dbForProject->getDocument('functions', $functionId);
|
$function = $dbForProject->getDocument('functions', $functionId);
|
||||||
if ($function->isEmpty()) {
|
if ($function->isEmpty()) {
|
||||||
throw new Exception('Function not found', 404);
|
throw new \Exception('Function not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$deploymentId = $deployment->getId();
|
$deploymentId = $deployment->getId();
|
||||||
|
@ -132,11 +132,11 @@ class Builds extends Action
|
||||||
|
|
||||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||||
if ($deployment->isEmpty()) {
|
if ($deployment->isEmpty()) {
|
||||||
throw new Exception('Deployment not found', 404);
|
throw new \Exception('Deployment not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($deployment->getAttribute('entrypoint', ''))) {
|
if (empty($deployment->getAttribute('entrypoint', ''))) {
|
||||||
throw new Exception('Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".', 500);
|
throw new \Exception('Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".', 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
$version = $function->getAttribute('version', 'v2');
|
$version = $function->getAttribute('version', 'v2');
|
||||||
|
@ -144,7 +144,7 @@ class Builds extends Action
|
||||||
$key = $function->getAttribute('runtime');
|
$key = $function->getAttribute('runtime');
|
||||||
$runtime = $runtimes[$key] ?? null;
|
$runtime = $runtimes[$key] ?? null;
|
||||||
if (\is_null($runtime)) {
|
if (\is_null($runtime)) {
|
||||||
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported');
|
throw new \Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Realtime preparation
|
// Realtime preparation
|
||||||
|
@ -306,7 +306,7 @@ class Builds extends Action
|
||||||
$directorySize = $localDevice->getDirectorySize($tmpDirectory);
|
$directorySize = $localDevice->getDirectorySize($tmpDirectory);
|
||||||
$functionsSizeLimit = (int) App::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000');
|
$functionsSizeLimit = (int) App::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000');
|
||||||
if ($directorySize > $functionsSizeLimit) {
|
if ($directorySize > $functionsSizeLimit) {
|
||||||
throw new Exception('Repository directory size should be less than ' . number_format($functionsSizeLimit / 1048576, 2) . ' MBs.');
|
throw new \Exception('Repository directory size should be less than ' . number_format($functionsSizeLimit / 1048576, 2) . ' MBs.');
|
||||||
}
|
}
|
||||||
|
|
||||||
Console::execute('tar --exclude code.tar.gz -czf ' . $tmpPathFile . ' -C /tmp/builds/' . \escapeshellcmd($buildId) . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr);
|
Console::execute('tar --exclude code.tar.gz -czf ' . $tmpPathFile . ' -C /tmp/builds/' . \escapeshellcmd($buildId) . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr);
|
||||||
|
@ -431,7 +431,7 @@ class Builds extends Action
|
||||||
$build = $dbForProject->getDocument('builds', $build->getId());
|
$build = $dbForProject->getDocument('builds', $build->getId());
|
||||||
|
|
||||||
if ($build->isEmpty()) {
|
if ($build->isEmpty()) {
|
||||||
throw new Exception('Build not found', 404);
|
throw new \Exception('Build not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$build = $build->setAttribute('logs', $build->getAttribute('logs', '') . $logs);
|
$build = $build->setAttribute('logs', $build->getAttribute('logs', '') . $logs);
|
||||||
|
|
|
@ -25,7 +25,7 @@ use Utopia\Messaging\Adapter\SMS as SMSAdapter;
|
||||||
use Utopia\Messaging\Adapter\SMS\Mock;
|
use Utopia\Messaging\Adapter\SMS\Mock;
|
||||||
use Utopia\Messaging\Adapter\SMS\Msg91;
|
use Utopia\Messaging\Adapter\SMS\Msg91;
|
||||||
use Utopia\Messaging\Adapter\SMS\Telesign;
|
use Utopia\Messaging\Adapter\SMS\Telesign;
|
||||||
use Utopia\Messaging\Adapter\SMS\Textmagic;
|
use Utopia\Messaging\Adapter\SMS\TextMagic;
|
||||||
use Utopia\Messaging\Adapter\SMS\Twilio;
|
use Utopia\Messaging\Adapter\SMS\Twilio;
|
||||||
use Utopia\Messaging\Adapter\SMS\Vonage;
|
use Utopia\Messaging\Adapter\SMS\Vonage;
|
||||||
use Utopia\Messaging\Messages\Email;
|
use Utopia\Messaging\Messages\Email;
|
||||||
|
@ -456,7 +456,7 @@ class Messaging extends Action
|
||||||
return match ($provider->getAttribute('provider')) {
|
return match ($provider->getAttribute('provider')) {
|
||||||
'mock' => new Mock('username', 'password'),
|
'mock' => new Mock('username', 'password'),
|
||||||
'twilio' => new Twilio($credentials['accountSid'], $credentials['authToken']),
|
'twilio' => new Twilio($credentials['accountSid'], $credentials['authToken']),
|
||||||
'textmagic' => new Textmagic($credentials['username'], $credentials['apiKey']),
|
'textmagic' => new TextMagic($credentials['username'], $credentials['apiKey']),
|
||||||
'telesign' => new Telesign($credentials['customerId'], $credentials['apiKey']),
|
'telesign' => new Telesign($credentials['customerId'], $credentials['apiKey']),
|
||||||
'msg91' => new Msg91($credentials['senderId'], $credentials['authKey'], $credentials['templateId']),
|
'msg91' => new Msg91($credentials['senderId'], $credentials['authKey'], $credentials['templateId']),
|
||||||
'vonage' => new Vonage($credentials['apiKey'], $credentials['apiSecret']),
|
'vonage' => new Vonage($credentials['apiKey'], $credentials['apiSecret']),
|
||||||
|
|
|
@ -12,7 +12,7 @@ abstract class Promise
|
||||||
|
|
||||||
private mixed $result;
|
private mixed $result;
|
||||||
|
|
||||||
public function __construct(?callable $executor = null)
|
final public function __construct(?callable $executor = null)
|
||||||
{
|
{
|
||||||
if (\is_null($executor)) {
|
if (\is_null($executor)) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -6,11 +6,6 @@ use Swoole\Coroutine\Channel;
|
||||||
|
|
||||||
class Swoole extends Promise
|
class Swoole extends Promise
|
||||||
{
|
{
|
||||||
public function __construct(?callable $executor = null)
|
|
||||||
{
|
|
||||||
parent::__construct($executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(
|
protected function execute(
|
||||||
callable $executor,
|
callable $executor,
|
||||||
callable $resolve,
|
callable $resolve,
|
||||||
|
|
|
@ -238,8 +238,11 @@ class OpenAPI3 extends Format
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($route->getLabel('sdk.response.code', 500) === 204) {
|
if ($route->getLabel('sdk.response.code', 500) === 204) {
|
||||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['description'] = 'No content';
|
$labelCode = (string)$route->getLabel('sdk.response.code', '500');
|
||||||
unset($temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['schema']);
|
$temp['responses'][$labelCode]['description'] = 'No content';
|
||||||
|
if(isset($temp['responses'][$labelCode]['schema'])) {
|
||||||
|
unset($temp['responses'][$labelCode]['schema']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!empty($scope))) { // && 'public' != $scope
|
if ((!empty($scope))) { // && 'public' != $scope
|
||||||
|
|
|
@ -24,11 +24,14 @@ class V14 extends Filter
|
||||||
|
|
||||||
private function convertEvents($content)
|
private function convertEvents($content)
|
||||||
{
|
{
|
||||||
|
// TODO: If nessessary, implement V13 and use following code:
|
||||||
|
/*
|
||||||
$migration = new MigrationV13();
|
$migration = new MigrationV13();
|
||||||
|
|
||||||
$events = $content['events'] ?? [];
|
$events = $content['events'] ?? [];
|
||||||
$content['events'] = $migration->migrateEvents($events);
|
$content['events'] = $migration->migrateEvents($events);
|
||||||
|
|
||||||
return $content;
|
return $content;
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Appwrite\Utopia\Response\Filters;
|
namespace Appwrite\Utopia\Response\Filters;
|
||||||
|
|
||||||
|
use Appwrite\ID;
|
||||||
use Appwrite\Utopia\Response;
|
use Appwrite\Utopia\Response;
|
||||||
use Appwrite\Utopia\Response\Filter;
|
use Appwrite\Utopia\Response\Filter;
|
||||||
|
|
||||||
|
|
|
@ -209,8 +209,6 @@ class V12 extends Filter
|
||||||
unset($content['bucketsRead']);
|
unset($content['bucketsRead']);
|
||||||
unset($content['bucketsUpdate']);
|
unset($content['bucketsUpdate']);
|
||||||
unset($content['bucketsDelete']);
|
unset($content['bucketsDelete']);
|
||||||
unset($content['filesCount']);
|
|
||||||
unset($content['bucketsDelete']);
|
|
||||||
unset($content['filesCreate']);
|
unset($content['filesCreate']);
|
||||||
unset($content['filesRead']);
|
unset($content['filesRead']);
|
||||||
unset($content['filesUpdate']);
|
unset($content['filesUpdate']);
|
||||||
|
|
|
@ -183,7 +183,6 @@ class StorageClientTest extends Scope
|
||||||
/**
|
/**
|
||||||
* @depends testCreateFile
|
* @depends testCreateFile
|
||||||
* @param $file
|
* @param $file
|
||||||
* @return array
|
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function testGetFileDownload($file)
|
public function testGetFileDownload($file)
|
||||||
|
|
|
@ -232,7 +232,6 @@ class StorageServerTest extends Scope
|
||||||
/**
|
/**
|
||||||
* @depends testCreateFile
|
* @depends testCreateFile
|
||||||
* @param $file
|
* @param $file
|
||||||
* @return array
|
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function testGetFileDownload($file)
|
public function testGetFileDownload($file)
|
||||||
|
|
Loading…
Reference in a new issue