mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Final template API rework
This commit is contained in:
@@ -701,9 +701,8 @@ jobs:
|
||||
|
||||
- name: Installing latest version
|
||||
run: |
|
||||
rm docker-compose.yml
|
||||
# TODO: Minify docker-compose; remove development tooling
|
||||
rm .env
|
||||
curl https://appwrite.io/install/compose -o docker-compose.yml
|
||||
curl https://appwrite.io/install/env -o .env
|
||||
sed -i 's/_APP_OPTIONS_ABUSE=enabled/_APP_OPTIONS_ABUSE=disabled/g' .env
|
||||
docker compose up -d
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
# Version 1.9.2
|
||||
|
||||
### Notable changes
|
||||
|
||||
### Fixes
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
# Version 1.9.0
|
||||
|
||||
## What's Changed
|
||||
|
||||
+3
-3
@@ -72,7 +72,7 @@ docker run -it --rm \
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:1.9.1
|
||||
appwrite/appwrite:1.9.2
|
||||
```
|
||||
|
||||
### Windows
|
||||
@@ -84,7 +84,7 @@ docker run -it --rm ^
|
||||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:1.9.1
|
||||
appwrite/appwrite:1.9.2
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
@@ -94,7 +94,7 @@ docker run -it --rm `
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock `
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
|
||||
--entrypoint="install" `
|
||||
appwrite/appwrite:1.9.1
|
||||
appwrite/appwrite:1.9.2
|
||||
```
|
||||
|
||||
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
|
||||
|
||||
@@ -75,7 +75,7 @@ docker run -it --rm \
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:1.9.1
|
||||
appwrite/appwrite:1.9.2
|
||||
```
|
||||
|
||||
### Windows
|
||||
@@ -88,7 +88,7 @@ docker run -it --rm ^
|
||||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:1.9.1
|
||||
appwrite/appwrite:1.9.2
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
@@ -99,7 +99,7 @@ docker run -it --rm `
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock `
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
|
||||
--entrypoint="install" `
|
||||
appwrite/appwrite:1.9.1
|
||||
appwrite/appwrite:1.9.2
|
||||
```
|
||||
|
||||
Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation.
|
||||
|
||||
@@ -26,6 +26,7 @@ use Appwrite\Utopia\Request\Filters\V19 as RequestV19;
|
||||
use Appwrite\Utopia\Request\Filters\V20 as RequestV20;
|
||||
use Appwrite\Utopia\Request\Filters\V21 as RequestV21;
|
||||
use Appwrite\Utopia\Request\Filters\V22 as RequestV22;
|
||||
use Appwrite\Utopia\Request\Filters\V23 as RequestV23;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Filters\V16 as ResponseV16;
|
||||
use Appwrite\Utopia\Response\Filters\V17 as ResponseV17;
|
||||
@@ -34,6 +35,7 @@ use Appwrite\Utopia\Response\Filters\V19 as ResponseV19;
|
||||
use Appwrite\Utopia\Response\Filters\V20 as ResponseV20;
|
||||
use Appwrite\Utopia\Response\Filters\V21 as ResponseV21;
|
||||
use Appwrite\Utopia\Response\Filters\V22 as ResponseV22;
|
||||
use Appwrite\Utopia\Response\Filters\V23 as ResponseV23;
|
||||
use Appwrite\Utopia\View;
|
||||
use Executor\Executor;
|
||||
use MaxMind\Db\Reader;
|
||||
@@ -897,6 +899,9 @@ Http::init()
|
||||
if (version_compare($requestFormat, '1.9.1', '<')) {
|
||||
$request->addFilter(new RequestV22());
|
||||
}
|
||||
if (version_compare($requestFormat, '1.9.2', '<')) {
|
||||
$request->addFilter(new RequestV23());
|
||||
}
|
||||
}
|
||||
|
||||
$localeParam = (string) $request->getParam('locale', $request->getHeader('x-appwrite-locale', ''));
|
||||
@@ -921,6 +926,9 @@ Http::init()
|
||||
*/
|
||||
$responseFormat = $request->getHeader('x-appwrite-response-format', System::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
|
||||
if ($responseFormat) {
|
||||
if (version_compare($responseFormat, '1.9.2', '<')) {
|
||||
$response->addFilter(new ResponseV23());
|
||||
}
|
||||
if (version_compare($responseFormat, '1.9.1', '<')) {
|
||||
$response->addFilter(new ResponseV22());
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_RESOURCE_TOKEN_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_BUSTER = 4322;
|
||||
const APP_VERSION_STABLE = '1.9.1';
|
||||
const APP_CACHE_BUSTER = 4323;
|
||||
const APP_VERSION_STABLE = '1.9.2';
|
||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
|
||||
|
||||
@@ -94,6 +94,7 @@ abstract class Migration
|
||||
'1.8.1' => 'V23',
|
||||
'1.9.0' => 'V24',
|
||||
'1.9.1' => 'V24',
|
||||
'1.9.2' => 'V24',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,13 +33,13 @@ class Delete extends Action
|
||||
$this->setHttpMethod(Action::HTTP_REQUEST_METHOD_DELETE)
|
||||
->setHttpPath('/v1/project/templates/email')
|
||||
->httpAlias('/v1/projects/:projectId/templates/email')
|
||||
->httpAlias('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
->httpAlias('/v1/projects/:projectId/templates/email/:templateId/:locale')
|
||||
->desc('Delete project email template')
|
||||
->groups(['api', 'project'])
|
||||
->label('scope', 'templates.write')
|
||||
->label('event', 'templates.[templateType].delete')
|
||||
->label('audits.event', 'project.template.delete')
|
||||
->label('audits.resource', 'project.template/{response.type}')
|
||||
->label('audits.resource', 'project.template/{response.templateId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: 'templates',
|
||||
@@ -56,8 +56,8 @@ class Delete extends Action
|
||||
],
|
||||
contentType: ContentType::NONE
|
||||
))
|
||||
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? [], true), 'Custom email template type. Can be one of: '.\implode(', ', Config::getParam('locale-templates')['email'] ?? []))
|
||||
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Custom email template locale.', optional: true, injections: ['localeCodes'])
|
||||
->param('templateId', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? [], true), 'Custom email template type. Can be one of: '.\implode(', ', Config::getParam('locale-templates')['email'] ?? []))
|
||||
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Custom email template locale. If left empty, the fallback locale (en) will be used.', optional: true, injections: ['localeCodes'])
|
||||
->inject('response')
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForPlatform')
|
||||
@@ -68,7 +68,7 @@ class Delete extends Action
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $type,
|
||||
string $templateId,
|
||||
string $locale,
|
||||
Response $response,
|
||||
QueueEvent $queueForEvents,
|
||||
@@ -77,16 +77,16 @@ class Delete extends Action
|
||||
Document $project,
|
||||
Locale $localeObject,
|
||||
) {
|
||||
$locale = $locale ?: $localeObject->default ?: $localeObject->fallback ?: System::getEnv('_APP_LOCALE', 'en');
|
||||
$locale = $locale ?: System::getEnv('_APP_LOCALE', 'en');
|
||||
|
||||
$templates = $project->getAttribute('templates', []);
|
||||
$template = $templates['email.' . $type . '-' . $locale] ?? null;
|
||||
$template = $templates['email.' . $templateId . '-' . $locale] ?? null;
|
||||
|
||||
if (is_null($template)) {
|
||||
throw new Exception(Exception::PROJECT_TEMPLATE_DEFAULT_DELETION);
|
||||
}
|
||||
|
||||
unset($templates['email.' . $type . '-' . $locale]);
|
||||
unset($templates['email.' . $templateId . '-' . $locale]);
|
||||
|
||||
$updates = new Document([
|
||||
'templates' => $templates,
|
||||
@@ -94,7 +94,7 @@ class Delete extends Action
|
||||
|
||||
$project = $authorization->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $updates));
|
||||
|
||||
$queueForEvents->setParam('templateType', $type);
|
||||
$queueForEvents->setParam('templateType', $templateId);
|
||||
|
||||
$response->noContent();
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ class Get extends Action
|
||||
public function __construct()
|
||||
{
|
||||
$this->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/project/templates/email/:type')
|
||||
->httpAlias('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
->setHttpPath('/v1/project/templates/email/:templateId')
|
||||
->httpAlias('/v1/projects/:projectId/templates/email/:templateId/:locale')
|
||||
->desc('Get project email template')
|
||||
->groups(['api', 'project'])
|
||||
->label('scope', 'templates.read')
|
||||
@@ -47,8 +47,8 @@ class Get extends Action
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? [], true), 'Custom email template type. Can be one of: '.\implode(', ', Config::getParam('locale-templates')['email'] ?? []))
|
||||
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Custom email template locale.', optional: true, injections: ['localeCodes'])
|
||||
->param('templateId', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? [], true), 'Custom email template type. Can be one of: '.\implode(', ', Config::getParam('locale-templates')['email'] ?? []))
|
||||
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Custom email template locale. If left empty, the fallback locale (en) will be used.', optional: true, injections: ['localeCodes'])
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('locale')
|
||||
@@ -56,16 +56,16 @@ class Get extends Action
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $type,
|
||||
string $templateId,
|
||||
string $locale,
|
||||
Response $response,
|
||||
Document $project,
|
||||
Locale $localeObject,
|
||||
) {
|
||||
$locale = $locale ?: $localeObject->default ?: $localeObject->fallback ?: System::getEnv('_APP_LOCALE', 'en');
|
||||
$locale = $locale ?: System::getEnv('_APP_LOCALE', 'en');
|
||||
|
||||
$templates = $project->getAttribute('templates', []);
|
||||
$template = $templates['email.' . $type . '-' . $locale] ?? null;
|
||||
$template = $templates['email.' . $templateId . '-' . $locale] ?? null;
|
||||
|
||||
$localeObj = new Locale($locale);
|
||||
$localeObj->setFallback(System::getEnv('_APP_LOCALE', 'en'));
|
||||
@@ -94,7 +94,7 @@ class Get extends Action
|
||||
];
|
||||
|
||||
// fallback to the base template.
|
||||
$config = $templateConfigs[$type] ?? [
|
||||
$config = $templateConfigs[$templateId] ?? [
|
||||
'file' => 'email-inner-base.tpl',
|
||||
'placeholders' => ['buttonText', 'body', 'footer']
|
||||
];
|
||||
@@ -107,21 +107,21 @@ class Get extends Action
|
||||
// Set type-specific parameters
|
||||
foreach ($config['placeholders'] as $param) {
|
||||
$escapeHtml = !in_array($param, ['clientInfo', 'body', 'footer', 'description']);
|
||||
$message->setParam("{{{$param}}}", $localeObj->getText("emails.{$type}.{$param}"), escapeHtml: $escapeHtml);
|
||||
$message->setParam("{{{$param}}}", $localeObj->getText("emails.{$templateId}.{$param}"), escapeHtml: $escapeHtml);
|
||||
}
|
||||
|
||||
$message
|
||||
// common placeholders on all the templates
|
||||
->setParam('{{hello}}', $localeObj->getText("emails.{$type}.hello"))
|
||||
->setParam('{{thanks}}', $localeObj->getText("emails.{$type}.thanks"))
|
||||
->setParam('{{signature}}', $localeObj->getText("emails.{$type}.signature"));
|
||||
->setParam('{{hello}}', $localeObj->getText("emails.{$templateId}.hello"))
|
||||
->setParam('{{thanks}}', $localeObj->getText("emails.{$templateId}.thanks"))
|
||||
->setParam('{{signature}}', $localeObj->getText("emails.{$templateId}.signature"));
|
||||
|
||||
// `useContent: false` will strip new lines!
|
||||
$message = $message->render(useContent: true);
|
||||
|
||||
$template = [
|
||||
'message' => $message,
|
||||
'subject' => $localeObj->getText('emails.' . $type . '.subject'),
|
||||
'subject' => $localeObj->getText('emails.' . $templateId . '.subject'),
|
||||
'senderEmail' => '',
|
||||
'senderName' => '',
|
||||
'custom' => false,
|
||||
@@ -130,7 +130,7 @@ class Get extends Action
|
||||
$template['custom'] = true;
|
||||
}
|
||||
|
||||
$template['type'] = $type;
|
||||
$template['templateId'] = $templateId;
|
||||
$template['locale'] = $locale;
|
||||
|
||||
$response->dynamic(new Document($template), Response::MODEL_EMAIL_TEMPLATE);
|
||||
|
||||
@@ -33,13 +33,13 @@ class Update extends Action
|
||||
$this->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpPath('/v1/project/templates/email')
|
||||
->httpAlias('/v1/projects/:projectId/templates/email')
|
||||
->httpAlias('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
->httpAlias('/v1/projects/:projectId/templates/email/:templateId/:locale')
|
||||
->desc('Update project email template')
|
||||
->groups(['api', 'project'])
|
||||
->label('scope', 'templates.write')
|
||||
->label('event', 'templates.[templateType].update')
|
||||
->label('audits.event', 'project.template.update')
|
||||
->label('audits.resource', 'project.template/{response.type}')
|
||||
->label('audits.resource', 'project.template/{response.templateId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: 'templates',
|
||||
@@ -55,8 +55,8 @@ class Update extends Action
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? [], true), 'Custom email template type. Can be one of: '.\implode(', ', Config::getParam('locale-templates')['email'] ?? []))
|
||||
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Custom email template locale.', optional: true, injections: ['localeCodes'])
|
||||
->param('templateId', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? [], true), 'Custom email template type. Can be one of: '.\implode(', ', Config::getParam('locale-templates')['email'] ?? []))
|
||||
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Custom email template locale. If left empty, the fallback locale (en) will be used.', optional: true, injections: ['localeCodes'])
|
||||
->param('subject', '', new Text(255), 'Subject of the email template. Can be up to 255 characters.')
|
||||
->param('message', '', new Text(10485760), 'Plain or HTML body of the email template message. Can be up to 10MB of content.')
|
||||
->param('senderName', '', new Text(255, 0), 'Name of the email sender.', true)
|
||||
@@ -72,7 +72,7 @@ class Update extends Action
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $type,
|
||||
string $templateId,
|
||||
string $locale,
|
||||
string $subject,
|
||||
string $message,
|
||||
@@ -86,7 +86,7 @@ class Update extends Action
|
||||
Document $project,
|
||||
Locale $localeObject,
|
||||
) {
|
||||
$locale = $locale ?: $localeObject->default ?: $localeObject->fallback ?: System::getEnv('_APP_LOCALE', 'en');
|
||||
$locale = $locale ?: System::getEnv('_APP_LOCALE', 'en');
|
||||
|
||||
$template = [
|
||||
'senderName' => $senderName,
|
||||
@@ -97,7 +97,7 @@ class Update extends Action
|
||||
];
|
||||
|
||||
$templates = $project->getAttribute('templates', []);
|
||||
$templates['email.' . $type . '-' . $locale] = $template;
|
||||
$templates['email.' . $templateId . '-' . $locale] = $template;
|
||||
|
||||
$updates = new Document([
|
||||
'templates' => $templates,
|
||||
@@ -105,10 +105,10 @@ class Update extends Action
|
||||
|
||||
$project = $authorization->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $updates));
|
||||
|
||||
$queueForEvents->setParam('templateType', $type);
|
||||
$queueForEvents->setParam('templateType', $templateId);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'type' => $type,
|
||||
'templateId' => $templateId,
|
||||
'locale' => $locale,
|
||||
'senderName' => $template['senderName'],
|
||||
'senderEmail' => $template['senderEmail'],
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Request\Filters;
|
||||
|
||||
use Appwrite\Utopia\Request\Filter;
|
||||
|
||||
class V23 extends Filter
|
||||
{
|
||||
// Convert 1.9.1 params to 1.9.2
|
||||
protected function parseEmailTemplate(array $content): array
|
||||
{
|
||||
if (isset($content['type'])) {
|
||||
$content['templateId'] = $content['type'];
|
||||
unset($content['type']);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
public function parse(array $content, string $model): array
|
||||
{
|
||||
switch ($model) {
|
||||
case 'project.getEmailTemplate':
|
||||
case 'project.updateEmailTemplate':
|
||||
case 'project.deleteEmailTemplate':
|
||||
$content = $this->parseEmailTemplate($content);
|
||||
break;
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Filters;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Filter;
|
||||
|
||||
// Convert 1.9.2 Data format to 1.9.1 format
|
||||
class V23 extends Filter
|
||||
{
|
||||
public function parse(array $content, string $model): array
|
||||
{
|
||||
return match ($model) {
|
||||
Response::MODEL_EMAIL_TEMPLATE => $this->parseEmailTemplate($content),
|
||||
default => $content,
|
||||
};
|
||||
}
|
||||
|
||||
private function parseEmailTemplate(array $content): array
|
||||
{
|
||||
if (isset($content['templateId'])) {
|
||||
$content['type'] = $content['templateId'];
|
||||
unset($content['templateId']);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
abstract class Template extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->addRule('type', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Template type',
|
||||
'default' => '',
|
||||
'example' => 'verification',
|
||||
])
|
||||
->addRule('locale', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Template locale',
|
||||
'default' => '',
|
||||
'example' => 'en_us',
|
||||
])
|
||||
->addRule('message', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Template message',
|
||||
'default' => '',
|
||||
'example' => 'Click on the link to verify your account.',
|
||||
])
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,31 @@
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class TemplateEmail extends Template
|
||||
class TemplateEmail extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this
|
||||
->addRule('templateId', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Template type',
|
||||
'default' => '',
|
||||
'example' => 'verification',
|
||||
])
|
||||
->addRule('locale', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Template locale',
|
||||
'default' => '',
|
||||
'example' => 'en_us',
|
||||
])
|
||||
->addRule('message', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Template message',
|
||||
'default' => '',
|
||||
'example' => 'Click on the link to verify your account.',
|
||||
])
|
||||
->addRule('senderName', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Name of the sender',
|
||||
|
||||
@@ -15,7 +15,7 @@ trait TemplatesBase
|
||||
$template = $this->getEmailTemplate('verification', 'en');
|
||||
|
||||
$this->assertSame(200, $template['headers']['status-code']);
|
||||
$this->assertSame('verification', $template['body']['type']);
|
||||
$this->assertSame('verification', $template['body']['templateId']);
|
||||
$this->assertSame('en', $template['body']['locale']);
|
||||
$this->assertFalse($template['body']['custom']);
|
||||
$this->assertNotEmpty($template['body']['subject']);
|
||||
@@ -27,7 +27,7 @@ trait TemplatesBase
|
||||
$template = $this->getEmailTemplate('verification');
|
||||
|
||||
$this->assertSame(200, $template['headers']['status-code']);
|
||||
$this->assertSame('verification', $template['body']['type']);
|
||||
$this->assertSame('verification', $template['body']['templateId']);
|
||||
$this->assertSame('en', $template['body']['locale']);
|
||||
$this->assertFalse($template['body']['custom']);
|
||||
}
|
||||
@@ -40,7 +40,7 @@ trait TemplatesBase
|
||||
$get = $this->getEmailTemplate('magicSession', 'en');
|
||||
|
||||
$this->assertSame(200, $get['headers']['status-code']);
|
||||
$this->assertSame('magicSession', $get['body']['type']);
|
||||
$this->assertSame('magicSession', $get['body']['templateId']);
|
||||
$this->assertSame('en', $get['body']['locale']);
|
||||
$this->assertTrue($get['body']['custom']);
|
||||
$this->assertSame('Magic Subject', $get['body']['subject']);
|
||||
@@ -85,7 +85,7 @@ trait TemplatesBase
|
||||
);
|
||||
|
||||
$this->assertSame(200, $update['headers']['status-code']);
|
||||
$this->assertSame('verification', $update['body']['type']);
|
||||
$this->assertSame('verification', $update['body']['templateId']);
|
||||
$this->assertSame('en', $update['body']['locale']);
|
||||
$this->assertSame('Please verify your email', $update['body']['subject']);
|
||||
$this->assertSame('Click here to verify: {{url}}', $update['body']['message']);
|
||||
@@ -135,7 +135,7 @@ trait TemplatesBase
|
||||
);
|
||||
|
||||
$this->assertSame(200, $update['headers']['status-code']);
|
||||
$this->assertSame('sessionAlert', $update['body']['type']);
|
||||
$this->assertSame('sessionAlert', $update['body']['templateId']);
|
||||
$this->assertSame('en', $update['body']['locale']);
|
||||
|
||||
// Cleanup
|
||||
@@ -281,6 +281,105 @@ trait TemplatesBase
|
||||
$this->deleteEmailTemplate('recovery', 'en');
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Legacy response format tests (request + response filters)
|
||||
// =========================================================================
|
||||
|
||||
public function testGetEmailTemplateLegacyResponseFormat(): void
|
||||
{
|
||||
$headers = \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-response-format' => '1.9.1',
|
||||
], $this->getHeaders());
|
||||
|
||||
$template = $this->client->call(
|
||||
Client::METHOD_GET,
|
||||
'/project/templates/email/verification',
|
||||
$headers,
|
||||
);
|
||||
|
||||
$this->assertSame(200, $template['headers']['status-code']);
|
||||
// Response filter should rename templateId -> type for < 1.9.2 clients.
|
||||
$this->assertArrayHasKey('type', $template['body']);
|
||||
$this->assertArrayNotHasKey('templateId', $template['body']);
|
||||
$this->assertSame('verification', $template['body']['type']);
|
||||
$this->assertSame('en', $template['body']['locale']);
|
||||
}
|
||||
|
||||
public function testUpdateEmailTemplateLegacyResponseFormat(): void
|
||||
{
|
||||
$headers = \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-response-format' => '1.9.1',
|
||||
], $this->getHeaders());
|
||||
|
||||
// Request filter should accept legacy `type` and map it to `templateId`.
|
||||
$update = $this->client->call(
|
||||
Client::METHOD_PATCH,
|
||||
'/project/templates/email',
|
||||
$headers,
|
||||
[
|
||||
'type' => 'magicSession',
|
||||
'locale' => 'en',
|
||||
'subject' => 'Legacy Subject',
|
||||
'message' => 'Legacy Body',
|
||||
],
|
||||
);
|
||||
|
||||
$this->assertSame(200, $update['headers']['status-code']);
|
||||
// Response filter should rename templateId -> type for < 1.9.2 clients.
|
||||
$this->assertArrayHasKey('type', $update['body']);
|
||||
$this->assertArrayNotHasKey('templateId', $update['body']);
|
||||
$this->assertSame('magicSession', $update['body']['type']);
|
||||
$this->assertSame('Legacy Subject', $update['body']['subject']);
|
||||
$this->assertSame('Legacy Body', $update['body']['message']);
|
||||
$this->assertTrue($update['body']['custom']);
|
||||
|
||||
// Verify persisted, then cleanup via legacy DELETE with `type`.
|
||||
$get = $this->getEmailTemplate('magicSession', 'en');
|
||||
$this->assertSame(200, $get['headers']['status-code']);
|
||||
$this->assertTrue($get['body']['custom']);
|
||||
|
||||
$delete = $this->client->call(
|
||||
Client::METHOD_DELETE,
|
||||
'/project/templates/email',
|
||||
$headers,
|
||||
[
|
||||
'type' => 'magicSession',
|
||||
'locale' => 'en',
|
||||
],
|
||||
);
|
||||
$this->assertSame(204, $delete['headers']['status-code']);
|
||||
|
||||
$after = $this->getEmailTemplate('magicSession', 'en');
|
||||
$this->assertFalse($after['body']['custom']);
|
||||
}
|
||||
|
||||
public function testUpdateEmailTemplateLegacyInvalidType(): void
|
||||
{
|
||||
$headers = \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-response-format' => '1.9.1',
|
||||
], $this->getHeaders());
|
||||
|
||||
$update = $this->client->call(
|
||||
Client::METHOD_PATCH,
|
||||
'/project/templates/email',
|
||||
$headers,
|
||||
[
|
||||
'type' => 'notATemplate',
|
||||
'locale' => 'en',
|
||||
'subject' => 'Subject',
|
||||
'message' => 'Message',
|
||||
],
|
||||
);
|
||||
|
||||
$this->assertSame(400, $update['headers']['status-code']);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Helpers
|
||||
// =========================================================================
|
||||
@@ -315,7 +414,7 @@ trait TemplatesBase
|
||||
bool $authenticated = true,
|
||||
): mixed {
|
||||
$params = [
|
||||
'type' => $type,
|
||||
'templateId' => $type,
|
||||
];
|
||||
|
||||
if ($locale !== null) {
|
||||
@@ -352,7 +451,7 @@ trait TemplatesBase
|
||||
protected function deleteEmailTemplate(string $type, ?string $locale = null, bool $authenticated = true): mixed
|
||||
{
|
||||
$params = [
|
||||
'type' => $type,
|
||||
'templateId' => $type,
|
||||
];
|
||||
|
||||
if ($locale !== null) {
|
||||
|
||||
Reference in New Issue
Block a user