researchable-pdf-renderer 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 46ac20c1bae093f3f5243631635911cb49e775256297f304591de0521f478b3d
4
+ data.tar.gz: bc05f3afacfae35204051d2fa12d09647992d2aa686595c07d8cee39b9a785a4
5
+ SHA512:
6
+ metadata.gz: e95c4240e96569dfbe81c68cb606cead1bc84d11b454885bce647b81b7ce71ac7df979e9ba82166ff376ca576dabe6902e0d78507227e7a9957cadd9b8ce74da
7
+ data.tar.gz: 2dad00551ac0bb379c5f9e3b7a0de62b327ccf1ad1a30d231aaaf4cabfe0f9c3c75d6777d677cf42b9883dcaf6d15aebb0e27278419dca7730d8387a5c6fa60e
@@ -0,0 +1,53 @@
1
+ Gemfile.lock
2
+ gemfiles
3
+
4
+ # rcov generated
5
+ coverage
6
+ coverage.data
7
+
8
+ # rdoc generated
9
+ rdoc
10
+
11
+ # yard generated
12
+ doc
13
+ .yardoc
14
+
15
+ # bundler
16
+ .bundle
17
+
18
+ # jeweler generated
19
+ pkg
20
+
21
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
22
+ #
23
+ # * Create a file at ~/.gitignore
24
+ # * Include files you want ignored
25
+ # * Run: git config --global core.excludesfile ~/.gitignore
26
+ #
27
+ # After doing this, these files will be ignored in all your git projects,
28
+ # saving you from having to 'pollute' every project you touch with them
29
+ #
30
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
31
+ #
32
+ # For MacOS:
33
+ #
34
+ #.DS_Store
35
+
36
+ # For TextMate
37
+ #*.tmproj
38
+ #tmtags
39
+
40
+ # For emacs:
41
+ #*~
42
+ #\#*
43
+ #.\#*
44
+
45
+ # For vim:
46
+ #*.swp
47
+
48
+ # For redcar:
49
+ #.redcar
50
+
51
+ # For rubinius:
52
+ #*.rbc
53
+
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ group :test do
8
+ gem 'guard-rspec'
9
+ end
@@ -0,0 +1,14 @@
1
+ guard :rspec, cmd: "bundle exec rspec" do
2
+ require "guard/rspec/dsl"
3
+ dsl = Guard::RSpec::Dsl.new(self)
4
+
5
+ # RSpec files
6
+ rspec = dsl.rspec
7
+ watch(rspec.spec_helper) { rspec.spec_dir }
8
+ watch(rspec.spec_support) { rspec.spec_dir }
9
+ watch(rspec.spec_files)
10
+
11
+ # Ruby files
12
+ ruby = dsl.ruby
13
+ dsl.watch_spec_files_for(ruby.lib_files)
14
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'researchable/pdf_renderer'
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ require 'active_interaction'
3
+ require 'researchable/pdf_renderer/version'
4
+ require 'researchable/pdf_renderer/sessions'
5
+ require 'researchable/pdf_renderer/endpoint'
6
+ require 'researchable/pdf_renderer/get_pdf'
7
+
8
+ module Researchable
9
+ module PdfRenderer
10
+ end
11
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Researchable
4
+ module PdfRenderer
5
+ # @api private
6
+ class Endpoint < ActiveInteraction::Base
7
+ object :basic_auth_session,
8
+ default: -> { Researchable::PdfRenderer.basic_auth_session },
9
+ class: Researchable::PdfRenderer::Sessions::BasicAuthSession
10
+
11
+ def validate_response_for
12
+ response = yield
13
+ return deal_with_errors if response.parsed_response.is_a?(Hash) && response['errors'].present?
14
+ return deal_with_unprocessable_entity if response.code == 422
15
+ response_to_result response
16
+ end
17
+
18
+ # This function is used so it can be mocked later, and so
19
+ # that it can be specified for each subclass
20
+ def response_to_result(response)
21
+ response
22
+ end
23
+
24
+ private
25
+
26
+ def deal_with_unprocessable_entity
27
+ errors.add :base, 'Validations failed!'
28
+ nil
29
+ end
30
+
31
+ def deal_with_errors
32
+ response['errors'].each do |attribute, attribute_errors|
33
+ attribute_errors.each do |error|
34
+ if respond_to?(attribute.to_sym) || attribute.to_sym == :base
35
+ errors.add attribute.to_sym, error
36
+ else
37
+ errors.add :base, error
38
+ end
39
+ end
40
+ end
41
+ nil
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Researchable
4
+ module PdfRenderer
5
+ require 'tempfile'
6
+ require 'yaml'
7
+ class GetPdf < Endpoint
8
+ string :template
9
+ hash :report_data, strip: false
10
+
11
+ def execute
12
+ template_file = convert_to_file('template.md', template)
13
+ report_data_file = convert_to_file('report_data.yml', report_data.to_yaml)
14
+
15
+ result = validate_response_for do
16
+ basic_auth_session.post(
17
+ '/render',
18
+ template: template_file,
19
+ report_data: report_data_file
20
+ )
21
+ end
22
+ report_data_file.close
23
+ template_file.close
24
+ result
25
+ end
26
+
27
+ private
28
+
29
+ def convert_to_file(filename, data)
30
+ data_file = Tempfile.new(filename)
31
+ data_file.write(data)
32
+ data_file.rewind
33
+ data_file
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ require 'httparty'
2
+ require 'researchable/pdf_renderer/sessions/basic_auth_session'
3
+
4
+ module Researchable
5
+ module PdfRenderer
6
+ def self.basic_auth_session(*arguments, &block)
7
+ Sessions::BasicAuthSession.new(*arguments, &block)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Researchable
4
+ module PdfRenderer
5
+ module Sessions
6
+ class BasicAuthSession
7
+ attr_reader :api_url
8
+ attr_reader :username
9
+ attr_reader :password
10
+
11
+ def initialize(api_url: ENV['SVC_PDFRENDERER_URL'],
12
+ username: ENV['SVC_PDFRENDERER_BASICAUTH_ID'],
13
+ password: ENV['SVC_PDFRENDERER_BASICAUTH_SECRET'])
14
+ @api_url = api_url
15
+ @username = username
16
+ @password = password
17
+ end
18
+
19
+ def get(path, params = {})
20
+ perform_request_or_fail do
21
+ HTTParty.get(full_url_for(path),
22
+ query: params,
23
+ basic_auth: basic_auth)
24
+ end
25
+ end
26
+
27
+ def post(path, params = {})
28
+ perform_request_or_fail do
29
+ HTTParty.post(full_url_for(path),
30
+ #headers: { 'Content-Type' => 'multip' },
31
+ multipart: true,
32
+ body: params,
33
+ basic_auth: basic_auth)
34
+ end
35
+ end
36
+
37
+ def patch(path, params = {})
38
+ perform_request_or_fail do
39
+ HTTParty.patch(full_url_for(path),
40
+ headers: { 'Content-Type' => 'application/json' },
41
+ body: params.to_json,
42
+ basic_auth: basic_auth)
43
+ end
44
+ end
45
+
46
+ def delete(path, params = {})
47
+ perform_request_or_fail do
48
+ HTTParty.delete(full_url_for(path),
49
+ query: params,
50
+ basic_auth: basic_auth)
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def perform_request_or_fail
57
+ response = yield
58
+ case response.code
59
+ when 200..299, 422
60
+ response
61
+ when 401
62
+ access_denied(response)
63
+ else
64
+ raise response.parsed_response || "Received HTTP response code #{response.code}!"
65
+ end
66
+ end
67
+
68
+ def access_denied(response)
69
+ if response.headers['WWW-Authenticate']
70
+ raise NoSession
71
+ else
72
+ raise Unauthorized
73
+ end
74
+ end
75
+
76
+ def full_url_for(path)
77
+ api_url + api_base + path
78
+ end
79
+
80
+ def api_base
81
+ '/api/v1'
82
+ end
83
+
84
+ def basic_auth
85
+ { username: username, password: password }
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Researchable
4
+ module PdfRenderer
5
+ VERSION = '0.0.1'
6
+ end
7
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path('lib/researchable/pdf_renderer/version', __dir__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = 'researchable-pdf-renderer'
7
+ gem.version = Researchable::PdfRenderer::VERSION
8
+ gem.license = 'Unlicensed'
9
+ gem.summary = 'Gem for rendering result PDFs'
10
+ gem.authors = ['Frank Blaauw']
11
+ gem.email = 'pdf-renderer@researchable.nl'
12
+
13
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
14
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
15
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
+ gem.require_paths = ['lib']
17
+
18
+ gem.add_dependency 'active_interaction'
19
+ gem.add_dependency 'httparty'
20
+
21
+ gem.add_development_dependency 'appraisal'
22
+ gem.add_development_dependency 'bundler'
23
+ gem.add_development_dependency 'rake'
24
+ gem.add_development_dependency 'rspec'
25
+ gem.add_development_dependency 'yard'
26
+ gem.add_development_dependency 'factory_bot'
27
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :basic_auth_session do
5
+ initialize_with { new(username: 'some_username',
6
+ password: 'some_password',
7
+ api_url: 'http://svc-pdfrenderer') }
8
+ end
9
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe Endpoint do
4
+ class TestEndpoint < Endpoint
5
+ object :response, class: RSpec::Mocks::Double
6
+
7
+ def execute
8
+ validate_response_for do
9
+ response
10
+ end
11
+ end
12
+
13
+ def response_to_result(response)
14
+ response['key']
15
+ end
16
+ end
17
+
18
+ describe '#initialize' do
19
+ it 'calls PdfRenderer.basic_auth_session to fill in basic_auth_session if the basic_auth_session is not passed in' do
20
+ bas_double = Researchable::PdfRenderer::Sessions::BasicAuthSession.new # doubles dont pass AI default type checking
21
+ allow(Researchable::PdfRenderer).to receive(:basic_auth_session).and_return bas_double
22
+ endpoint = TestEndpoint.new
23
+ expect(endpoint.instance_variable_get :@basic_auth_session).to eq(bas_double)
24
+ end
25
+
26
+ it 'sets basic_auth_session to the passed basic_auth_session' do
27
+ bas_double = double("BasicAuthSession")
28
+ endpoint = TestEndpoint.new(basic_auth_session: bas_double)
29
+ expect(endpoint.instance_variable_get :@basic_auth_session).to eq(bas_double)
30
+ end
31
+ end
32
+
33
+ describe 'with validations' do
34
+ let(:response) do
35
+ double('httparty_repsonse', code: 200, parsed_response: {}).tap do |response|
36
+ allow(response).to receive(:[]).with('key').and_return ['values']
37
+ allow(response).to receive(:[]).with('errors').and_return []
38
+ end
39
+ end
40
+
41
+ it 'returns the parsed response' do
42
+ outcome = TestEndpoint.run response: response
43
+ expect(outcome.result).to eq(['values'])
44
+ end
45
+
46
+ it 'is valid' do
47
+ result = TestEndpoint.run response: response
48
+ expect(result).to be_valid
49
+ end
50
+
51
+ context 'when the response contains errors' do
52
+ before do
53
+ allow(response).to receive(:[]).with('errors').and_return('base' => ['Some annoying error'])
54
+ end
55
+
56
+ it 'adds the errors' do
57
+ outcome = TestEndpoint.run response: response
58
+ expect(outcome.errors.full_messages.first).to match('Some annoying error')
59
+ end
60
+
61
+ it 'marks the outcome as invalid' do
62
+ outcome = TestEndpoint.run response: response
63
+ expect(outcome).not_to be_valid
64
+ end
65
+
66
+ it 'does not return a result' do
67
+ outcome = TestEndpoint.run response: response
68
+ expect(outcome.result).to be_nil
69
+ end
70
+ end
71
+
72
+ context 'when the response code is 422 but the response contains no errors' do
73
+ it 'adds an error to base' do
74
+ allow(response).to receive(:code).and_return 422
75
+ outcome = TestEndpoint.run response: response
76
+ expect(outcome).not_to be_valid
77
+ expect(outcome.errors.full_messages).to include('Validations failed!')
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe GetPdf do
6
+ let(:options) { { content: '# Some markdown' } }
7
+ let(:api_path) { '/render' }
8
+ let(:response) do
9
+ httparty_response \
10
+ 'filename' => 'filename.pdf',
11
+ 'binary_content' => 'binarycontent'
12
+ end
13
+ let(:session) { FactoryBot.build(:basic_auth_session) }
14
+
15
+ before { allow(BasicAuthSession).to receive(:new).and_return session }
16
+
17
+ it 'returns a respondent object' do
18
+ expect(session).to receive(:post).with(api_path, options).and_return response
19
+ result = GetPdf.run(options).result.parsed_response
20
+
21
+ expect(result['filename']).to eq 'filename.pdf'
22
+ expect(result['binary_content']).to eq 'binarycontent'
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Researchable::PdfRenderer do
4
+ it "should have a VERSION constant" do
5
+ expect(subject.const_get('VERSION')).to_not be_empty
6
+ end
7
+ end
@@ -0,0 +1,52 @@
1
+ require 'rspec'
2
+ require 'factory_bot'
3
+ require 'researchable-pdf-renderer'
4
+
5
+ include Researchable::PdfRenderer
6
+ include Researchable::PdfRenderer::Sessions
7
+
8
+
9
+ Dir[File.join(File.expand_path(File.dirname(__FILE__)), 'support', '*.rb')].each {|f| require f}
10
+ Dir[File.join(File.expand_path(File.dirname(__FILE__)), 'factories', '*.rb')].each {|f| require f}
11
+
12
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
13
+ RSpec.configure do |config|
14
+ # rspec-expectations config goes here. You can use an alternate
15
+ # assertion/expectation library such as wrong or the stdlib/minitest
16
+ # assertions if you prefer.
17
+ config.expect_with :rspec do |expectations|
18
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
19
+ end
20
+
21
+ # rspec-mocks config goes here. You can use an alternate test double
22
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
23
+ config.mock_with :rspec do |mocks|
24
+ # Prevents you from mocking or stubbing a method that does not exist on
25
+ # a real object. This is generally recommended, and will default to
26
+ # `true` in RSpec 4.
27
+ mocks.verify_partial_doubles = true
28
+ end
29
+
30
+ config.shared_context_metadata_behavior = :apply_to_host_groups
31
+ config.warnings = true
32
+
33
+ # Print the 10 slowest examples and example groups at the
34
+ # end of the spec run, to help surface which specs are running
35
+ # particularly slow.
36
+ config.profile_examples = 1
37
+
38
+ # Run specs in random order to surface order dependencies. If you find an
39
+ # order dependency and want to debug it, you can fix the order by providing
40
+ # the seed, which is printed after each run.
41
+ # --seed 1234
42
+ config.order = :random
43
+
44
+ # Seed global randomization in this process using the `--seed` CLI option.
45
+ # Setting this allows you to use `--seed` to deterministically reproduce
46
+ # test failures related to randomization by passing the same `--seed` value
47
+ # as the one that triggered the failure.
48
+ Kernel.srand config.seed
49
+
50
+ config.filter_run focus: true
51
+ config.run_all_when_everything_filtered = true
52
+ end
@@ -0,0 +1,8 @@
1
+ def httparty_response(response_data)
2
+ response = double('reponse')
3
+ allow(response).to receive(:[]).and_return []
4
+ allow(response).to receive(:body).and_return response_data
5
+ allow(response).to receive(:code).and_return 200
6
+ allow(response).to receive(:parsed_response).and_return response_data
7
+ response
8
+ end
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: researchable-pdf-renderer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Frank Blaauw
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-06-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: active_interaction
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: httparty
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: appraisal
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: yard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: factory_bot
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description:
126
+ email: pdf-renderer@researchable.nl
127
+ executables: []
128
+ extensions: []
129
+ extra_rdoc_files: []
130
+ files:
131
+ - ".gitignore"
132
+ - Gemfile
133
+ - Guardfile
134
+ - lib/researchable-pdf-renderer.rb
135
+ - lib/researchable/pdf_renderer.rb
136
+ - lib/researchable/pdf_renderer/endpoint.rb
137
+ - lib/researchable/pdf_renderer/get_pdf.rb
138
+ - lib/researchable/pdf_renderer/sessions.rb
139
+ - lib/researchable/pdf_renderer/sessions/basic_auth_session.rb
140
+ - lib/researchable/pdf_renderer/version.rb
141
+ - researchable_pdf_renderer.gemspec
142
+ - spec/factories/basic_auth_sessions.rb
143
+ - spec/lib/researchable/pdf_renderer/endpoint_spec.rb
144
+ - spec/lib/researchable/pdf_renderer/get_pdf_spec.rb
145
+ - spec/lib/researchable_pdf_renderer_spec.rb
146
+ - spec/spec_helper.rb
147
+ - spec/support/httparty_response.rb
148
+ homepage:
149
+ licenses:
150
+ - Unlicensed
151
+ metadata: {}
152
+ post_install_message:
153
+ rdoc_options: []
154
+ require_paths:
155
+ - lib
156
+ required_ruby_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ required_rubygems_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ requirements: []
167
+ rubygems_version: 3.0.8
168
+ signing_key:
169
+ specification_version: 4
170
+ summary: Gem for rendering result PDFs
171
+ test_files:
172
+ - spec/factories/basic_auth_sessions.rb
173
+ - spec/lib/researchable/pdf_renderer/endpoint_spec.rb
174
+ - spec/lib/researchable/pdf_renderer/get_pdf_spec.rb
175
+ - spec/lib/researchable_pdf_renderer_spec.rb
176
+ - spec/spec_helper.rb
177
+ - spec/support/httparty_response.rb