Module: Webhookdb::Message
- Extended by:
- MethodUtilities
- Includes:
- Appydays::Configurable
- Defined in:
- lib/webhookdb/message.rb,
lib/webhookdb/message/liquid_drops.rb
Defined Under Namespace
Classes: Body, CustomerDrop, Delivery, EmailTransport, EnvironmentDrop, FakeTransport, InvalidTransportError, MissingTemplateError, Recipient, Rendering, Template, Transport
Constant Summary collapse
Class Method Summary collapse
-
.dispatch(template, to, transport_type) ⇒ Object
Create a Webhookdb::Message::Delivery ready to deliver (rendered, all bodies set up) using the given transport_type to the given user.
-
.render(template, transport_type, recipient) ⇒ Object
Render the transport-specific version of the given template and return a the rendering (content and exposed variables).
- .send_unsent ⇒ Object
-
.unify_drops_encoding(drops) ⇒ Hash
Handle encoding in liquid drop string values that would likely crash message rendering.
Methods included from MethodUtilities
attr_predicate, attr_predicate_accessor, singleton_attr_accessor, singleton_attr_reader, singleton_attr_writer, singleton_method_alias, singleton_predicate_accessor, singleton_predicate_reader
Class Method Details
.dispatch(template, to, transport_type) ⇒ Object
Create a Webhookdb::Message::Delivery ready to deliver (rendered, all bodies set up) using the given transport_type to the given user.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/webhookdb/message.rb', line 39 def self.dispatch(template, to, transport_type) (transport = Webhookdb::Message::Transport.for(transport_type)) or raise InvalidTransportError, "Invalid transport #{transport_type}" recipient = transport.recipient(to) contents = self.render(template, transport_type, recipient) Webhookdb::Message::Delivery.db.transaction do delivery = Webhookdb::Message::Delivery.create( template: template.full_template_name, transport_type: transport.type, transport_service: transport.service, to: recipient.to, recipient: recipient.customer, extra_fields: template.extra_fields, ) transport.add_bodies(delivery, contents) delivery.publish_deferred("dispatched", delivery.id) return delivery end end |
.render(template, transport_type, recipient) ⇒ Object
Render the transport-specific version of the given template and return a the rendering (content and exposed variables).
Templates can expose data to the caller by using the ‘expose’ tag, like expose subject %Hello from Webhookdb{% endexpose %}. This is available as [:subject] on the returned rendering.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/webhookdb/message.rb', line 67 def self.render(template, transport_type, recipient) template_file = template.template_path(transport_type) raise MissingTemplateError, "#{template_file} does not exist" unless template_file.exist? drops = template.liquid_drops.stringify_keys.merge( "recipient" => Webhookdb::Message::CustomerDrop.new(recipient), "environment" => Webhookdb::Message::EnvironmentDrop.new, "app_url" => Webhookdb.app_url, ) drops = self.unify_drops_encoding(drops) content_tmpl = Liquid::Template.parse(template_file.read) # The 'expose' drop smashes data into the register. # We need to keep track of the register to get the subject back out, # so we need to make our own context. lctx = Liquid::Context.new( [drops, content_tmpl.assigns], content_tmpl.instance_assigns, content_tmpl.registers, true, content_tmpl.resource_limits, ) content = content_tmpl.render!(lctx, strict_variables: true) transport = Webhookdb::Message::Transport.for(transport_type) if transport.supports_layout? layout_file = template.layout_path(transport_type) if layout_file raise MissingTemplateError, "#{template_file} does not exist" unless layout_file.exist? layout_tmpl = Liquid::Template.parse(layout_file.read) drops["content"] = content.dup content = layout_tmpl.render!(drops, strict_variables: true, registers: content_tmpl.registers) end end return Rendering.new(content, lctx.registers) end |
.send_unsent ⇒ Object
155 156 157 |
# File 'lib/webhookdb/message.rb', line 155 def self.send_unsent Webhookdb::Message::Delivery.unsent.each(&:send!) end |
.unify_drops_encoding(drops) ⇒ Hash
Handle encoding in liquid drop string values that would likely crash message rendering.
If there is a mixed character encoding of string values in a liquid drop, such as when handling user-supplied values, force all strings into UTF-8.
This is needed because the way Ruby does encoding coercion when parsing input which does not declare an encoding, such as a file or especially an HTTP response. Ruby will:
-
Use ASCII if the values fit into 7 bits
-
Use ASCII-8BIT if the values fit into 8 bits (128 to 255)
-
Otherwise, use UTF-8.
The actual rules are more complex, but this is common enough.
While ASCII encoding can be used as UTF-8, ASCII-8BIT cannot. So adding ‘(ascii-8bit string) + (utf-8 string)` will error with an `Encoding::CompatibilityError`.
Instead, if we see a series of liquid drop string values with different encodings, force them all to be UTF-8. This can result in some unexpected behavior, but it should be fine, since you’d only see it with unexpected input (all valid inputs should be UTF-8).
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/webhookdb/message.rb', line 131 def self.unify_drops_encoding(drops) return drops if drops.empty? seen_enc = nil force_enc = false drops.each_value do |v| next unless v.respond_to?(:encoding) seen_enc ||= v.encoding next if seen_enc == v.encoding force_enc = true break end return drops unless force_enc utf8 = Encoding.find("UTF-8") result = drops.each_with_object({}) do |(k, v), memo| if v.respond_to?(:encoding) && v.encoding != utf8 v2 = v.encode(utf8, invalid: :replace, undef: :replace, replace: "?") memo[k] = v2 else memo[k] = v end end return result end |