feat: add the support for legacy wa templates

This commit is contained in:
Muhsin Keloth
2025-08-05 15:40:59 +05:30
parent 7d8c3ca65e
commit 0495316fd1
3 changed files with 321 additions and 3 deletions
@@ -0,0 +1,115 @@
# Service to convert legacy WhatsApp template parameter formats to enhanced format
#
# Legacy formats (deprecated):
# - Array: ["John", "Order123"] - positional parameters
# - Flat Hash: {"1": "John", "2": "Order123"} - direct key-value mapping
#
# Enhanced format:
# - Component-based: {"body": {"1": "John", "2": "Order123"}} - structured by template components
# - Supports header, body, footer, and button parameters separately
#
class Whatsapp::TemplateConverterService
def initialize(template_params, template)
@template_params = template_params
@template = template
end
def normalize_to_enhanced
processed_params = @template_params['processed_params']
# Early return if already enhanced format
return @template_params if enhanced_format?(processed_params)
# Convert legacy formats to enhanced structure
# TODO: Legacy format support will be deprecated and removed after 2-3 releases
enhanced_params = convert_legacy_to_enhanced(processed_params, @template)
# Replace original params with enhanced structure
@template_params['processed_params'] = enhanced_params
@template_params['format_version'] = 'enhanced'
@template_params
end
private
def enhanced_format?(processed_params)
return false unless processed_params.is_a?(Hash)
# Enhanced format has component-based structure
component_keys = %w[body header footer buttons]
has_component_structure = processed_params.keys.any? { |k| component_keys.include?(k) }
# Additional validation for enhanced format
if has_component_structure
validate_enhanced_structure(processed_params)
else
false
end
end
def validate_enhanced_structure(params)
valid_body?(params['body']) &&
valid_header?(params['header']) &&
valid_buttons?(params['buttons'])
end
def valid_body?(body)
body.nil? || body.is_a?(Hash)
end
def valid_header?(header)
header.nil? || header.is_a?(Hash)
end
def valid_buttons?(buttons)
return true if buttons.nil?
return false unless buttons.is_a?(Array)
buttons.all? { |b| b.is_a?(Hash) && b['type'] }
end
def convert_legacy_to_enhanced(legacy_params, _template)
# Legacy system only supported text-based templates with body parameters
# We only convert the parameter format, not add new features
enhanced = {}
case legacy_params
when Array
# Array format: ["John", "Order123"] → {body: {"1": "John", "2": "Order123"}}
body_params = convert_array_to_body_params(legacy_params)
enhanced['body'] = body_params unless body_params.empty?
when Hash
# Hash format: {"1": "John", "name": "Jane"} → {body: {"1": "John", "name": "Jane"}}
body_params = convert_hash_to_body_params(legacy_params)
enhanced['body'] = body_params unless body_params.empty?
else
raise ArgumentError, "Unknown legacy format: #{legacy_params.class}"
end
enhanced
end
def convert_array_to_body_params(params_array)
return {} if params_array.empty?
body_params = {}
params_array.each_with_index do |value, index|
body_params[(index + 1).to_s] = value.to_s
end
body_params
end
def convert_hash_to_body_params(params_hash)
return {} if params_hash.empty?
body_params = {}
params_hash.each do |key, value|
body_params[key.to_s] = value.to_s
end
body_params
end
end
@@ -28,11 +28,15 @@ class Whatsapp::TemplateProcessorService
template = find_template
return if template.blank?
process_enhanced_template_params(template)
# Convert legacy format to enhanced format before processing
converter = Whatsapp::TemplateConverterService.new(template_params, template)
normalized_params = converter.normalize_to_enhanced
process_enhanced_template_params(template, normalized_params['processed_params'])
end
def process_enhanced_template_params(template)
processed_params = template_params['processed_params']
def process_enhanced_template_params(template, processed_params = nil)
processed_params ||= template_params['processed_params']
components = []
components.concat(process_header_components(processed_params))
@@ -0,0 +1,199 @@
require 'rails_helper'
describe Whatsapp::TemplateConverterService do
let(:template) do
{
'name' => 'test_template',
'language' => 'en',
'components' => [
{
'type' => 'BODY',
'text' => 'Hello {{1}}, your order {{2}} is ready!'
}
]
}
end
let(:media_template) do
{
'name' => 'media_template',
'language' => 'en',
'components' => [
{
'type' => 'HEADER',
'format' => 'IMAGE'
},
{
'type' => 'BODY',
'text' => 'Check out {{1}}!'
}
]
}
end
let(:button_template) do
{
'name' => 'button_template',
'language' => 'en',
'components' => [
{
'type' => 'BODY',
'text' => 'Visit our website!'
},
{
'type' => 'BUTTONS',
'buttons' => [
{
'type' => 'URL',
'url' => 'https://example.com/{{1}}'
},
{
'type' => 'COPY_CODE'
}
]
}
]
}
end
describe '#normalize_to_enhanced' do
context 'when already enhanced format' do
let(:enhanced_params) do
{
'processed_params' => {
'body' => { '1' => 'John', '2' => 'Order123' }
}
}
end
it 'returns unchanged' do
converter = described_class.new(enhanced_params, template)
result = converter.normalize_to_enhanced
expect(result).to eq(enhanced_params)
end
end
context 'when legacy array format' do
let(:legacy_array_params) do
{
'processed_params' => %w[John Order123]
}
end
it 'converts to enhanced format' do
converter = described_class.new(legacy_array_params, template)
result = converter.normalize_to_enhanced
expect(result['processed_params']).to eq({
'body' => { '1' => 'John', '2' => 'Order123' }
})
expect(result['format_version']).to eq('enhanced')
end
end
context 'when legacy flat hash format' do
let(:legacy_hash_params) do
{
'processed_params' => { '1' => 'John', '2' => 'Order123' }
}
end
it 'converts to enhanced format' do
converter = described_class.new(legacy_hash_params, template)
result = converter.normalize_to_enhanced
expect(result['processed_params']).to eq({
'body' => { '1' => 'John', '2' => 'Order123' }
})
expect(result['format_version']).to eq('enhanced')
end
end
context 'when legacy hash with all body parameters' do
let(:legacy_hash_params) do
{
'processed_params' => {
'1' => 'Product',
'customer_name' => 'John'
}
}
end
it 'converts to enhanced format with body only' do
converter = described_class.new(legacy_hash_params, media_template)
result = converter.normalize_to_enhanced
expect(result['processed_params']).to eq({
'body' => {
'1' => 'Product',
'customer_name' => 'John'
}
})
expect(result['format_version']).to eq('enhanced')
end
end
context 'when invalid format' do
let(:invalid_params) do
{
'processed_params' => 'invalid_string'
}
end
it 'raises ArgumentError' do
expect do
converter = described_class.new(invalid_params, template)
converter.normalize_to_enhanced
end.to raise_error(ArgumentError, /Unknown legacy format/)
end
end
end
describe '#enhanced_format?' do
it 'returns true for valid enhanced format' do
enhanced = { 'body' => { '1' => 'test' } }
converter = described_class.new({}, template)
expect(converter.send(:enhanced_format?, enhanced)).to be true
end
it 'returns false for array' do
converter = described_class.new({}, template)
expect(converter.send(:enhanced_format?, ['test'])).to be false
end
it 'returns false for flat hash' do
converter = described_class.new({}, template)
expect(converter.send(:enhanced_format?, { '1' => 'test' })).to be false
end
it 'returns false for invalid structure' do
invalid = { 'body' => 'not_a_hash' }
converter = described_class.new({}, template)
expect(converter.send(:enhanced_format?, invalid)).to be false
end
end
describe 'simplified conversion methods' do
describe '#convert_array_to_body_params' do
it 'converts empty array' do
converter = described_class.new({}, template)
result = converter.send(:convert_array_to_body_params, [])
expect(result).to eq({})
end
it 'converts array to numbered body parameters' do
converter = described_class.new({}, template)
result = converter.send(:convert_array_to_body_params, %w[John Order123])
expect(result).to eq({ '1' => 'John', '2' => 'Order123' })
end
end
describe '#convert_hash_to_body_params' do
it 'converts hash to body parameters' do
converter = described_class.new({}, template)
result = converter.send(:convert_hash_to_body_params, { 'name' => 'John', 'order' => '123' })
expect(result).to eq({ 'name' => 'John', 'order' => '123' })
end
end
end
end