fix(email): allow larger email templates
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
class EmailTemplate < ApplicationRecord
|
||||
BRANDED_LAYOUT_NAME = 'base'.freeze
|
||||
DEFAULT_LOCALE = 'en'.freeze
|
||||
MAX_BODY_LENGTH = 256.kilobytes
|
||||
CONTENT_FOR_LAYOUT_PATTERN = /\{\{\s*content_for_layout\s*\}\}/
|
||||
|
||||
enum :locale, LANGUAGES_CONFIG.map { |key, val| [val[:iso_639_1_code], key] }.to_h, prefix: true
|
||||
@@ -34,6 +35,7 @@ class EmailTemplate < ApplicationRecord
|
||||
if: :installation_scoped?
|
||||
validates :name, uniqueness: { scope: %i[account_id template_type locale], conditions: -> { where(inbox_id: nil) } }, if: :account_scoped?
|
||||
validates :name, uniqueness: { scope: %i[inbox_id template_type locale] }, if: :inbox_scoped?
|
||||
validates :body, length: { maximum: MAX_BODY_LENGTH }
|
||||
validate :validate_inbox_account
|
||||
validate :validate_liquid_body
|
||||
validate :validate_layout_slot, if: :layout?
|
||||
|
||||
@@ -85,6 +85,20 @@ RSpec.describe 'Branded Email Layout API', type: :request do
|
||||
expect(response.parsed_body['branded_email_layout']).to eq(layout)
|
||||
end
|
||||
|
||||
it 'updates account-scoped branded email layouts up to 100 KiB' do
|
||||
account.enable_features!(:branded_email_templates)
|
||||
slot = '{{ content_for_layout }}'
|
||||
large_layout = "#{'a' * (100.kilobytes - slot.length)}#{slot}"
|
||||
|
||||
patch "/api/v1/accounts/#{account.id}/branded_email_layout",
|
||||
headers: admin.create_new_auth_token,
|
||||
params: { branded_email_layout: large_layout },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(EmailTemplate.account_branded_layout_template_for(account).body.length).to eq(100.kilobytes)
|
||||
end
|
||||
|
||||
it 'clears account-scoped branded email layout when blank value is passed' do
|
||||
account.enable_features!(:branded_email_templates)
|
||||
create(:email_template, :layout, account: account, body: layout)
|
||||
|
||||
@@ -636,6 +636,22 @@ RSpec.describe 'Inboxes API', type: :request do
|
||||
expect(response.parsed_body['branded_email_layout']).to eq(layout)
|
||||
end
|
||||
|
||||
it 'rejects branded email layouts larger than 256 KiB' do
|
||||
account.enable_features!(:branded_email_templates)
|
||||
email_channel = create(:channel_email, account: account)
|
||||
email_inbox = create(:inbox, channel: email_channel, account: account)
|
||||
slot = '{{ content_for_layout }}'
|
||||
large_layout = "#{'a' * (EmailTemplate::MAX_BODY_LENGTH - slot.length + 1)}#{slot}"
|
||||
|
||||
patch "/api/v1/accounts/#{account.id}/inboxes/#{email_inbox.id}",
|
||||
headers: admin.create_new_auth_token,
|
||||
params: { branded_email_layout: large_layout },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
expect(response.parsed_body['error']).to include('is too long (maximum is 262144 characters)')
|
||||
end
|
||||
|
||||
it 'rolls back branded email layout when inbox update fails' do
|
||||
account.enable_features!(:branded_email_templates)
|
||||
email_channel = create(:channel_email, account: account)
|
||||
|
||||
@@ -68,6 +68,23 @@ RSpec.describe EmailTemplate do
|
||||
expect(template.errors[:body]).to include('must include {{ content_for_layout }}')
|
||||
end
|
||||
|
||||
it 'allows email templates up to 262,144 characters' do
|
||||
slot = '{{ content_for_layout }}'
|
||||
body = "#{'a' * (described_class::MAX_BODY_LENGTH - slot.length)}#{slot}"
|
||||
template = build(:email_template, :layout, account: create(:account), body: body)
|
||||
|
||||
expect(template).to be_valid
|
||||
end
|
||||
|
||||
it 'rejects email templates larger than 262,144 characters' do
|
||||
slot = '{{ content_for_layout }}'
|
||||
body = "#{'a' * (described_class::MAX_BODY_LENGTH - slot.length + 1)}#{slot}"
|
||||
template = build(:email_template, :layout, account: create(:account), body: body)
|
||||
|
||||
expect(template).not_to be_valid
|
||||
expect(template.errors[:body]).to include('is too long (maximum is 262144 characters)')
|
||||
end
|
||||
|
||||
it 'validates liquid syntax' do
|
||||
template = build(:email_template, body: '{{ broken ')
|
||||
|
||||
|
||||
@@ -4,5 +4,6 @@ properties:
|
||||
type:
|
||||
- string
|
||||
- 'null'
|
||||
description: Account-scoped Liquid HTML layout for branded email replies. Blank or null removes the account override.
|
||||
maxLength: 262144
|
||||
description: Account-scoped Liquid HTML layout for branded email replies. Maximum length is 262,144 characters. Blank or null removes the account override.
|
||||
example: '<html><body>{{ content_for_layout }}</body></html>'
|
||||
|
||||
@@ -113,8 +113,9 @@ properties:
|
||||
type:
|
||||
- string
|
||||
- 'null'
|
||||
maxLength: 262144
|
||||
description: |
|
||||
Liquid HTML layout for outbound email replies. Must include `{{ content_for_layout }}`.
|
||||
Liquid HTML layout for outbound email replies. Must include `{{ content_for_layout }}` and be 262,144 characters or shorter.
|
||||
|
||||
Available for: `Email`
|
||||
example: '<html><body>{{ content_for_layout }}</body></html>'
|
||||
|
||||
@@ -12804,7 +12804,8 @@
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"description": "Liquid HTML layout for outbound email replies. Must include `{{ content_for_layout }}`.\n\nAvailable for: `Email`\n",
|
||||
"maxLength": 262144,
|
||||
"description": "Liquid HTML layout for outbound email replies. Must include `{{ content_for_layout }}` and be 262,144 characters or shorter.\n\nAvailable for: `Email`\n",
|
||||
"example": "<html><body>{{ content_for_layout }}</body></html>"
|
||||
},
|
||||
"channel": {
|
||||
@@ -12842,7 +12843,8 @@
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"description": "Account-scoped Liquid HTML layout for branded email replies. Blank or null removes the account override.",
|
||||
"maxLength": 262144,
|
||||
"description": "Account-scoped Liquid HTML layout for branded email replies. Maximum length is 262,144 characters. Blank or null removes the account override.",
|
||||
"example": "<html><body>{{ content_for_layout }}</body></html>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11311,7 +11311,8 @@
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"description": "Liquid HTML layout for outbound email replies. Must include `{{ content_for_layout }}`.\n\nAvailable for: `Email`\n",
|
||||
"maxLength": 262144,
|
||||
"description": "Liquid HTML layout for outbound email replies. Must include `{{ content_for_layout }}` and be 262,144 characters or shorter.\n\nAvailable for: `Email`\n",
|
||||
"example": "<html><body>{{ content_for_layout }}</body></html>"
|
||||
},
|
||||
"channel": {
|
||||
@@ -11349,7 +11350,8 @@
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"description": "Account-scoped Liquid HTML layout for branded email replies. Blank or null removes the account override.",
|
||||
"maxLength": 262144,
|
||||
"description": "Account-scoped Liquid HTML layout for branded email replies. Maximum length is 262,144 characters. Blank or null removes the account override.",
|
||||
"example": "<html><body>{{ content_for_layout }}</body></html>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4119,7 +4119,8 @@
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"description": "Liquid HTML layout for outbound email replies. Must include `{{ content_for_layout }}`.\n\nAvailable for: `Email`\n",
|
||||
"maxLength": 262144,
|
||||
"description": "Liquid HTML layout for outbound email replies. Must include `{{ content_for_layout }}` and be 262,144 characters or shorter.\n\nAvailable for: `Email`\n",
|
||||
"example": "<html><body>{{ content_for_layout }}</body></html>"
|
||||
},
|
||||
"channel": {
|
||||
@@ -4157,7 +4158,8 @@
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"description": "Account-scoped Liquid HTML layout for branded email replies. Blank or null removes the account override.",
|
||||
"maxLength": 262144,
|
||||
"description": "Account-scoped Liquid HTML layout for branded email replies. Maximum length is 262,144 characters. Blank or null removes the account override.",
|
||||
"example": "<html><body>{{ content_for_layout }}</body></html>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3534,7 +3534,8 @@
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"description": "Liquid HTML layout for outbound email replies. Must include `{{ content_for_layout }}`.\n\nAvailable for: `Email`\n",
|
||||
"maxLength": 262144,
|
||||
"description": "Liquid HTML layout for outbound email replies. Must include `{{ content_for_layout }}` and be 262,144 characters or shorter.\n\nAvailable for: `Email`\n",
|
||||
"example": "<html><body>{{ content_for_layout }}</body></html>"
|
||||
},
|
||||
"channel": {
|
||||
@@ -3572,7 +3573,8 @@
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"description": "Account-scoped Liquid HTML layout for branded email replies. Blank or null removes the account override.",
|
||||
"maxLength": 262144,
|
||||
"description": "Account-scoped Liquid HTML layout for branded email replies. Maximum length is 262,144 characters. Blank or null removes the account override.",
|
||||
"example": "<html><body>{{ content_for_layout }}</body></html>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4295,7 +4295,8 @@
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"description": "Liquid HTML layout for outbound email replies. Must include `{{ content_for_layout }}`.\n\nAvailable for: `Email`\n",
|
||||
"maxLength": 262144,
|
||||
"description": "Liquid HTML layout for outbound email replies. Must include `{{ content_for_layout }}` and be 262,144 characters or shorter.\n\nAvailable for: `Email`\n",
|
||||
"example": "<html><body>{{ content_for_layout }}</body></html>"
|
||||
},
|
||||
"channel": {
|
||||
@@ -4333,7 +4334,8 @@
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"description": "Account-scoped Liquid HTML layout for branded email replies. Blank or null removes the account override.",
|
||||
"maxLength": 262144,
|
||||
"description": "Account-scoped Liquid HTML layout for branded email replies. Maximum length is 262,144 characters. Blank or null removes the account override.",
|
||||
"example": "<html><body>{{ content_for_layout }}</body></html>"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user