talktome 0.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b12474ba945a8c289bc358c4e654667ac7684618b3c19f3babd74dcd6c4dd2dd
4
- data.tar.gz: e3a4ebaaa0796bae48cc52e978c3aee64aade68cd80c654616171f9bf49da783
3
+ metadata.gz: 8ca8cf300ad4144fd6336f1ff56e4fc97140742869f10397ce62c9d0fecd3c03
4
+ data.tar.gz: 9d439309e79ebb3da0ea177af0606e882a643adc55e045cca5a56bb154ac6888
5
5
  SHA512:
6
- metadata.gz: a64d48bc5544b99ae7868a10d2435b390526ccf9a95fc0ca323952264cb933749d20aea08365b3d9f995143df7514f7624e40dc69b2398791750149db2abc21e
7
- data.tar.gz: 9253bdbe2c08c1b75a6eeb9bb4a5e0dbfb6402365d9e925fe64282e872e034bd71da900eb235fe8e674ecd6e586b3a73a81a832775267c21b10f4f5ad1365a95
6
+ metadata.gz: d4bd93a6e3bde5390185ebd55844a0db7b48fd81ca0c0982289d67ca15c5866f402b645191f6ba1826a7c4b5147b67937e0d7c07cd9f08788a616b2f019751c4
7
+ data.tar.gz: 9b255ab12108b8e37c024aff7bbe49b8ce85c543cc14fe62b797d64aadcbd59c2a9fe33530bdfa3e35872a7a3a553673ff064472e4fbf4cf4563377c2de4ad14
data/LICENSE.md CHANGED
@@ -1,22 +1,20 @@
1
- # The MIT Licence
1
+ Copyright (c) 2017-2021 - Enspirit SPRL (Bernard Lambeau)
2
2
 
3
- Copyright (c) 2017 - Enspirit SPRL (Bernard Lambeau)
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
4
10
 
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
12
13
 
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,3 +1,141 @@
1
1
  # Talktome - Talk to users easily
2
2
 
