Merge remote-tracking branch 'origin/1.6.x' into feat-development-keys

This commit is contained in:
Damodar Lohani
2024-07-29 09:32:58 +00:00
38 changed files with 666 additions and 117 deletions
+1
View File
@@ -9,6 +9,7 @@
!/.idea/php.xml
.DS_Store
.php_cs.cache
.phpactor.json
debug/
app/sdks
dev/yasd_init.php
-4
View File
@@ -1,4 +0,0 @@
[submodule "app/console"]
path = app/console
url = https://github.com/appwrite/console
branch = 1.6.x
-18
View File
@@ -12,23 +12,6 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \
--no-plugins --no-scripts --prefer-dist \
`if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi`
FROM --platform=$BUILDPLATFORM node:20.11.0-alpine3.19 as node
COPY app/console /usr/local/src/console
WORKDIR /usr/local/src/console
ARG VITE_GA_PROJECT
ARG VITE_CONSOLE_MODE
ARG VITE_APPWRITE_GROWTH_ENDPOINT=https://growth.appwrite.io/v1
ENV VITE_GA_PROJECT=$VITE_GA_PROJECT
ENV VITE_CONSOLE_MODE=$VITE_CONSOLE_MODE
ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT
RUN npm ci
RUN npm run build
FROM appwrite/base:0.9.1 as final
LABEL maintainer="team@appwrite.io"
@@ -48,7 +31,6 @@ RUN \
WORKDIR /usr/src/code
COPY --from=composer /usr/local/src/vendor /usr/src/code/vendor
COPY --from=node /usr/local/src/console/build /usr/src/code/console
# Add Source Code
COPY ./app /usr/src/code/app
+35 -2
View File
@@ -3848,6 +3848,39 @@ $projectCollections = array_merge([
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('scheduledAt'),
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('scheduleInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('scheduleId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
@@ -4751,8 +4784,8 @@ $consoleCollections = array_merge([
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'required' => false,
'default' => [],
'array' => true,
'filters' => [],
],
+1 -1
View File
@@ -221,7 +221,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '6.0.0-rc.3',
'version' => '6.0.0-rc.4',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
Submodule app/console deleted from f978cecfaf
+11 -4
View File
@@ -1744,9 +1744,8 @@ App::post('/v1/functions/:functionId/executions')
->setContext('function', $function);
if ($async) {
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
if(is_null($scheduledAt)) {
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
$queueForFunctions
->setType('http')
->setExecution($execution)
@@ -1770,7 +1769,7 @@ App::post('/v1/functions/:functionId/executions')
'jwt' => $jwt,
];
$dbForConsole->createDocument('schedules', new Document([
$schedule = $dbForConsole->createDocument('schedules', new Document([
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => ScheduleExecutions::getSupportedResource(),
'resourceId' => $execution->getId(),
@@ -1781,6 +1780,13 @@ App::post('/v1/functions/:functionId/executions')
'data' => $data,
'active' => true,
]));
$execution = $execution
->setAttribute('scheduleId', $schedule->getId())
->setAttribute('scheduleInternalId', $schedule->getInternalId())
->setAttribute('scheduledAt', $scheduledAt);
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
}
return $response
@@ -1825,7 +1831,8 @@ App::post('/v1/functions/:functionId/executions')
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
'APPWRITE_VERSION' => APP_VERSION_STABLE
'APPWRITE_VERSION' => APP_VERSION_STABLE,
'APPWRITE_REGION' => $project->getAttribute('region'),
]);
/** Execute function */
+2 -2
View File
@@ -63,7 +63,7 @@ App::post('/v1/storage/buckets')
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true)
->param('maximumFileSize', fn (array $plan) => empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024, fn (array $plan) => new Range(1, empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true, ['plan'])
->param('maximumFileSize', fn (array $plan) => empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1000 * 1000, fn (array $plan) => new Range(1, empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1000 * 1000), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true, ['plan'])
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
->param('compression', Compression::NONE, new WhiteList([Compression::NONE, Compression::GZIP, Compression::ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . Compression::NONE . ', [' . Compression::GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . Compression::ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
@@ -240,7 +240,7 @@ App::put('/v1/storage/buckets/:bucketId')
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true)
->param('maximumFileSize', fn (array $plan) => empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024, fn (array $plan) => new Range(1, empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true, ['plan'])
->param('maximumFileSize', fn (array $plan) => empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1000 * 1000, fn (array $plan) => new Range(1, empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1000 * 1000), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true, ['plan'])
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
->param('compression', Compression::NONE, new WhiteList([Compression::NONE, Compression::GZIP, Compression::ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . Compression::NONE . ', [' . Compression::GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . Compression::ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
+2 -6
View File
@@ -2109,7 +2109,7 @@ App::post('/v1/users/:userId/jwts')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_JWT)
->param('userId', '', new UID(), 'User ID.')
->param('sessionId', 'recent', new UID(), 'Session ID. Use the string \'recent\' to use the most recent session. Defaults to the most recent session.', true)
->param('sessionId', '', new UID(), 'Session ID. Use the string \'recent\' to use the most recent session. Defaults to the most recent session.', true)
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
->inject('response')
->inject('dbForProject')
@@ -2137,17 +2137,13 @@ App::post('/v1/users/:userId/jwts')
}
}
if ($session->isEmpty()) {
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
}
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $duration, 0);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic(new Document(['jwt' => $jwt->encode([
'userId' => $user->getId(),
'sessionId' => $session->getId()
'sessionId' => $session->isEmpty() ? '' : $session->getId()
])]), Response::MODEL_JWT);
});
+24 -1
View File
@@ -2,6 +2,7 @@
require_once __DIR__ . '/../init.php';
use Ahc\Jwt\JWT;
use Appwrite\Auth\Auth;
use Appwrite\Event\Certificate;
use Appwrite\Event\Event;
@@ -12,9 +13,11 @@ use Appwrite\Platform\Appwrite;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Request\Filters\V16 as RequestV16;
use Appwrite\Utopia\Request\Filters\V17 as RequestV17;
use Appwrite\Utopia\Request\Filters\V18 as RequestV18;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Filters\V16 as ResponseV16;
use Appwrite\Utopia\Response\Filters\V17 as ResponseV17;
use Appwrite\Utopia\Response\Filters\V18 as ResponseV18;
use Appwrite\Utopia\View;
use Executor\Executor;
use MaxMind\Db\Reader;
@@ -165,7 +168,15 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
throw new AppwriteException(AppwriteException::USER_UNAUTHORIZED, 'To execute function using domain, execute permissions must include "any" or "guests"');
}
$jwtExpiry = $function->getAttribute('timeout', 900);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
$apiKey = $jwtObj->encode([
'projectId' => $project->getId(),
'scopes' => $function->getAttribute('scopes', [])
]);
$headers = \array_merge([], $requestHeaders);
$headers['x-appwrite-key'] = API_KEY_DYNAMIC . '_' . $apiKey;
$headers['x-appwrite-trigger'] = 'http';
$headers['x-appwrite-user-id'] = '';
$headers['x-appwrite-user-jwt'] = '';
@@ -244,15 +255,21 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
$vars[$var->getAttribute('key')] = $var->getAttribute('value', '');
}
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_DOMAIN');
$endpoint = $protocol . '://' . $hostname . "/v1";
// Appwrite vars
$vars = \array_merge($vars, [
'APPWRITE_FUNCTION_API_ENDPOINT' => $endpoint,
'APPWRITE_FUNCTION_ID' => $functionId,
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
'APPWRITE_VERSION' => APP_VERSION_STABLE
'APPWRITE_VERSION' => APP_VERSION_STABLE,
'APPWRITE_REGION' => $project->getAttribute('region'),
]);
/** Execute function */
@@ -436,6 +453,9 @@ App::init()
if (version_compare($requestFormat, '1.5.0', '<')) {
$request->addFilter(new RequestV17());
}
if (version_compare($requestFormat, '1.6.0', '<')) {
$request->addFilter(new RequestV18());
}
}
$domain = $request->getHostname();
@@ -552,6 +572,9 @@ App::init()
if (version_compare($responseFormat, '1.5.0', '<')) {
$response->addFilter(new ResponseV17());
}
if (version_compare($responseFormat, '1.6.0', '<')) {
$response->addFilter(new ResponseV18());
}
if (version_compare($responseFormat, APP_VERSION_STABLE, '>')) {
$response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is ". APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks");
}
+1
View File
@@ -206,6 +206,7 @@ App::init()
throw new Exception(Exception::USER_API_KEY_AND_SESSION_SET);
}
// Remove after migration
if(!\str_contains($apiKey, '_')) {
$keyType = API_KEY_STANDARD;
$authKey = $apiKey;
+9 -42
View File
@@ -16,8 +16,7 @@ App::init()
;
});
App::get('/console/*')
->alias('/')
App::get('/')
->alias('auth/*')
->alias('/invite')
->alias('/login')
@@ -31,45 +30,13 @@ App::get('/console/*')
->inject('request')
->inject('response')
->action(function (Request $request, Response $response) {
$fallback = file_get_contents(__DIR__ . '/../../../console/index.html');
// Card SSR
if (\str_starts_with($request->getURI(), '/card')) {
$urlCunks = \explode('/', $request->getURI());
$userId = $urlCunks[\count($urlCunks) - 1] ?? '';
$domain = $request->getProtocol() . '://' . $request->getHostname();
if (!empty($userId)) {
$ogImageUrl = $domain . '/v1/cards/cloud-og?userId=' . $userId;
} else {
$ogImageUrl = $domain . '/v1/cards/cloud-og?mock=normal';
}
$ogTags = [
'<title>Appwrite Cloud Card</title>',
'<meta name="description" content="Appwrite Cloud is now LIVE! Share your Cloud card for a chance to win an exclusive Cloud hoodie!">',
'<meta property="og:url" content="' . $domain . $request->getURI() . '">',
'<meta name="og:image:type" content="image/png">',
'<meta name="og:image:width" content="1008">',
'<meta name="og:image:height" content="1008">',
'<meta property="og:type" content="website">',
'<meta property="og:title" content="Appwrite Cloud Card">',
'<meta property="og:description" content="Appwrite Cloud is now LIVE! Share your Cloud card for a chance to win an exclusive Cloud hoodie!">',
'<meta property="og:image" content="' . $ogImageUrl . '">',
'<meta name="twitter:card" content="summary_large_image">',
'<meta property="twitter:domain" content="' . $request->getHostname() . '">',
'<meta property="twitter:url" content="' . $domain . $request->getURI() . '">',
'<meta name="twitter:title" content="Appwrite Cloud Card">',
'<meta name="twitter:image:type" content="image/png">',
'<meta name="twitter:image:width" content="1008">',
'<meta name="twitter:image:height" content="1008">',
'<meta name="twitter:description" content="Appwrite Cloud is now LIVE! Share your Cloud card for a chance to win an exclusive Cloud hoodie!">',
'<meta name="twitter:image" content="' . $ogImageUrl . '">',
];
$fallback = \str_replace('<!-- {{CLOUD_OG}} -->', \implode('', $ogTags), $fallback);
$url = parse_url($request->getURI());
$target = "/console{$url['path']}";
if ($url['query'] ?? false) {
$target .= "?{$url['query']}";
}
$response->html($fallback);
if ($url['fragment'] ?? false) {
$target .= "#{$url['fragment']}";
}
$response->redirect($target);
});
-2
View File
@@ -57,8 +57,6 @@ $http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
Console::success('Reload completed...');
});
Files::load(__DIR__ . '/../console');
include __DIR__ . '/controllers/general.php';
$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) {
+7 -6
View File
@@ -118,7 +118,7 @@ const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours
const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 4314;
const APP_VERSION_STABLE = '1.5.7';
const APP_VERSION_STABLE = '1.6.0';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
@@ -1243,14 +1243,15 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
}
$jwtUserId = $payload['userId'] ?? '';
$jwtSessionId = $payload['sessionId'] ?? '';
if ($jwtUserId && $jwtSessionId) {
if (!empty($jwtUserId)) {
$user = $dbForProject->getDocument('users', $jwtUserId);
}
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
$user = new Document([]);
$jwtSessionId = $payload['sessionId'] ?? '';
if(!empty($jwtSessionId)) {
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
$user = new Document([]);
}
}
}
+22
View File
@@ -163,6 +163,28 @@ $image = $this->getParam('image', '');
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
- _APP_ASSISTANT_OPENAI_API_KEY
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: <?php echo $organization; ?>/console:appwrite/console:5.0.0-rc.11
restart: unless-stopped
networks:
- appwrite
labels:
- "traefik.enable=true"
- "traefik.constraint-label-stack=appwrite"
- "traefik.docker.network=appwrite"
- "traefik.http.services.appwrite_console.loadbalancer.server.port=80"
#ws
- traefik.http.routers.appwrite_console_http.entrypoints=appwrite_web
- traefik.http.routers.appwrite_console_http.rule=PathPrefix(`/console`)
- traefik.http.routers.appwrite_console_http.service=appwrite_console
# wss
- traefik.http.routers.appwrite_console_https.entrypoints=appwrite_websecure
- traefik.http.routers.appwrite_console_https.rule=PathPrefix(`/console`)
- traefik.http.routers.appwrite_console_https.service=appwrite_console
- traefik.http.routers.appwrite_console_https.tls=true
appwrite-realtime:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: realtime
Generated
+17 -16
View File
@@ -356,16 +356,16 @@
},
{
"name": "chillerlan/php-settings-container",
"version": "2.1.5",
"version": "2.1.6",
"source": {
"type": "git",
"url": "https://github.com/chillerlan/php-settings-container.git",
"reference": "f705310389264c3578fdd9ffb15aa2cd6d91772e"
"reference": "5553558bd381fce5108c6d0343c12e488cfec6bb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/f705310389264c3578fdd9ffb15aa2cd6d91772e",
"reference": "f705310389264c3578fdd9ffb15aa2cd6d91772e",
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/5553558bd381fce5108c6d0343c12e488cfec6bb",
"reference": "5553558bd381fce5108c6d0343c12e488cfec6bb",
"shasum": ""
},
"require": {
@@ -373,15 +373,16 @@
"php": "^7.4 || ^8.0"
},
"require-dev": {
"phan/phan": "^5.4",
"phpcsstandards/php_codesniffer": "^3.8",
"phpmd/phpmd": "^2.13",
"phpunit/phpunit": "^9.6"
"phpmd/phpmd": "^2.15",
"phpstan/phpstan": "^1.11",
"phpstan/phpstan-deprecation-rules": "^1.2",
"phpunit/phpunit": "^9.6",
"squizlabs/php_codesniffer": "^3.10"
},
"type": "library",
"autoload": {
"psr-4": {
"chillerlan\\Settings\\": "src/"
"chillerlan\\Settings\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -418,7 +419,7 @@
"type": "ko_fi"
}
],
"time": "2024-01-05T23:20:55+00:00"
"time": "2024-07-17T01:04:28+00:00"
},
{
"name": "dragonmantank/cron-expression",
@@ -2989,16 +2990,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "0.39.2",
"version": "0.39.3",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "550b56aa7ee3f98319b79a20d2478e68a1b5e9bd"
"reference": "16142d88270e368030d7956cadf2d7816413f8c4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/550b56aa7ee3f98319b79a20d2478e68a1b5e9bd",
"reference": "550b56aa7ee3f98319b79a20d2478e68a1b5e9bd",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/16142d88270e368030d7956cadf2d7816413f8c4",
"reference": "16142d88270e368030d7956cadf2d7816413f8c4",
"shasum": ""
},
"require": {
@@ -3034,9 +3035,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/0.39.2"
"source": "https://github.com/appwrite/sdk-generator/tree/0.39.3"
},
"time": "2024-07-10T11:23:54+00:00"
"time": "2024-07-12T15:29:48+00:00"
},
{
"name": "doctrine/deprecations",
+23 -1
View File
@@ -52,7 +52,7 @@ services:
DEBUG: false
TESTING: true
VERSION: dev
ports:
ports:
- 9501:80
networks:
- appwrite
@@ -192,6 +192,28 @@ services:
- _APP_EXPERIMENT_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: appwrite/console:5.0.0-rc.11
restart: unless-stopped
networks:
- appwrite
labels:
- "traefik.enable=true"
- "traefik.constraint-label-stack=appwrite"
- "traefik.docker.network=appwrite"
- "traefik.http.services.appwrite_console.loadbalancer.server.port=80"
#ws
- traefik.http.routers.appwrite_console_http.entrypoints=appwrite_web
- traefik.http.routers.appwrite_console_http.rule=PathPrefix(`/console`)
- traefik.http.routers.appwrite_console_http.service=appwrite_console
# wss
- traefik.http.routers.appwrite_console_https.entrypoints=appwrite_websecure
- traefik.http.routers.appwrite_console_https.rule=PathPrefix(`/console`)
- traefik.http.routers.appwrite_console_https.service=appwrite_console
- traefik.http.routers.appwrite_console_https.tls=true
appwrite-realtime:
entrypoint: realtime
<<: *x-logging
@@ -1,3 +1,2 @@
appwrite account deleteMfaAuthenticator \
--type totp \
--otp <OTP>
--type totp
+2
View File
@@ -1 +1,3 @@
Use this endpoint to fetch the favorite icon (AKA favicon) of any remote website URL.
This endpoint does not follow HTTP redirects.
+2
View File
@@ -1,3 +1,5 @@
Use this endpoint to fetch a remote image URL and crop it to any image size you want. This endpoint is very useful if you need to crop and display remote images in your app or in case you want to make sure a 3rd party image is properly served using a TLS protocol.
When one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 400x400px.
This endpoint does not follow HTTP redirects.
+1 -1
View File
@@ -6,7 +6,7 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
stopOnFailure="true"
>
<extensions>
<extension class="Appwrite\Tests\TestHook" />
+1
View File
@@ -86,6 +86,7 @@ abstract class Migration
'1.5.5' => 'V20',
'1.5.6' => 'V20',
'1.5.7' => 'V20',
'1.6.0' => 'V21'
];
/**
+180
View File
@@ -0,0 +1,180 @@
<?php
namespace Appwrite\Migration\Version;
use Appwrite\Migration\Migration;
use Exception;
use Throwable;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
class V21 extends Migration
{
/**
* @throws Throwable
*/
public function execute(): void
{
/**
* Disable SubQueries for Performance.
*/
foreach (['subQueryIndexes', 'subQueryPlatforms', 'subQueryDomains', 'subQueryKeys', 'subQueryWebhooks', 'subQuerySessions', 'subQueryTokens', 'subQueryMemberships', 'subQueryVariables', 'subQueryChallenges', 'subQueryProjectVariables', 'subQueryTargets', 'subQueryTopicTargets'] as $name) {
Database::addFilter(
$name,
fn () => null,
fn () => []
);
}
Console::log('Migrating Project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')');
$this->projectDB->setNamespace("_{$this->project->getInternalId()}");
Console::info('Migrating Collections');
$this->migrateCollections();
Console::info('Migrating Documents');
$this->forEachDocument([$this, 'fixDocument']);
}
/**
* Migrate Collections.
*
* @return void
* @throws Exception|Throwable
*/
private function migrateCollections(): void
{
$internalProjectId = $this->project->getInternalId();
$collectionType = match ($internalProjectId) {
'console' => 'console',
default => 'projects',
};
$collections = $this->collections[$collectionType];
foreach ($collections as $collection) {
$id = $collection['$id'];
Console::log("Migrating Collection \"{$id}\"");
$this->projectDB->setNamespace("_$internalProjectId");
switch ($id) {
case 'projects':
// Create accessedAt attribute
try {
$this->createAttributeFromCollection($this->projectDB, $id, 'accessedAt');
} catch (Throwable $th) {
Console::warning("'accessedAt' from {$id}: {$th->getMessage()}");
}
break;
case 'schedules':
// Create data attribute
try {
$this->createAttributeFromCollection($this->projectDB, $id, 'data');
} catch (Throwable $th) {
Console::warning("'data' from {$id}: {$th->getMessage()}");
}
break;
case 'functions':
// Create scopes attribute
try {
$this->createAttributeFromCollection($this->projectDB, $id, 'scopes');
} catch (Throwable $th) {
Console::warning("'scopes' from {$id}: {$th->getMessage()}");
}
// Create size attribute
try {
$this->createAttributeFromCollection($this->projectDB, $id, 'size');
} catch (Throwable $th) {
Console::warning("'size' from {$id}: {$th->getMessage()}");
}
break;
case 'executions':
// Create requestMethod index
try {
$this->createIndexFromCollection($this->projectDB, $id, '_key_requestMethod');
} catch (\Throwable $th) {
Console::warning("'_key_requestMethod' from {$id}: {$th->getMessage()}");
}
// Create requestPath index
try {
$this->createIndexFromCollection($this->projectDB, $id, '_key_requestPath');
} catch (\Throwable $th) {
Console::warning("'_key_requestPath' from {$id}: {$th->getMessage()}");
}
// Create deployment index
try {
$this->createIndexFromCollection($this->projectDB, $id, '_key_deployment');
} catch (\Throwable $th) {
Console::warning("'_key_deployment' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create 'scheduledAt' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'scheduledAt');
} catch (\Throwable $th) {
Console::warning("'scheduledAt' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create 'scheduleInternalId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'scheduleInternalId');
} catch (\Throwable $th) {
Console::warning("'scheduleInternalId' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create 'scheduleId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'scheduleId');
} catch (\Throwable $th) {
Console::warning("'scheduleId' from {$id}: {$th->getMessage()}");
}
}
usleep(50000);
}
}
/**
* Fix run on each document
*
* @param Document $document
* @return Document
*/
protected function fixDocument(Document $document): Document
{
switch ($document->getCollection()) {
case 'projects':
/**
* Bump version number.
*/
$document->setAttribute('version', '1.6.0');
// Add accessedAt attribute
$document->setAttribute('accessedAt', DateTime::now());
break;
case 'functions':
// Add scopes attribute
$document->setAttribute('scopes', []);
// Add size attribute
$document->setAttribute('size', 's-1vcpu-512m');
}
return $document;
}
}
+2 -1
View File
@@ -400,7 +400,8 @@ class Builds extends Action
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
'APPWRITE_VERSION' => APP_VERSION_STABLE
'APPWRITE_VERSION' => APP_VERSION_STABLE,
'APPWRITE_REGION' => $project->getAttribute('region'),
]);
$command = $deployment->getAttribute('commands', '');
+5 -1
View File
@@ -373,6 +373,9 @@ class Functions extends Action
$headers['x-appwrite-event'] = $event ?? '';
$headers['x-appwrite-user-id'] = $user->getId() ?? '';
$headers['x-appwrite-user-jwt'] = $jwt ?? '';
$headers['x-appwrite-country-code'] = '';
$headers['x-appwrite-continent-code'] = '';
$headers['x-appwrite-continent-eu'] = 'false';
/** Create execution or update execution status */
$execution = $dbForProject->getDocument('executions', $executionId ?? '');
@@ -464,7 +467,8 @@ class Functions extends Action
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
'APPWRITE_VERSION' => APP_VERSION_STABLE
'APPWRITE_VERSION' => APP_VERSION_STABLE,
'APPWRITE_REGION' => $project->getAttribute('region'),
]);
/** Execute function */
@@ -0,0 +1,21 @@
<?php
namespace Appwrite\Utopia\Request\Filters;
use Appwrite\Utopia\Request\Filter;
class V18 extends Filter
{
// Convert 1.5 params to 1.6
public function parse(array $content, string $model): array
{
switch ($model) {
case 'account.deleteMfaAuthenticator':
unset($content['otp']);
break;
}
return $content;
}
}
@@ -0,0 +1,43 @@
<?php
namespace Appwrite\Utopia\Response\Filters;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Filter;
class V18 extends Filter
{
// Convert 1.6 Data format to 1.5 format
public function parse(array $content, string $model): array
{
$parsedResponse = $content;
$parsedResponse = match($model) {
Response::MODEL_FUNCTION => $this->parseFunction($content),
Response::MODEL_EXECUTION => $this->parseExecution($content),
Response::MODEL_PROJECT => $this->parseProject($content),
default => $parsedResponse,
};
return $parsedResponse;
}
protected function parseExecution(array $content)
{
unset($content['scheduledAt']);
return $content;
}
protected function parseFunction(array $content)
{
unset($content['scopes']);
return $content;
}
protected function parseProject(array $content)
{
unset($content['authMockNumbers']);
unset($content['authSessionAlerts']);
return $content;
}
}
@@ -4,6 +4,7 @@ namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
use Utopia\Database\DateTime;
use Utopia\Database\Helpers\Role;
class Execution extends Model
@@ -110,6 +111,13 @@ class Execution extends Model
'default' => 0,
'example' => 0.400,
])
->addRule('scheduledAt', [
'type' => self::TYPE_DATETIME,
'description' => 'The scheduled time for execution. If left empty, execution will be queued immediately.',
'required' => false,
'default' => DateTime::now(),
'example' => self::TYPE_DATETIME_EXAMPLE,
])
;
}
+13
View File
@@ -75,6 +75,12 @@ class Executor
$runtimeId = "$projectId-$deploymentId-build";
$route = "/runtimes";
$timeout = (int) System::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900);
// Remove after migration
if ($version == 'v3') {
$version = 'v4';
}
$params = [
'runtimeId' => $runtimeId,
'source' => $source,
@@ -188,6 +194,13 @@ class Executor
$runtimeId = "$projectId-$deploymentId";
$route = '/runtimes/' . $runtimeId . '/execution';
// Remove after migration
if ($version == 'v3') {
$version = 'v4';
}
$params = [
'runtimeId' => $runtimeId,
'variables' => $variables,
+3 -3
View File
@@ -15,7 +15,7 @@ class HTTPTest extends Scope
public function setUp(): void
{
parent::setUp();
$this->client->setEndpoint('http://localhost');
$this->client->setEndpoint('http://traefik');
}
public function testOptions()
@@ -163,11 +163,11 @@ class HTTPTest extends Scope
public function testDefaultOAuth2()
{
$response = $this->client->call(Client::METHOD_GET, '/auth/oauth2/success', $this->getHeaders());
$response = $this->client->call(Client::METHOD_GET, '/console/auth/oauth2/success', $this->getHeaders());
$this->assertEquals(200, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_GET, '/auth/oauth2/failure', $this->getHeaders());
$response = $this->client->call(Client::METHOD_GET, '/console/auth/oauth2/failure', $this->getHeaders());
$this->assertEquals(200, $response['headers']['status-code']);
}
+1 -1
View File
@@ -17,7 +17,7 @@ class HooksTest extends Scope
public function setUp(): void
{
parent::setUp();
$this->client->setEndpoint('http://localhost');
$this->client->setEndpoint('http://traefik');
}
public function testProjectHooks()
@@ -8,6 +8,7 @@ use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideClient;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Role;
@@ -269,6 +270,7 @@ class FunctionsCustomClientTest extends Scope
// Schedule execution for the future
\date_default_timezone_set('UTC');
$futureTime = (new \DateTime())->add(new \DateInterval('PT10S'))->format('Y-m-d H:i:s');
$futureTimeIso = DateTime::formatTz($futureTime);
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', array_merge([
'content-type' => 'application/json',
@@ -286,9 +288,23 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(202, $execution['headers']['status-code']);
$this->assertEquals('scheduled', $execution['body']['status']);
$this->assertEquals($futureTimeIso, $execution['body']['scheduledAt']);
$executionId = $execution['body']['$id'];
// List executions and ensure it has schedule date
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertGreaterThan(0, \count($response['body']['executions']));
$recentExecution = $response['body']['executions'][0];
$this->assertEquals($executionId, $recentExecution['$id']);
$this->assertEquals($futureTimeIso, $recentExecution['scheduledAt']);
sleep(20);
$execution = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions/' . $executionId, [
@@ -303,6 +319,7 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals('/custom', $execution['body']['requestPath']);
$this->assertEquals('GET', $execution['body']['requestMethod']);
$this->assertGreaterThan(0, $execution['body']['duration']);
$this->assertEquals($futureTimeIso, $execution['body']['scheduledAt']);
/* Test for FAILURE */
@@ -449,6 +466,7 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals('PHP', $output['APPWRITE_FUNCTION_RUNTIME_NAME']);
$this->assertEquals('8.0', $output['APPWRITE_FUNCTION_RUNTIME_VERSION']);
$this->assertEquals(APP_VERSION_STABLE, $output['APPWRITE_VERSION']);
$this->assertEquals('default', $output['APPWRITE_REGION']);
$this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']);
$this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']);
$this->assertEquals($this->getUser()['$id'], $output['APPWRITE_FUNCTION_USER_ID']);
@@ -834,6 +852,7 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals('PHP', $output['APPWRITE_FUNCTION_RUNTIME_NAME']);
$this->assertEquals('8.0', $output['APPWRITE_FUNCTION_RUNTIME_VERSION']);
$this->assertEquals(APP_VERSION_STABLE, $output['APPWRITE_VERSION']);
$this->assertEquals('default', $output['APPWRITE_REGION']);
$this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']);
$this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']);
$this->assertEquals($this->getUser()['$id'], $output['APPWRITE_FUNCTION_USER_ID']);
+45 -2
View File
@@ -1589,6 +1589,27 @@ trait UsersBase
], false);
$this->assertEquals($user['headers']['status-code'], 201);
// Create JWT 0, with no session available
$response = $this->client->call(Client::METHOD_POST, '/users/' . $userId . '/jwts', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['jwt']);
$jwt0 = $response['body']['jwt'];
// Ensure JWT 0 works
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-jwt' => $jwt0,
]));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($userId, $response['body']['$id']);
// Create two sessions
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
@@ -1641,12 +1662,13 @@ trait UsersBase
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($userId, $response['body']['$id']);
// Create JWT 2 for latest session using default param
// Create JWT 2 for latest session using 'current' param
$response = $this->client->call(Client::METHOD_POST, '/users/' . $userId . '/jwts', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'duration' => 5
'duration' => 5,
'sessionId' => 'current'
]);
$this->assertEquals(201, $response['headers']['status-code']);
@@ -1696,6 +1718,27 @@ trait UsersBase
$this->assertEquals(401, $response['headers']['status-code']);
// Ensure JWT 0 works still even with no sessions
$response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId . '/sessions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'sessionId' => $session2Id
]);
$this->assertEquals(204, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-jwt' => $jwt0,
]));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($userId, $response['body']['$id']);
// Cleanup after test
$response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId, array_merge([
@@ -11,6 +11,7 @@ return function ($context) {
'APPWRITE_FUNCTION_RUNTIME_NAME' => \getenv('APPWRITE_FUNCTION_RUNTIME_NAME') ?: '',
'APPWRITE_FUNCTION_RUNTIME_VERSION' => \getenv('APPWRITE_FUNCTION_RUNTIME_VERSION') ?: '',
'APPWRITE_VERSION' => \getenv('APPWRITE_VERSION') ?: '',
'APPWRITE_REGION' => \getenv('APPWRITE_REGION') ?: '',
'APPWRITE_FUNCTION_EVENT' => $context->req->headers['x-appwrite-event'] ?? '',
'APPWRITE_FUNCTION_EVENT_DATA' => $context->req->bodyRaw ?? '',
'APPWRITE_FUNCTION_DATA' => $context->req->bodyRaw ?? '',
@@ -8,6 +8,7 @@ return function ($context) {
'APPWRITE_FUNCTION_TRIGGER' => $context->req->headers['x-appwrite-trigger'] ?? '',
'APPWRITE_FUNCTION_RUNTIME_NAME' => \getenv('APPWRITE_FUNCTION_RUNTIME_NAME') ?: '',
'APPWRITE_FUNCTION_RUNTIME_VERSION' => \getenv('APPWRITE_FUNCTION_RUNTIME_VERSION') ?: '',
'APPWRITE_REGION' => \getenv('APPWRITE_REGION') ?: '',
'UNICODE_TEST' => "êä"
]);
};
+1
View File
@@ -8,6 +8,7 @@ return function ($context) {
'APPWRITE_FUNCTION_TRIGGER' => $context->req->headers['x-appwrite-trigger'] ?? '',
'APPWRITE_FUNCTION_RUNTIME_NAME' => \getenv('APPWRITE_FUNCTION_RUNTIME_NAME') ?: '',
'APPWRITE_FUNCTION_RUNTIME_VERSION' => \getenv('APPWRITE_FUNCTION_RUNTIME_VERSION') ?: '',
'APPWRITE_REGION' => \getenv('APPWRITE_REGION') ?: '',
'UNICODE_TEST' => "êä",
'GLOBAL_VARIABLE' => \getenv('GLOBAL_VARIABLE') ?: ''
]);
@@ -0,0 +1,51 @@
<?php
namespace Tests\Unit\Utopia\Request\Filters;
use Appwrite\Utopia\Request\Filter;
use Appwrite\Utopia\Request\Filters\V18;
use PHPUnit\Framework\TestCase;
class V18Test extends TestCase
{
/**
* @var Filter
*/
protected $filter;
public function setUp(): void
{
$this->filter = new V18();
}
public function tearDown(): void
{
}
public function deleteMfaAuthenticatorProvider()
{
return [
'remove otp' => [
[
'type' => 'totp',
'otp' => 1230
],
[
'type' => 'totp'
]
]
];
}
/**
* @dataProvider deleteMfaAuthenticatorProvider
*/
public function testdeleteMfaAuthenticator(array $content, array $expected): void
{
$model = 'account.deleteMfaAuthenticator';
$result = $this->filter->parse($content, $model);
$this->assertEquals($expected, $result);
}
}
@@ -0,0 +1,110 @@
<?php
namespace Tests\Unit\Utopia\Response\Filters;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Filters\V18;
use PHPUnit\Framework\TestCase;
class V18Test extends TestCase
{
/**
* @var Filter
*/
protected $filter = null;
public function setUp(): void
{
$this->filter = new V18();
}
public function tearDown(): void
{
}
public function functionProvider(): array
{
return [
'remove scopes' => [
[
'scopes' => [
'example_scope',
'example_scope2',
],
],
[
]
]
];
}
/**
* @dataProvider functionProvider
*/
public function testFunction(array $content, array $expected): void
{
$model = Response::MODEL_FUNCTION;
$result = $this->filter->parse($content, $model);
$this->assertEquals($expected, $result);
}
public function executionProvider(): array
{
return [
'remove scheduledAt' => [
[
'scheduledAt' => '2024-07-13T09:00:00.000Z',
],
[
]
]
];
}
/**
* @dataProvider executionProvider
*/
public function testExecution(array $content, array $expected): void
{
$model = Response::MODEL_EXECUTION;
$result = $this->filter->parse($content, $model);
$this->assertEquals($expected, $result);
}
public function projectProvider(): array
{
return [
'remove authMockNumbers and authSessionAlerts' => [
[
'authMockNumbers' => [
'example_mock_number',
'example_mock_number2',
],
'authSessionAlerts' => [
'example_alert',
'example_alert2',
],
],
[
]
]
];
}
/**
* @dataProvider projectProvider
*/
public function testProject(array $content, array $expected): void
{
$model = Response::MODEL_PROJECT;
$result = $this->filter->parse($content, $model);
$this->assertEquals($expected, $result);
}
}