talktome 0.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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.