bidi2pdf-rails 0.0.1.alpha.1 → 0.1.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 +4 -4
- data/.idea/bidi2pdf-rails.iml +16 -8
- data/.rubocop.yml +11 -0
- data/CHANGELOG.md +33 -0
- data/README.md +104 -41
- data/Rakefile +2 -0
- data/cliff.toml +126 -0
- data/lib/bidi2pdf_rails/browser_console_log_subscriber.rb +1 -1
- data/lib/bidi2pdf_rails/chromedriver_manager_singleton.rb +10 -10
- data/lib/bidi2pdf_rails/config.rb +133 -0
- data/lib/bidi2pdf_rails/configurable.rb +106 -0
- data/lib/bidi2pdf_rails/main_log_subscriber.rb +1 -1
- data/lib/bidi2pdf_rails/network_log_subscriber.rb +1 -1
- data/lib/bidi2pdf_rails/railtie.rb +2 -0
- data/lib/bidi2pdf_rails/services/pdf_renderer.rb +9 -8
- data/lib/bidi2pdf_rails/version.rb +1 -1
- data/lib/bidi2pdf_rails.rb +28 -99
- data/lib/generators/bidi2pdf_rails/USAGE +12 -4
- data/lib/generators/bidi2pdf_rails/initializer_generator.rb +136 -31
- data/lib/generators/bidi2pdf_rails/templates/bidi2pdf_rails.rb.tt +15 -101
- data/spec/acceptance/user_can_download_report_pdf_spec.rb +133 -0
- data/spec/acceptance/user_can_generate_pdf_from_protected_remote_url_spec.rb +173 -0
- data/spec/dummy/app/controllers/reports_controller.rb +35 -2
- data/spec/dummy/app/controllers/secure_controller.rb +52 -0
- data/spec/dummy/app/views/layouts/simple.html.erb +17 -0
- data/spec/dummy/app/views/secure/show.html.erb +10 -0
- data/spec/dummy/config/initializers/bidi2pdf_rails.rb +64 -54
- data/spec/dummy/config/routes.rb +9 -1
- data/spec/dummy/log/development.log +3850 -0
- data/spec/dummy/log/test.log +53046 -0
- data/spec/dummy/tmp/pids/server.pid +1 -1
- data/spec/integration/generators/bidi2pdf_rails/initializer_generator_spec.rb +64 -0
- data/spec/rails_helper.rb +8 -1
- data/spec/spec_helper.rb +47 -5
- data/spec/support/default_dirs_helper.rb +32 -0
- data/spec/support/pdf_helper.rb +12 -0
- data/spec/support/render_setting_helpers.rb +28 -0
- data/spec/support/request_server_bootstrap.rb +44 -0
- data/spec/{bidi2pdf_rails → unit/bidi2pdf_rails}/bidi2pdf_rails_spec.rb +1 -1
- data/spec/unit/bidi2pdf_rails/configurable/base_nested_config_spec.rb +133 -0
- data/tasks/changelog.rake +29 -0
- data/tasks/coverage.rake +23 -0
- metadata +69 -20
- data/spec/dummy/spec/helpers/reports_helper_spec.rb +0 -15
- data/spec/dummy/spec/requests/reports_spec.rb +0 -10
- data/spec/dummy/spec/views/reports/show.html.erb_spec.rb +0 -5
- data/spec/generator/bidie2pdf_rails_initializer_generator_spec.rb +0 -5
- data/spec/generator/initializer_generator_spec.rb +0 -5
- data/spec/requests/reports_spec.rb +0 -17
@@ -1,44 +1,72 @@
|
|
1
1
|
module Bidi2pdfRails
|
2
2
|
class InitializerGenerator < Rails::Generators::Base
|
3
3
|
source_root File.expand_path("templates", __dir__)
|
4
|
-
desc "Generates a config/initializers/bidi2pdf.rb config file"
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
def self.infer_option_type(default)
|
6
|
+
case default
|
7
|
+
when true, false then :boolean
|
8
|
+
when Numeric then :numeric
|
9
|
+
when Hash then :hash
|
10
|
+
when Array then :array
|
11
|
+
when Proc then :string
|
12
|
+
else :string
|
13
|
+
end
|
14
|
+
end
|
11
15
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@proxy = yes?("Use a proxy server? (y/n)", :green)
|
16
|
+
def self.normalize_group(key)
|
17
|
+
key.to_s.sub(/(_settings|_options)$/, "")
|
18
|
+
end
|
16
19
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
+
def self.with_group_config
|
21
|
+
that = self
|
22
|
+
Bidi2pdfRails::Config::CONFIG_OPTIONS.each_pair do |group_key, group_config|
|
23
|
+
group_prefix = that.normalize_group(group_key)
|
24
|
+
yield group_key, group_prefix, group_config
|
20
25
|
end
|
26
|
+
end
|
21
27
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
if @pdf_margins
|
27
|
-
@pdf_margin_top = ask("PDF margin top (mm):", :yellow)
|
28
|
-
@pdf_margin_bottom = ask("PDF margin bottom (mm):", :yellow)
|
29
|
-
@pdf_margin_left = ask("PDF margin left (mm):", :yellow)
|
30
|
-
@pdf_margin_right = ask("PDF margin right (mm):", :yellow)
|
28
|
+
def self.with_group_option
|
29
|
+
with_group_config do |group_key, group_prefix, group_config|
|
30
|
+
group_config[:options].each do |opt|
|
31
|
+
yield group_key, group_prefix, opt
|
31
32
|
end
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
with_group_option do |_group_key, group_prefix, opt|
|
37
|
+
next unless opt[:ask]
|
38
|
+
|
39
|
+
name = "#{group_prefix}_#{opt[:name]}"
|
40
|
+
type = infer_option_type(opt[:default])
|
41
|
+
default = opt[:default_as_str] || opt[:default]
|
42
|
+
|
43
|
+
class_option name,
|
44
|
+
type: type,
|
45
|
+
desc: opt[:desc],
|
46
|
+
default: default,
|
47
|
+
enum: opt[:limited_to]
|
48
|
+
end
|
49
|
+
|
50
|
+
def ask_questions
|
51
|
+
@answers = {}
|
52
|
+
|
53
|
+
return if options[:quiet]
|
54
|
+
|
55
|
+
self.class.with_group_config do |_group_key, group_prefix, group_config|
|
56
|
+
next unless group_config[:ask]
|
57
|
+
if group_config[:ask]
|
58
|
+
next unless yes?(group_config[:ask], :green)
|
36
59
|
end
|
37
60
|
|
38
|
-
|
39
|
-
|
40
|
-
|
61
|
+
group_config[:options].select { |opt| opt[:ask] }.each do |opt|
|
62
|
+
option_key = "#{group_prefix}_#{opt[:name]}".to_sym
|
63
|
+
next if cli_option_set?(option_key, opt)
|
64
|
+
|
65
|
+
@answers[option_key] = prompt_for_option(opt)
|
66
|
+
end
|
41
67
|
end
|
68
|
+
|
69
|
+
@answers.compact!
|
42
70
|
end
|
43
71
|
|
44
72
|
def generate_config
|
@@ -49,9 +77,10 @@ module Bidi2pdfRails
|
|
49
77
|
|
50
78
|
def inject_environment_config
|
51
79
|
env_file = "config/environments/development.rb"
|
80
|
+
env_path = File.join(destination_root, env_file)
|
52
81
|
|
53
|
-
if File.exist?(
|
54
|
-
if File.read(
|
82
|
+
if File.exist?(env_path)
|
83
|
+
if File.read(env_path).include?("config.x.bidi2pdf_rails")
|
55
84
|
say_status :skipped, "Bidi2PDF settings already present in #{env_file}", :yellow
|
56
85
|
else
|
57
86
|
content = <<~RUBY.gsub(/^(?=[\w#])/, " ")
|
@@ -70,5 +99,81 @@ module Bidi2pdfRails
|
|
70
99
|
say_status :error, "Could not find #{env_file}", :red
|
71
100
|
end
|
72
101
|
end
|
102
|
+
|
103
|
+
no_commands do
|
104
|
+
def cli_option_set?(option_key, opt_def)
|
105
|
+
return false unless options.key?(option_key.to_s)
|
106
|
+
|
107
|
+
default = opt_def[:default_as_str] || opt_def[:default]
|
108
|
+
options[option_key.to_s].to_s != default.to_s
|
109
|
+
end
|
110
|
+
|
111
|
+
def prompt_for_option(opt)
|
112
|
+
type = self.class.infer_option_type(opt[:default])
|
113
|
+
|
114
|
+
if type == :boolean
|
115
|
+
question = opt[:desc].match?(/y\/n/i) ? opt[:desc] : "#{opt[:desc]} (y/n)?"
|
116
|
+
yes?(question, opt[:color])
|
117
|
+
else
|
118
|
+
opts = {
|
119
|
+
default: opt[:default_as_str] || opt[:default],
|
120
|
+
limited_to: opt[:limited_to],
|
121
|
+
echo: !opt[:secret]
|
122
|
+
}.compact
|
123
|
+
|
124
|
+
ask(opt[:desc], opt[:color], **opts)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def normalize_group(key)
|
129
|
+
self.class.normalize_group(key)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def changed_by_user?(full_top_level_name, option_name)
|
136
|
+
top_level_name = normalize_group full_top_level_name
|
137
|
+
|
138
|
+
full_qual_option_name = "#{top_level_name}_#{option_name}".to_sym
|
139
|
+
return false unless options.key?(full_qual_option_name.to_s)
|
140
|
+
|
141
|
+
option_def = Bidi2pdfRails::Config::CONFIG_OPTIONS.dig(full_top_level_name.to_sym, :options).find { |opt| opt[:name] == option_name.to_sym }
|
142
|
+
default = option_def[:default_as_str] ? option_def[:default_as_str] : option_def[:default]
|
143
|
+
|
144
|
+
current_value = get_option_value(top_level_name, option_name)
|
145
|
+
|
146
|
+
return false if default.nil? && current_value.empty?
|
147
|
+
return true if default.nil? && current_value.present?
|
148
|
+
|
149
|
+
!current_value.to_s.match /#{Regexp.escape(default.to_s)}/
|
150
|
+
end
|
151
|
+
|
152
|
+
def get_option_value(top_level_name, option_name)
|
153
|
+
top_level_name = top_level_name.to_s.sub(/(_settings)|(_options)$/, "")
|
154
|
+
full_qual_option_name = "#{top_level_name}_#{option_name}".to_sym
|
155
|
+
|
156
|
+
result = if @answers.key?(full_qual_option_name)
|
157
|
+
@answers[full_qual_option_name]
|
158
|
+
elsif options.key?(full_qual_option_name.to_s)
|
159
|
+
options[full_qual_option_name.to_s]
|
160
|
+
else
|
161
|
+
nil
|
162
|
+
end
|
163
|
+
|
164
|
+
if result.is_a?(String)
|
165
|
+
if result.match(/(^\s+->\s+{)|(.*Rails\.)/) || numeric?(result)
|
166
|
+
return result
|
167
|
+
end
|
168
|
+
'"' + result + '"'
|
169
|
+
else
|
170
|
+
result
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def numeric?(string)
|
175
|
+
# Float/Integer conversion with rescue catches most numeric formats
|
176
|
+
true if Float(string) rescue false
|
177
|
+
end
|
73
178
|
end
|
74
179
|
end
|
@@ -2,111 +2,25 @@
|
|
2
2
|
|
3
3
|
Bidi2pdfRails.configure do |config|
|
4
4
|
overrides = Rails.application.config.x.bidi2pdf_rails
|
5
|
+
<%
|
6
|
+
Bidi2pdfRails::Config::CONFIG_OPTIONS.each_pair do |top_level_option_name, top_level_option|
|
7
|
+
name = top_level_option[:name]
|
8
|
+
%>
|
5
9
|
|
6
|
-
config.notification_service = ActiveSupport::Notifications
|
7
|
-
|
8
|
-
config.logger = Rails.logger
|
9
|
-
config.verbosity = overrides.verbosity.nil? ? :none : overrides.verbosity
|
10
|
-
config.default_timeout = 10
|
11
|
-
|
12
|
-
# Chrome & BiDi
|
13
|
-
# config.remote_browser_url = nil
|
14
|
-
config.headless = overrides.headless.nil? ? <%= @headless ? "true" : "false" %> : overrides.headless
|
15
|
-
#
|
16
|
-
# alternative to remote_browser_url
|
17
|
-
#
|
18
|
-
# config.chromedriver_port = 0
|
19
|
-
# config.chrome_session_args = [
|
20
|
-
# "--disable-gpu",
|
21
|
-
# "--no-sandbox"
|
22
|
-
# ]
|
23
|
-
|
24
|
-
<% if @proxy %>
|
25
|
-
config.proxy_addr = "<%= @proxy_addr %>"
|
26
|
-
config.proxy_port = <%= @proxy_port %>
|
27
|
-
config.proxy_user = nil
|
28
|
-
config.proxy_pass = nil
|
29
|
-
<% else %>
|
30
|
-
# config.proxy_addr = nil
|
31
|
-
# config.proxy_port = nil
|
32
|
-
# config.proxy_user = nil
|
33
|
-
# config.proxy_pass = nil
|
34
|
-
<% end %>
|
35
|
-
|
36
|
-
# PDF settings (css media settings take precedence over these)
|
37
|
-
<% if @configure_pdf %>
|
38
|
-
|
39
|
-
config.pdf_orientation = "<%= @pdf_orientation || "portrait" %>"
|
40
|
-
<% if @pdf_margins %>
|
41
|
-
config.pdf_margin_top = <%= @pdf_margin_top || 2.5 %>
|
42
|
-
config.pdf_margin_bottom = <%= @pdf_margin_bottom || 2 %>
|
43
|
-
config.pdf_margin_left = <%= @pdf_margin_left || 2 %>
|
44
|
-
config.pdf_margin_right = <%= @pdf_margin_right || 2 %>
|
45
|
-
<% else %>
|
46
|
-
# config.pdf_margin_top = 2.5
|
47
|
-
# config.pdf_margin_bottom = 2
|
48
|
-
# config.pdf_margin_left = 2
|
49
|
-
# config.pdf_margin_right = 2
|
50
|
-
<% end %>
|
51
|
-
|
52
|
-
<% if @pdf_page %>
|
53
|
-
# Page settings
|
54
|
-
config.pdf_page_width = <%= @pdf_page_width || Bidi2pdf::PAPER_FORMATS_CM[:a4][:width] %>
|
55
|
-
config.pdf_page_height = <%= @pdf_page_height || Bidi2pdf::PAPER_FORMATS_CM[:a4][:height] %>
|
56
|
-
<% else %>
|
57
|
-
# Page settings
|
58
|
-
# config.pdf_page_format = "A4"
|
59
|
-
#
|
60
|
-
# or custom page size
|
61
|
-
#
|
62
|
-
# config.pdf_page_width = <%= Bidi2pdf::PAPER_FORMATS_CM[:a4][:width] %>
|
63
|
-
# config.pdf_page_height = <%= Bidi2pdf::PAPER_FORMATS_CM[:a4][:height] %>
|
64
|
-
<% end %>
|
65
|
-
|
66
|
-
config.pdf_print_background = <%= @pdf_print_background ? "true" : "false" %>
|
67
|
-
config.pdf_scale = <%= @pdf_scale || 1.0 %>
|
68
|
-
config.shrink_to_fit = <%= @shrink_to_fit ? "true" : "false" %>
|
69
|
-
<% else %>
|
70
|
-
# config.pdf_orientation = "portrait"
|
71
|
-
# config.pdf_margin_top = 2.5
|
72
|
-
# config.pdf_margin_bottom = 2
|
73
|
-
# config.pdf_margin_left = 2
|
74
|
-
# config.pdf_margin_right = 2
|
75
|
-
# config.pdf_print_background = true
|
76
|
-
# config.pdf_scale = 1.0
|
77
|
-
# config.shrink_to_fit = true
|
78
|
-
|
79
|
-
# Page settings
|
80
|
-
# config.pdf_page_format = "A4"
|
81
10
|
#
|
82
|
-
#
|
11
|
+
# <%= name %>
|
83
12
|
#
|
84
|
-
|
85
|
-
|
13
|
+
<% top_level_option[:options].each do |option| %>
|
14
|
+
<% if option[:limited_to] %>
|
15
|
+
<%= "# Allowed values: #{option[:limited_to].map { |v| v.inspect }.join(", ")}" %>
|
16
|
+
<% end -%>
|
17
|
+
<% if changed_by_user?(top_level_option_name, option[:name]) -%>
|
18
|
+
<%= "config.#{top_level_option_name}.#{option[:name]} = #{get_option_value(top_level_option_name, option[:name]) } # #{option[:desc]}" -%>
|
19
|
+
<% else -%>
|
20
|
+
<%= "# config.#{top_level_option_name}.#{option[:name]} = #{option[:default_as_str] ? option[:default_as_str] : option[:default].inspect} # #{option[:desc]}" -%>
|
21
|
+
<% end -%>
|
22
|
+
<% end -%>
|
86
23
|
<% end %>
|
87
|
-
|
88
|
-
# config.cookies = [
|
89
|
-
# { name: "session", value: "abc123", domain: "example.com" }
|
90
|
-
# ]
|
91
|
-
|
92
|
-
# config.headers = {
|
93
|
-
# "X-API-KEY" => "topsecret"
|
94
|
-
# }
|
95
|
-
|
96
|
-
# config.auth = {
|
97
|
-
# username: "admin",
|
98
|
-
# password: "secret"
|
99
|
-
# }
|
100
|
-
|
101
|
-
# chromedriver install dir
|
102
|
-
# config.install_dir = Rails.root.join("tmp", "bidi2pdf").to_s
|
103
|
-
|
104
|
-
# wait for the page to load settings
|
105
|
-
# config.wait_for_network_idle = true
|
106
|
-
# config.wait_for_page_loaded = false
|
107
|
-
# this script can be any valid JS script that returns a Promise
|
108
|
-
# it will wait for the Promise to resolve
|
109
|
-
# config.wait_for_page_check_script = nil
|
110
24
|
end
|
111
25
|
|
112
26
|
Rails.application.config.after_initialize do
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails_helper"
|
4
|
+
require "net/http"
|
5
|
+
require "rack/handler/puma"
|
6
|
+
require "socket"
|
7
|
+
|
8
|
+
RSpec.feature "As a devoloper, I want to generate PDF's with bidi2pdf-rails", :pdf, type: :request do
|
9
|
+
# This feature demonstrates how to use bidi2pdf-rails to generate PDFs
|
10
|
+
# from Rails views, remote URLs, and inline HTML. These specs double as
|
11
|
+
# living documentation for gem users.
|
12
|
+
|
13
|
+
before(:all) do
|
14
|
+
# Prepare the PDF rendering engine (chromium, etc.)
|
15
|
+
Bidi2pdfRails::ChromedriverManagerSingleton.initialize_manager force: true
|
16
|
+
end
|
17
|
+
|
18
|
+
after(:all) do
|
19
|
+
Bidi2pdfRails::ChromedriverManagerSingleton.shutdown
|
20
|
+
end
|
21
|
+
|
22
|
+
scenario "Rendering a controller view to PDF using layout: 'pdf'" do
|
23
|
+
# Controller example:
|
24
|
+
#
|
25
|
+
# def show
|
26
|
+
# respond_to do |format|
|
27
|
+
# format.html
|
28
|
+
# format.pdf { render pdf: 'my-report', layout: 'pdf' }
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
|
32
|
+
when_ "I visit the PDF version of a report" do
|
33
|
+
before do
|
34
|
+
@response = get_pdf_response "/reports/1.pdf"
|
35
|
+
end
|
36
|
+
|
37
|
+
then_ "I receive a successful HTTP response" do
|
38
|
+
expect(@response.code).to eq("200")
|
39
|
+
end
|
40
|
+
|
41
|
+
and_ "I receive a PDF file in response" do
|
42
|
+
expect(@response['Content-Type']).to eq("application/pdf")
|
43
|
+
end
|
44
|
+
|
45
|
+
and_ "the PDF contains the expected number of pages" do
|
46
|
+
expect(@response.body).to have_pdf_page_count(5)
|
47
|
+
end
|
48
|
+
|
49
|
+
and_ "the disposition header is set to attachment" do
|
50
|
+
expect(@response['Content-Disposition']).to start_with('inline; filename="my-report.pdf"')
|
51
|
+
end
|
52
|
+
|
53
|
+
and_ "the PDF contains the expected content" do
|
54
|
+
expect(@response.body).to contains_pdf_text("Section Two").at_page(2)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
scenario "Converting a remote URL into a PDF with custom options" do
|
60
|
+
# Controller usage:
|
61
|
+
#
|
62
|
+
# def convert_remote_url
|
63
|
+
# render pdf: 'convert-remote-url', url: "http://example.com", wait_for_page_loaded: false, print_options: { page: { format: :A4 } }
|
64
|
+
# end
|
65
|
+
|
66
|
+
when_ "I visit the PDF version of a report" do
|
67
|
+
before do
|
68
|
+
@response = get_pdf_response "/convert-remote-url.pdf"
|
69
|
+
end
|
70
|
+
|
71
|
+
then_ "I receive a successful HTTP response" do
|
72
|
+
expect(@response.code).to eq("200")
|
73
|
+
end
|
74
|
+
|
75
|
+
and_ "I receive a PDF file in response" do
|
76
|
+
expect(@response['Content-Type']).to eq("application/pdf")
|
77
|
+
end
|
78
|
+
|
79
|
+
and_ "the PDF contains the expected number of pages" do
|
80
|
+
expect(@response.body).to have_pdf_page_count(1)
|
81
|
+
end
|
82
|
+
|
83
|
+
and_ "the disposition header is set to attachment" do
|
84
|
+
expect(@response['Content-Disposition']).to start_with('inline; filename="convert-remote-url.pdf"')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
scenario "Rendering inline HTML directly to PDF" do
|
90
|
+
# Controller usage:
|
91
|
+
#
|
92
|
+
# def inline_html
|
93
|
+
# html = <<~HTML
|
94
|
+
# <html>
|
95
|
+
# <head>
|
96
|
+
# </head>
|
97
|
+
# <body>
|
98
|
+
# <h1>PDF Rendering Sample</h1>
|
99
|
+
# <p style="page-break-after: always;">Page break</p>
|
100
|
+
# <p>Content Page 2</p>
|
101
|
+
# </body>
|
102
|
+
# </html>
|
103
|
+
#
|
104
|
+
# render pdf: 'inline-html', inline: html, wait_for_page_loaded: false, print_options: { page: { format: :A4 } }
|
105
|
+
# end
|
106
|
+
|
107
|
+
when_ "I visit the PDF version of a report" do
|
108
|
+
before do
|
109
|
+
@response = get_pdf_response "/inline-html.pdf"
|
110
|
+
end
|
111
|
+
|
112
|
+
then_ "I receive a successful HTTP response" do
|
113
|
+
expect(@response.code).to eq("200")
|
114
|
+
end
|
115
|
+
|
116
|
+
and_ "I receive a PDF file in response" do
|
117
|
+
expect(@response['Content-Type']).to eq("application/pdf")
|
118
|
+
end
|
119
|
+
|
120
|
+
and_ "the PDF contains the expected number of pages" do
|
121
|
+
expect(@response.body).to have_pdf_page_count(2)
|
122
|
+
end
|
123
|
+
|
124
|
+
and_ "the disposition header is set to attachment" do
|
125
|
+
expect(@response['Content-Disposition']).to start_with('inline; filename="inline-html.pdf"')
|
126
|
+
end
|
127
|
+
|
128
|
+
and_ "the PDF contains the expected content" do
|
129
|
+
expect(@response.body).to contains_pdf_text("PDF Rendering Sample")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails_helper"
|
4
|
+
require "net/http"
|
5
|
+
require "rack/handler/puma"
|
6
|
+
require "socket"
|
7
|
+
require "base64"
|
8
|
+
|
9
|
+
RSpec.feature "As a user, I want to generate a PDF from a protected remote URL", :pdf, type: :request do
|
10
|
+
before(:all) do
|
11
|
+
# Bidi2pdfRails.config.general_options.headless = false
|
12
|
+
Bidi2pdfRails::ChromedriverManagerSingleton.initialize_manager force: true
|
13
|
+
end
|
14
|
+
|
15
|
+
after(:all) do
|
16
|
+
Bidi2pdfRails::ChromedriverManagerSingleton.shutdown
|
17
|
+
end
|
18
|
+
|
19
|
+
scenario "Using basic auth for remote PDF rendering" do
|
20
|
+
# Controller setup:
|
21
|
+
#
|
22
|
+
# You can configure basic auth in two ways:
|
23
|
+
#
|
24
|
+
# 1. In an initializer (global config):
|
25
|
+
#
|
26
|
+
# Bidi2pdfRails.configure do |config|
|
27
|
+
# config.render_remote_settings.basic_auth_user = ->(_controller) { "admin" }
|
28
|
+
# config.render_remote_settings.basic_auth_pass = ->(_controller) { "secret" }
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# 2. Inline within controller action:
|
32
|
+
#
|
33
|
+
# render pdf: 'convert-remote-url-basic-auth',
|
34
|
+
# url: basic_auth_endpoint_url(only_path: false),
|
35
|
+
# auth: { username: "admin", password: "secret" }
|
36
|
+
|
37
|
+
when_ "I request a PDF generated from a basic-auth protected page" do
|
38
|
+
before do
|
39
|
+
with_render_setting :basic_auth_user, ->(_controller) { "admin" }
|
40
|
+
# in prod better to use:
|
41
|
+
# Bidi2pdfRails.config.render_remote_settings.basic_auth_pass = ->(_controller) { Rails.application.credentials.dig('bidi2pdf_rails', 'basic_auth_pass') }
|
42
|
+
with_render_setting :basic_auth_pass, ->(_controller) { "secret" }
|
43
|
+
|
44
|
+
@response = get_pdf_response "/convert-remote-url-basic-auth"
|
45
|
+
end
|
46
|
+
|
47
|
+
then_ "I receive a successful HTTP response" do
|
48
|
+
expect(@response.code).to eq("200")
|
49
|
+
end
|
50
|
+
|
51
|
+
and_ "I receive a PDF file in response" do
|
52
|
+
expect(@response['Content-Type']).to eq("application/pdf")
|
53
|
+
end
|
54
|
+
|
55
|
+
and_ "the PDF contains the expected number of pages" do
|
56
|
+
expect(@response.body).to have_pdf_page_count(1)
|
57
|
+
end
|
58
|
+
|
59
|
+
and_ "the disposition header is set to attachment" do
|
60
|
+
expect(@response['Content-Disposition']).to start_with('inline; filename="convert-remote-url-basic-auth.pdf"')
|
61
|
+
end
|
62
|
+
|
63
|
+
and_ "the PDF contains the expected content" do
|
64
|
+
expect(@response.body).to contains_pdf_text("This page is secured with: HTTPBasicAuthentication").at_page(1)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
scenario "Using a session cookie for remote PDF rendering" do
|
70
|
+
# Controller setup:
|
71
|
+
#
|
72
|
+
# You can configure cookies in two ways:
|
73
|
+
#
|
74
|
+
# 1. In an initializer (global config):
|
75
|
+
#
|
76
|
+
# Bidi2pdfRails.configure do |config|
|
77
|
+
# config.render_remote_settings.cookies = = ->(controller) { { "auth_token" => signed_cookie_value("valid-authentication-token", controller) } }
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# 2. Inline within controller action:
|
81
|
+
#
|
82
|
+
# render pdf: 'convert-remote-url-cookie',
|
83
|
+
# url: cookie_endpoint_url(only_path: false),
|
84
|
+
# wait_for_page_loaded: false,
|
85
|
+
# cookies: { "auth_token" => signed_cookie_value("valid-authentication-token", controller) }
|
86
|
+
#
|
87
|
+
|
88
|
+
when_ "I request a PDF from a page that requires a session cookie" do
|
89
|
+
def signed_cookie_value(value, controller)
|
90
|
+
request = controller.request
|
91
|
+
secret = request.key_generator.generate_key(request.signed_cookie_salt)
|
92
|
+
# Sign the value
|
93
|
+
verifier = ActiveSupport::MessageVerifier.new(secret)
|
94
|
+
verifier.generate(value)
|
95
|
+
end
|
96
|
+
|
97
|
+
before do
|
98
|
+
with_render_setting :cookies, ->(controller) { { "auth_token" => signed_cookie_value("valid-authentication-token", controller) } }
|
99
|
+
|
100
|
+
@response = get_pdf_response "/convert-remote-url-cookie"
|
101
|
+
end
|
102
|
+
|
103
|
+
then_ "I receive a successful HTTP response" do
|
104
|
+
expect(@response.code).to eq("200")
|
105
|
+
end
|
106
|
+
|
107
|
+
and_ "I receive a PDF file in response" do
|
108
|
+
expect(@response['Content-Type']).to eq("application/pdf")
|
109
|
+
end
|
110
|
+
|
111
|
+
and_ "the PDF contains the expected number of pages" do
|
112
|
+
expect(@response.body).to have_pdf_page_count(1)
|
113
|
+
end
|
114
|
+
|
115
|
+
and_ "the disposition header is set to attachment" do
|
116
|
+
expect(@response['Content-Disposition']).to start_with('inline; filename="convert-remote-url-cookie.pdf"')
|
117
|
+
end
|
118
|
+
|
119
|
+
and_ "the PDF contains the expected content" do
|
120
|
+
expect(@response.body).to contains_pdf_text("Protected Resource This page is secured with: CookieAuthentication").at_page(1)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
scenario "Using custom headers for remote PDF rendering" do
|
126
|
+
# Controller setup:
|
127
|
+
#
|
128
|
+
# You can configure headers in two ways:
|
129
|
+
#
|
130
|
+
# 1. In an initializer (global config):
|
131
|
+
#
|
132
|
+
# Bidi2pdfRails.configure do |config|
|
133
|
+
# config.render_remote_settings.headers = ->(controller) { { "X-API-Key" => "your-secret-api-key" } }
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# 2. Inline within controller action:
|
137
|
+
#
|
138
|
+
# render pdf: 'convert-remote-url-cookie',
|
139
|
+
# url: api_endpoint_url(only_path: false),
|
140
|
+
# wait_for_page_loaded: false,
|
141
|
+
# headers: { "X-API-Key" => "your-secret-api-key" }
|
142
|
+
#
|
143
|
+
|
144
|
+
when_ "I request a PDF from a page that requires an auth header" do
|
145
|
+
before do
|
146
|
+
@old_headers = Bidi2pdfRails.config.render_remote_settings.headers
|
147
|
+
with_render_setting :headers, ->(_controller) { { "X-API-Key" => "your-secret-api-key" } }
|
148
|
+
|
149
|
+
@response = get_pdf_response "/convert-remote-url-header"
|
150
|
+
end
|
151
|
+
|
152
|
+
then_ "I receive a successful HTTP response" do
|
153
|
+
expect(@response.code).to eq("200")
|
154
|
+
end
|
155
|
+
|
156
|
+
and_ "I receive a PDF file in response" do
|
157
|
+
expect(@response['Content-Type']).to eq("application/pdf")
|
158
|
+
end
|
159
|
+
|
160
|
+
and_ "the PDF contains the expected number of pages" do
|
161
|
+
expect(@response.body).to have_pdf_page_count(1)
|
162
|
+
end
|
163
|
+
|
164
|
+
and_ "the disposition header is set to attachment" do
|
165
|
+
expect(@response['Content-Disposition']).to start_with('inline; filename="convert-remote-url-cookie.pdf"')
|
166
|
+
end
|
167
|
+
|
168
|
+
and_ "the PDF contains the expected content" do
|
169
|
+
expect(@response.body).to contains_pdf_text("Protected Resource This page is secured with: API KeyAuthentication").at_page(1)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -6,7 +6,40 @@ class ReportsController < ApplicationController
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
render pdf: 'remote-
|
9
|
+
def convert_remote_url
|
10
|
+
render pdf: 'convert-remote-url', url: "http://example.com", wait_for_page_loaded: false, print_options: { page: { format: :A4 } }
|
11
|
+
end
|
12
|
+
|
13
|
+
def inline_html
|
14
|
+
html = <<~HTML
|
15
|
+
<html>
|
16
|
+
<head>
|
17
|
+
</head>
|
18
|
+
<body>
|
19
|
+
<h1>PDF Rendering Sample</h1>
|
20
|
+
<p style="page-break-after: always;">Page break</p>
|
21
|
+
<p>Content Page 2</p>
|
22
|
+
</body>
|
23
|
+
</html>
|
24
|
+
HTML
|
25
|
+
render pdf: 'inline-html', inline: html, wait_for_page_loaded: false, print_options: { page: { format: :A4 } }
|
26
|
+
end
|
27
|
+
|
28
|
+
def convert_remote_url_basic_auth
|
29
|
+
render pdf: 'convert-remote-url-basic-auth',
|
30
|
+
url: basic_auth_endpoint_url(only_path: false),
|
31
|
+
wait_for_page_loaded: false
|
32
|
+
end
|
33
|
+
|
34
|
+
def convert_remote_url_cookie
|
35
|
+
render pdf: 'convert-remote-url-cookie',
|
36
|
+
url: cookie_endpoint_url(only_path: false),
|
37
|
+
wait_for_page_loaded: false
|
38
|
+
end
|
39
|
+
|
40
|
+
def convert_remote_url_header
|
41
|
+
render pdf: 'convert-remote-url-cookie',
|
42
|
+
url: api_endpoint_url(only_path: false),
|
43
|
+
wait_for_page_loaded: false
|
11
44
|
end
|
12
45
|
end
|