3
- ...
3
+ [![Build Status](https://travis-ci.com/enspirit/talktome.svg?branch=master)](https://travis-ci.com/enspirit/talktome)
4
+
5
+ Talktome helps talking to users (by email for now, but later we aim to add support
6
+ for various notification systems) easily. As a ruby gem to be used programmatically,
7
+ or as a docker container exposing web services (different use cases, see below).
8
+
9
+ ## Using Talktome programmatically
10
+
11
+ Using Talktome programmatically is useful to send transactional emails to users.
12
+
13
+ ```
14
+ require 'talktome'
15
+ CLIENT = Talktome::Client::Local.new(path_to_templates)
16
+
17
+ # later on
18
+ CLIENT.talktome(template_name, user, template_info, strategies)
19
+
20
+ # typically, the will send an email to foo@bar.com instantiating
21
+ # the email found in path_to_templates/hello/email.md and
22
+ # instantiated using mustache and markdown
23
+ CLIENT.talktome("hello", {email: 'foo@bar.com'}, {}, [:email])
24
+ ```
25
+
26
+ ## Using Talktome using the docker image
27
+
28
+ The docker image aims at supporting another category of use cases, such as providing
29
+ a reusable backend for contact forms.
30
+
31
+ ```
32
+ docker run \
33
+ -p4567:4567 \
34
+ -e TALKTOME_EMAIL_DEFAULT_FROM=info@mydomain.com
35
+ -e TALKTOME_EMAIL_DEFAULT_TO=support@mydomain.com
36
+ enspirit/talktome
37
+ ```
38
+
39
+ Send an contact-us email through the web api using curl, as follows:
40
+
41
+ ```
42
+ curl -XPOST \
43
+ -H'Content-Type: application/json' \
44
+ -d'{"reply_to": "someone@foo.bar", "message": "Hello"}' \
45
+ http://127.0.0.1:4567/contact-us/
46
+ ```
47
+
48
+ This web API does not allow specifying `from` and `to` as input data to avoid
49
+ exposing a way to send SPAM easily.
50
+
51
+ ### Overriding templates (and having more than one endpoint)
52
+
53
+ The default image comes with a single contact-us email template used by Enspirit.
54
+ Feel free to override it by providing one or more email templates.
55
+
56
+ You can mount a volume with email templates into `/app/templates/`, which will
57
+ be used for the available endpoints. For instance, the following `templates/`
58
+ folder will expose two endpoints with possibly different behaviors (according
59
+ to the templates themselves):
60
+
61
+ ```
62
+ templates/
63
+ contact-us/
64
+ email.md
65
+ report-issue/
66
+ email.md
67
+ ```
68
+
69
+ Two usual ways to do so in docker: commandline or Dockerfile. On commandline,
70
+ use the following option:
71
+
72
+ ```
73
+ -v ${PWD}/my-templates:/app/templates
74
+ ```
75
+
76
+ In a Dockerfile, add your templates:
77
+
78
+ ```
79
+ FROM enspirit/talktome
80
+
81
+ COPY ./templates /app/templates
82
+ ```
83
+
84
+ ## Configuring Talktome
85
+
86
+ The easiest way to configure Talktome is through environment variables. The following
87
+ ones are supported:
88
+
89
+ ```
90
+ TALKTOME_DEBUG when set enables the dumping of sent messages to ./tmp folder
91
+
92
+ TALKTOME_EMAIL_DELIVERY smtp, file or test (see ruby Mail library)
93
+ TALKTOME_EMAIL_DEFAULT_FROM default From: to use for email sending
94
+ TALKTOME_EMAIL_DEFAULT_REPLYTO default Reply-To: to use for email sending
95
+ TALKTOME_EMAIL_DEFAULT_TO default To: to use for email sending
96
+
97
+ TALKTOME_EMAIL_SUBJECT Set the subject of the default "contact us" email
98
+ TALKTOME_EMAIL_FOOTER Set the footer of the default "contact us" email
99
+
100
+ TALKTOME_LAYOUTS_FOLDER Set the folder to use for messaging layouts
101
+
102
+ TALKTOME_SMTP_ADDRESS host address for smtp sending
103
+ TALKTOME_SMTP_PORT port of smtp server to use
104
+ TALKTOME_SMTP_DOMAIN sending domain
105
+ TALKTOME_SMTP_USER user for smtp authentication
106
+ TALKTOME_SMTP_PASSWORD password for smtp authentication
107
+ TALKTOME_SMTP_STARTTLS_AUTO true or false (see ruby Mail library)
108
+
109
+ TALKTOME_BEARER_SECRET secret for the webapi, to let send emails to anyone
110
+ ```
111
+
112
+ ## Hacking Talktome
113
+
114
+ In pure Ruby:
115
+
116
+ ```
117
+ bundle install
118
+ bundle exec rake test
119
+ ```
120
+
121
+ Or using docker, please then use the `make` targets initially cooked for Jenkins:
122
+
123
+ ```
124
+ make image
125
+ make test
126
+ ```
127
+
128
+ ## Contributing
129
+
130
+ Please use github issues for questions and bugs, and pull requests for
131
+ submitting improvement proposals and new features.
132
+
133
+ ## Contributors
134
+
135
+ Enspirit (https://enspirit.be) and Klaro App (https://klaro.cards) are
136
+ both actively using, contributing and funding work on this library.
137
+ Please contact Bernard Lambeau for any question.
138
+
139
+ ## Licence
140
+
141
+ Webspicy is distributed under a MIT Licence, by Enspirit SRL.
data/lib/talktome.rb CHANGED
@@ -4,25 +4,39 @@ require 'mustache'
4
4
  require 'redcarpet'
5
5
  module Talktome
6
6
 
7
+ # Root folder of the project structure
8
+ ROOT_FOLDER = Path.backfind('.[Gemfile]') or raise("Missing Gemfile")
9
+
10
+ def env(which, default = nil)
11
+ if ENV.has_key?(which)
12
+ got = ENV[which].to_s.strip
13
+ return got unless got.empty?
14
+ end
15
+ default
16
+ end
17
+ module_function :env
18
+
19
+ def with_env(which, &bl)
20
+ env(which).tap{|x|
21
+ bl.call(x) unless x.nil?
22
+ }
23
+ end
24
+ module_function :with_env
25
+
26
+ def set_env(which, value, &bl)
27
+ old, ENV[which] = ENV[which], value
28
+ bl.call.tap{
29
+ ENV[which] = old
30
+ }
31
+ end
32
+ module_function :set_env
33
+
7
34
  def redcarpet
8
35
  @redcarpet ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML, extensions = {})
9
36
  end
10
37
  module_function :redcarpet
11
38
 
12
- #
13
39
  # Infer all client and strategy options from environment variables.
14
- # The following ones are recognized:
15
- #
16
- # - TALKTOME_DEBUG: when set (to anything) enables the dumping of sent
17
- # messages to the debug folder
18
- # - TALKTOME_EMAIL_DELIVERY: "smtp", "file" or "test"
19
- # - TALKTOME_EMAIL_DEFAULT_FROM: default from address to use for email sending
20
- # - TALKTOME_SMTP_ADDRESS: host address for smtp sending
21
- # - TALKTOME_SMTP_PORT: port of smtp server to use
22
- # - TALKTOME_SMTP_DOMAIN: sending domain
23
- # - TALKTOME_SMTP_USER: user for smtp authentication
24
- # - TALKTOME_SMTP_PASSWORD: user for smtp authentication
25
- #
26
40
  def auto_options(folder)
27
41
  options = {}
28
42
  debug_folder = folder/"tmp"
@@ -40,10 +54,11 @@ module Talktome
40
54
 
41
55
  email_config.merge!({
42
56
  address: ENV['TALKTOME_SMTP_ADDRESS'],
43
- port: ENV['TALKTOME_SMTP_PORT'],
57
+ port: ENV['TALKTOME_SMTP_PORT'].to_i,
44
58
  domain: ENV['TALKTOME_SMTP_DOMAIN'],
45
59
  user_name: ENV['TALKTOME_SMTP_USER'],
46
- password: ENV['TALKTOME_SMTP_PASSWORD']
60
+ password: ENV['TALKTOME_SMTP_PASSWORD'],
61
+ enable_starttls_auto: (ENV['TALKTOME_SMTP_STARTTLS_AUTO'] != 'false')
47
62
  }) if email_delivery == :smtp
48
63
 
49
64
  email_config.merge!({
@@ -52,9 +67,21 @@ module Talktome
52
67
 
53
68
  options[:strategies][:email] = ::Talktome::Strategy::Email.new{|email|
54
69
  email.delivery_method(email_delivery, email_config)
55
- email.from(ENV['TALKTOME_EMAIL_DEFAULT_FROM']) if ENV['TALKTOME_EMAIL_DEFAULT_FROM']
70
+ with_env('TALKTOME_EMAIL_DEFAULT_FROM'){|default|
71
+ email.from(default)
72
+ }
73
+ with_env('TALKTOME_EMAIL_DEFAULT_TO'){|default|
74
+ email.to(default)
75
+ }
76
+ with_env('TALKTOME_EMAIL_DEFAULT_REPLYTO'){|default|
77
+ email.reply_to(default)
78
+ }
56
79
  }
57
80
 
81
+ if layouts_folder = ENV['TALKTOME_LAYOUTS_FOLDER']
82
+ options[:layouts] = Path(layouts_folder)
83
+ end
84
+
58
85
  options
59
86
  end
60
87
  module_function :auto_options
@@ -0,0 +1,77 @@
1
+ require 'sinatra'
2
+ require 'finitio'
3
+ require 'rack/robustness'
4
+ module Talktome
5
+ class App < Sinatra::Application
6
+
7
+ use Rack::Robustness do |g|
8
+ g.catch_all
9
+ g.status 500
10
+ g.content_type 'text/plain'
11
+ g.body{ "An error occured." }
12
+ end
13
+
14
+ set :raise_errors, true
15
+ set :show_exceptions, false
16
+ set :talktome, Talktome::Client::Local.new(ROOT_FOLDER/'templates')
17
+
18
+ VALIDATION_SCHEMA = ::Finitio.system(<<~FIO)
19
+ @import finitio/data
20
+ Email = String(s | s =~ /^[^@]+@[^@]+$/ )
21
+ {
22
+ to :? Email
23
+ reply_to :? Email
24
+ ... : .Object
25
+ }
26
+ FIO
27
+
28
+ post %r{/([a-z-]+([\/][a-z-]+)*)/} do |action, _|
29
+ begin
30
+ as_array = info.map{|k,v| {'key' => k.capitalize, 'value' => v}}
31
+ subject = Talktome.env('TALKTOME_EMAIL_SUBJECT', 'Someone wants to reach you!')
32
+ footer = Talktome.env('TALKTOME_EMAIL_FOOTER', "Truly yours,\n
33
+ Sent by [Enspirit.be](https://enspirit.be/), contact us if you need help with any IT task.")
34
+ user = load_user_from_info!
35
+ settings.talktome.talktome(action, user, info.merge(allvars: as_array, subject: subject, footer: footer), [:email]){|email|
36
+ email.reply_to = info[:reply_to] if info.has_key?(:reply_to)
37
+ }
38
+ [ 200, { "Content-Type" => "text/plain"}, ["Ok"] ]
39
+ rescue JSON::ParserError
40
+ fail!("Invalid data")
41
+ rescue Finitio::Error => ex
42
+ fail!(ex.message)
43
+ rescue ::Talktome::InvalidEmailError => ex
44
+ fail!("Invalid email address")
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def info
51
+ @info ||= VALIDATION_SCHEMA.dress(JSON.parse(request.body.read)).tap{|info|
52
+ not_a_robot!(info)
53
+ }
54
+ end
55
+
56
+ def load_user_from_info!
57
+ if to = info[:to]
58
+ secret = Talktome.env('TALKTOME_BEARER_SECRET')
59
+ fail!("Missing secret", 400) unless secret
60
+ fail!("Invalid secret", 401) unless "Bearer #{secret}" == env["HTTP_AUTHORIZATION"]
61
+ { email: info[:to] }
62
+ else
63
+ {}
64
+ end
65
+ end
66
+
67
+ def fail!(message, status = 400)
68
+ halt([ status, { "Content-Type" => "text/plain"}, [message] ])
69
+ end
70
+
71
+ def not_a_robot!(info)
72
+ # `reply_to_confirm` is a honeypot field, if it's filled it means it's a bot and an error is thrown
73
+ raise ::Talktome::InvalidEmailError if info[:reply_to_confirm] && info[:reply_to_confirm] =~ /^[^@]+@[^@]+$/
74
+ end
75
+
76
+ end # class App
77
+ end # module Talktome
@@ -33,7 +33,7 @@ module Talktome
33
33
  end
34
34
 
35
35
  def templater(strategy)
36
- return nil unless tpl_folder = options[:templates]
36
+ return nil unless tpl_folder = options[:layouts] || options[:templates]
37
37
  ->(message, src, ctype) {
38
38
  if (file = tpl_folder/"#{strategy}.#{ctype}").file?
39
39
  data = { metadata: message.metadata, yield: src }
@@ -1,4 +1,5 @@
1
1
  module Talktome
2
2
  class Error < StandardError; end
3
3
  class InvalidMessageError < Error; end
4
+ class InvalidEmailError < Error; end
4
5
  end
@@ -9,10 +9,23 @@ module Talktome
9
9
  end
10
10
 
11
11
  def send_message(message, user)
12
+ # Take a base email, with all info coming from the environment (if set)
12
13
  mail = base_email
13
- mail.to = users(user).map{|u| u[:email] }
14
- mail.reply_to = message.metadata["reply_to"] if message.metadata.has_key?("reply_to")
15
- mail.subject = message.metadata["subject"]
14
+
15
+ # Override environment defaults with template behavior, for flexibility
16
+ [
17
+ :to,
18
+ :reply_to,
19
+ :subject
20
+ ].each do |which|
21
+ if arg = message.metadata[which.to_s]
22
+ mail.send(:"#{which}=", arg)
23
+ end
24
+ end
25
+
26
+ # If the user is actually known from source code behavior, override the
27
+ # `mail.to` to send the email to that particular person.
28
+ mail.to = user[:email] if user.has_key?(:email)
16
29
 
17
30
  case message.extension
18
31
  when 'md', 'html', 'htm'
@@ -37,10 +50,6 @@ module Talktome
37
50
 
38
51
  private
39
52
 
40
- def users(user)
41
- user.is_a?(Array) ? user : [user]
42
- end
43
-
44
53
  def base_email
45
54
  default_email = Mail.new
46
55
  @defaulter.call(default_email) if @defaulter
@@ -1,8 +1,8 @@
1
1
  module Talktome
2
2
  module Version
3
- MAJOR = 0
4
- MINOR = 2
5
- TINY = 1
3
+ MAJOR = 1
4
+ MINOR = 3
5
+ TINY = 0
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
8
8
  end
@@ -0,0 +1,186 @@
1
+ require 'spec_helper'
2
+
3
+ module Talktome
4
+ describe App do
5
+ include Rack::Test::Methods
6
+
7
+ let(:app) {
8
+ Talktome::App.new
9
+ }
10
+
11
+ before(:each) do
12
+ Talktome::App.set :talktome, Talktome::Client::Local.new(Path.dir.parent/'fixtures')
13
+ ENV['TALKTOME_EMAIL_DEFAULT_TO'] = "to@talktome.com"
14
+ ENV['TALKTOME_EMAIL_DEFAULT_FROM'] = "from@talktome.com"
15
+ Mail::TestMailer.deliveries.clear
16
+ end
17
+
18
+ context 'POST /contact-us/, the basic contract' do
19
+
20
+ it 'works' do
21
+ post "/contact-us/", {
22
+ reply_to: 'hello@visitor.com',
23
+ message: 'Hello from visitor',
24
+ key: 'value',
25
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
26
+ expect(last_response).to be_ok
27
+ expect(Mail::TestMailer.deliveries.length).to eql(1)
28
+ expect(Mail::TestMailer.deliveries.first.to).to eql(["to@talktome.com"])
29
+ expect(Mail::TestMailer.deliveries.first.from).to eql(["from@talktome.com"])
30
+ expect(Mail::TestMailer.deliveries.first.subject).to eql("Someone wants to reach you!")
31
+ expect(Mail::TestMailer.deliveries.first.html_part.body).to include("<li>Key: value</li>")
32
+ expect(Mail::TestMailer.deliveries.first.html_part.body).to include("Truly yours")
33
+ end
34
+
35
+ it 'allows to use environment variable to tune the subject and the footer' do
36
+ Talktome.set_env('TALKTOME_EMAIL_SUBJECT', "Subject from environment") do
37
+ Talktome.set_env('TALKTOME_EMAIL_FOOTER', "Footer from environment") do
38
+ post "/contact-us/", {
39
+ reply_to: 'info@domain.com',
40
+ message: 'This is the message.'
41
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
42
+ expect(last_response).to be_ok
43
+ expect(Mail::TestMailer.deliveries.length).to eql(1)
44
+ expect(Mail::TestMailer.deliveries.first.subject).to eql("Subject from environment")
45
+ expect(Mail::TestMailer.deliveries.first.html_part.body).to include("Footer from environment")
46
+ end
47
+ end
48
+ end
49
+
50
+ it 'allows to use a token authentification to bypass default security measures, for e.g. passing the :to' do
51
+ Talktome.set_env('TALKTOME_BEARER_SECRET', "Some secret") do
52
+ header 'Authorization', 'Bearer Some secret'
53
+ post "/contact-us/", {
54
+ to: 'hello@visitor.com',
55
+ reply_to: 'hello@visitor.com',
56
+ message: 'Hello from visitor',
57
+ key: 'value',
58
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
59
+ expect(last_response).to be_ok
60
+ expect(Mail::TestMailer.deliveries.length).to eql(1)
61
+ expect(Mail::TestMailer.deliveries.first.to).to eql(["hello@visitor.com"])
62
+ expect(Mail::TestMailer.deliveries.first.from).to eql(["from@talktome.com"])
63
+ end
64
+ end
65
+
66
+ it 'detects invalid emails' do
67
+ post "/contact-us/", {
68
+ reply_to: 'helloatvisitor.com',
69
+ message: 'Hello from visitor'
70
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
71
+ expect(last_response.status).to eql(400)
72
+ expect(Mail::TestMailer.deliveries.length).to eql(0)
73
+ end
74
+
75
+ it "detects invalid bodies" do
76
+ post "/contact-us/", body: "foobar"
77
+ expect(last_response.status).to eql(400)
78
+ expect(Mail::TestMailer.deliveries.length).to eql(0)
79
+ end
80
+
81
+ it "detects stupid bots at least" do
82
+ post "/contact-us/", {
83
+ reply_to: 'hello@visitor.com',
84
+ message: 'Hello from visitor',
85
+ reply_to_confirm: 'hello@visitor.com'
86
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
87
+ expect(last_response.status).to eql(400)
88
+ expect(Mail::TestMailer.deliveries.length).to eql(0)
89
+ end
90
+
91
+ it 'forbids usage of :to unless a secret is provided' do
92
+ post "/contact-us/", {
93
+ to: 'hello@visitor.com',
94
+ reply_to: 'hello@visitor.com',
95
+ message: 'Hello from visitor',
96
+ key: 'value',
97
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
98
+ expect(last_response.status).to eql(400)
99
+ expect(Mail::TestMailer.deliveries.length).to eql(0)
100
+ end
101
+
102
+ it 'does not allow setting the :to without a valid AUTH token' do
103
+ Talktome.set_env('TALKTOME_BEARER_SECRET', "Invalid secret") do
104
+ post "/contact-us/", {
105
+ to: 'hello@visitor.com',
106
+ reply_to: 'hello@visitor.com',
107
+ message: 'Hello from visitor',
108
+ key: 'value',
109
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
110
+ expect(last_response.status).to eql(401)
111
+ expect(Mail::TestMailer.deliveries.length).to eql(0)
112
+ end
113
+ end
114
+
115
+ it 'requires a valid Email for :to' do
116
+ post "/contact-us/", {
117
+ to: nil,
118
+ reply_to: 'hello@visitor.com',
119
+ message: 'Hello from visitor',
120
+ key: 'value',
121
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
122
+ expect(last_response.status).to eql(400)
123
+
124
+ post "/contact-us/", {
125
+ to: "notavalidemail",
126
+ reply_to: 'hello@visitor.com',
127
+ message: 'Hello from visitor',
128
+ key: 'value',
129
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
130
+ expect(last_response.status).to eql(400)
131
+ end
132
+
133
+ end
134
+
135
+ context 'POST /contact-us/, regarding the Reply-To' do
136
+ class ::Talktome::Message::Template
137
+ def raise_on_context_miss?
138
+ false
139
+ end
140
+ end
141
+
142
+ around(:each) do |bl|
143
+ Talktome.set_env('TALKTOME_EMAIL_DEFAULT_REPLYTO', "replyto@talktome.com", &bl)
144
+ end
145
+
146
+ it 'takes the default value from environment if set' do
147
+ post "/contact-us/", {
148
+ message: 'Hello from visitor'
149
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
150
+ expect(last_response).to be_ok
151
+ expect(Mail::TestMailer.deliveries.length).to eql(1)
152
+ expect(Mail::TestMailer.deliveries.first.reply_to).to eql(["replyto@talktome.com"])
153
+ end
154
+
155
+ it "lets override it by passing a replyTo field" do
156
+ post "/contact-us/", {
157
+ reply_to: 'hello@visitor.com',
158
+ message: 'Hello from visitor'
159
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
160
+ expect(last_response).to be_ok
161
+ expect(Mail::TestMailer.deliveries.length).to eql(1)
162
+ expect(Mail::TestMailer.deliveries.first.reply_to).to eql(["hello@visitor.com"])
163
+ end
164
+ end
165
+
166
+ context 'POST /multi-lingual/en/' do
167
+
168
+ it 'works' do
169
+ post "/multi-lingual/en/", {
170
+ reply_to: 'hello@visitor.com',
171
+ message: 'Hello from visitor',
172
+ key: 'value',
173
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
174
+ expect(last_response.status).to eql(200)
175
+ expect(last_response).to be_ok
176
+ expect(Mail::TestMailer.deliveries.length).to eql(1)
177
+ expect(Mail::TestMailer.deliveries.first.to).to eql(["to@talktome.com"])
178
+ expect(Mail::TestMailer.deliveries.first.from).to eql(["from@talktome.com"])
179
+ expect(Mail::TestMailer.deliveries.first.subject).to eql("Someone wants to reach you!")
180
+ expect(Mail::TestMailer.deliveries.first.html_part.body).to include("<li>Key: value</li>")
181
+ expect(Mail::TestMailer.deliveries.first.html_part.body).to include("Truly yours")
182
+ end
183
+ end
184
+
185
+ end
186
+ end
@@ -3,110 +3,76 @@ module Talktome
3
3
  class Client
4
4
  describe Local do
5
5
 
6
- let(:user) {
7
- { email: "user@test.com" }
6
+ let(:strategy) {
7
+ Strategy::Debug.new
8
8
  }
9
9
 
10
- let(:tpldata) {
11
- { who: "Test user" }
10
+ let(:client){
11
+ Local.new(folder, options) do |c|
12
+ c.strategy :email, strategy
13
+ end
12
14
  }
13
15
 
14
16
  let(:folder) {
15
17
  Path.dir/"../fixtures"
16
18
  }
17
19
 
18
- let(:client){
19
- Local.new(folder, options) do |c|
20
- c.strategy :email, strategy
21
- end
20
+ let(:user) {
21
+ { email: "user@test.com" }
22
22
  }
23
23
 
24
- context 'with a debug strategy' do
25
- let(:strategy) {
26
- Strategy::Debug.new
27
- }
28
-
29
- before(:each) {
30
- strategy.clear!
31
- }
24
+ let(:tpldata) {
25
+ { who: "Test user" }
26
+ }
32
27
 
33
- context "without templates" do
34
- let(:options) {
35
- {}
36
- }
28
+ before(:each) {
29
+ strategy.clear!
30
+ }
37
31
 
38
- it 'sends email when requested' do
39
- client.talktome("welcome", user, tpldata, [:email])
40
- expect(strategy.last.message).not_to be_nil
41
- expect(strategy.last.message.to_html).to eql("<h1>Hello Test user</h1>\n\n<p>Welcome to this email example!</p>\n\n<h3>Test user</h3>\n")
42
- end
32
+ context "without layouts" do
33
+ let(:options) {
34
+ {}
35
+ }
43
36
 
44
- it 'lets send the same email to multiple users' do
45
- client.talktome("welcome", [user], tpldata, [:email])
46
- expect(strategy.last.message).not_to be_nil
47
- expect(strategy.last.message.to_html).to eql("<h1>Hello Test user</h1>\n\n<p>Welcome to this email example!</p>\n\n<h3>Test user</h3>\n")
48
- end
37
+ it 'sends email when requested' do
38
+ client.talktome("welcome", user, tpldata, [:email])
39
+ expect(strategy.last.message).not_to be_nil
40
+ expect(strategy.last.message.to_html).to eql("<h1>Hello Test user</h1>\n\n<p>Welcome to this email example!</p>\n\n<h3>Test user</h3>\n")
49
41
  end
42
+ end
50
43
 
51
- context "with templates" do
52
- let(:options) {
53
- {
54
- templates: Path.dir/"../fixtures/templates"
55
- }
44
+ context "with layouts under the :layouts option key" do
45
+ let(:options) {
46
+ {
47
+ layouts: Path.dir/"../fixtures/layouts"
56
48
  }
49
+ }
57
50
 
58
- it 'sends email when requested' do
59
- client.talktome("welcome", user, tpldata, [:email])
60
- expect(strategy.last.message).not_to be_nil
61
- expect(strategy.last.message.to_html).to eql("<html><title>Hello Test user</title><body><h1>Hello Test user</h1>\n\n<p>Welcome to this email example!</p>\n\n<h3>Test user</h3>\n</body></html>\n")
62
- end
63
-
64
- it 'yields the callback with the email' do
65
- seen = nil
66
- client.talktome("welcome", user, tpldata, [:email]){|m|
67
- seen = m
68
- }
69
- expect(seen).not_to be_nil
70
- end
51
+ it 'sends email when requested' do
52
+ client.talktome("welcome", user, tpldata, [:email])
53
+ expect(strategy.last.message).not_to be_nil
54
+ expect(strategy.last.message.to_html).to eql("<html><title>Hello Test user</title><body><h1>Hello Test user</h1>\n\n<p>Welcome to this email example!</p>\n\n<h3>Test user</h3>\n</body></html>\n")
71
55
  end
72
- end
73
56
 
74
- context 'with an email strategy' do
75
- before do
76
- Mail.defaults do
77
- delivery_method :test
78
- end
79
- Mail::TestMailer.deliveries = []
57
+ it 'yields the callback with the email' do
58
+ seen = nil
59
+ client.talktome("welcome", user, tpldata, [:email]){|m|
60
+ seen = m
61
+ }
62
+ expect(seen).not_to be_nil
80
63
  end
64
+ end
81
65
 
66
+ context "with layouts under the :templates option key (backward compatibility)" do
82
67
  let(:options) {
83
- {}
84
- }
85
-
86
- let(:strategy) {
87
- Strategy::Email.new({}){|m|
88
- m.from "hello@test.com"
68
+ {
69
+ templates: Path.dir/"../fixtures/layouts"
89
70
  }
90
71
  }
91
72
 
92
73
  it 'sends email when requested' do
93
74
  client.talktome("welcome", user, tpldata, [:email])
94
- mail = Mail::TestMailer.deliveries.first
95
- expect(mail).not_to be_nil
96
- expect(mail.from).to eql(["hello@test.com"])
97
- expect(mail.to).to eql(["user@test.com"])
98
- end
99
-
100
- it 'allows sending email to multiple users' do
101
- client.talktome("welcome", [
102
- { email: "user1@test.com" },
103
- { email: "user2@test.com" }
104
- ], tpldata, [:email])
105
- expect(Mail::TestMailer.deliveries.length).to eql(1)
106
- mail = Mail::TestMailer.deliveries.first
107
- expect(mail).not_to be_nil
108
- expect(mail.from).to eql(["hello@test.com"])
109
- expect(mail.to).to eql(["user1@test.com", "user2@test.com"])
75
+ expect(strategy.last.message.to_html).to eql("<html><title>Hello Test user</title><body><h1>Hello Test user</h1>\n\n<p>Welcome to this email example!</p>\n\n<h3>Test user</h3>\n</body></html>\n")
110
76
  end
111
77
  end
112
78
 
@@ -0,0 +1,13 @@
1
+ ---
2
+ subject: |-
3
+ {{subject}}
4
+ ---
5
+ Hello,
6
+
7
+ This person is trying to reach you:
8
+
9
+ {{#allvars}}
10
+ * {{key}}: {{value}}
11
+ {{/allvars}}
12
+
13
+ {{footer}}
File without changes
@@ -0,0 +1,13 @@
1
+ ---
2
+ subject: |-
3
+ {{subject}}
4
+ ---
5
+ Hello,
6
+
7
+ This person is trying to reach you:
8
+
9
+ {{#allvars}}
10
+ * {{key}}: {{value}}
11
+ {{/allvars}}
12
+
13
+ {{footer}}
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
2
  require 'talktome'
3
+ require 'talktome/app'
4
+ require 'rack/test'
3
5
 
4
6
  module SpecHelpers
5
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: talktome
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernard Lambeau
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-19 00:00:00.000000000 Z
11
+ date: 2021-05-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -16,42 +16,56 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '10'
19
+ version: '13'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '10'
26
+ version: '13'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '3.6'
33
+ version: '3.10'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '3.6'
40
+ version: '3.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rack-test
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.6.3
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.6.3
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: path
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - ">="
46
60
  - !ruby/object:Gem::Version
47
- version: '1.3'
61
+ version: '2.0'
48
62
  type: :runtime
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - ">="
53
67
  - !ruby/object:Gem::Version
54
- version: '1.3'
68
+ version: '2.0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: mail
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -100,6 +114,60 @@ dependencies:
100
114
  - - "~>"
101
115
  - !ruby/object:Gem::Version
102
116
  version: '3'
117
+ - !ruby/object:Gem::Dependency
118
+ name: sinatra
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '2.0'
124
+ - - "<"
125
+ - !ruby/object:Gem::Version
126
+ version: '3.0'
127
+ type: :runtime
128
+ prerelease: false
129
+ version_requirements: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '2.0'
134
+ - - "<"
135
+ - !ruby/object:Gem::Version
136
+ version: '3.0'
137
+ - !ruby/object:Gem::Dependency
138
+ name: finitio
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: 0.10.0
144
+ - - "<"
145
+ - !ruby/object:Gem::Version
146
+ version: 0.11.0
147
+ type: :runtime
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: 0.10.0
154
+ - - "<"
155
+ - !ruby/object:Gem::Version
156
+ version: 0.11.0
157
+ - !ruby/object:Gem::Dependency
158
+ name: rack-robustness
159
+ requirement: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - "~>"
162
+ - !ruby/object:Gem::Version
163
+ version: '1.1'
164
+ type: :runtime
165
+ prerelease: false
166
+ version_requirements: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - "~>"
169
+ - !ruby/object:Gem::Version
170
+ version: '1.1'
103
171
  description: Talktome helps you talk to users by email, messaging, sms, etc. It abstracts
104
172
  the messaging mechanisms and lets you manage message templates easily.
105
173
  email: blambeau@gmail.com
@@ -112,6 +180,7 @@ files:
112
180
  - README.md
113
181
  - Rakefile
114
182
  - lib/talktome.rb
183
+ - lib/talktome/app.rb
115
184
  - lib/talktome/client.rb
116
185
  - lib/talktome/client/local.rb
117
186
  - lib/talktome/error.rb
@@ -120,8 +189,11 @@ files:
120
189
  - lib/talktome/strategy/debug.rb
121
190
  - lib/talktome/strategy/email.rb
122
191
  - lib/talktome/version.rb
192
+ - spec/app/test_app.rb
123
193
  - spec/client/test_local.rb
124
- - spec/fixtures/templates/email.html
194
+ - spec/fixtures/contact-us/email.md
195
+ - spec/fixtures/layouts/email.html
196
+ - spec/fixtures/multi-lingual/en/email.md
125
197
  - spec/fixtures/welcome/email.md
126
198
  - spec/fixtures/welcome/footer.mustache
127
199
  - spec/message/test_initialize.rb
@@ -150,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
222
  - !ruby/object:Gem::Version
151
223
  version: '0'
152
224
  requirements: []
153
- rubygems_version: 3.1.2
225
+ rubygems_version: 3.2.15
154
226
  signing_key:
155
227
  specification_version: 4
156
228
  summary: Talktome helps you talk to users by email, messaging, sms, etc.