alephant-publisher-request 0.0.2
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 +7 -0
- data/.gitignore +23 -0
- data/.travis.yml +12 -0
- data/Gemfile +4 -0
- data/Guardfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +190 -0
- data/Rakefile +5 -0
- data/alephant-publisher-request.gemspec +38 -0
- data/lib/alephant/publisher/request.rb +75 -0
- data/lib/alephant/publisher/request/data_mapper.rb +35 -0
- data/lib/alephant/publisher/request/data_mapper_factory.rb +43 -0
- data/lib/alephant/publisher/request/error.rb +15 -0
- data/lib/alephant/publisher/request/processor.rb +34 -0
- data/lib/alephant/publisher/request/processor/base.rb +11 -0
- data/lib/alephant/publisher/request/version.rb +7 -0
- data/spec/data_mapper_factory_spec.rb +44 -0
- data/spec/data_mapper_spec.rb +41 -0
- data/spec/fixtures/components/foo/mapper.rb +6 -0
- data/spec/fixtures/components/foo/models/foo.rb +9 -0
- data/spec/fixtures/components/foo/templates/foo.mustache +1 -0
- data/spec/fixtures/components/invalid/mapper.rb +4 -0
- data/spec/integration/rack_server_spec.rb +62 -0
- data/spec/integration/spec_helper.rb +5 -0
- data/spec/not_implemented_mapper_spec.rb +19 -0
- data/spec/processor_spec.rb +25 -0
- data/spec/request_spec.rb +15 -0
- data/spec/spec_helper.rb +11 -0
- metadata +307 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9cfd32b39a5fcdd4d15d0d24925f861614a57bde
|
4
|
+
data.tar.gz: e8fb3770ab80f2bcd4b8e972909c23d03d20b449
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e2576063f21c41344fb0b74bbc32192fa7e6585e5f0a58212bff778c215cfad2da400ccf30f1ab36ae2bfb0617ca5c7e9ea580cd2dc0ce8276f63b5dce299332
|
7
|
+
data.tar.gz: 33794b02c484149ac16b821b65b026fd199ab3054d2feb7df3c9928ca442e4c01b86923169cb221297a497aa26d7e2a40e8ebc069931bc490d435adc7f881cca
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
.rspec
|
19
|
+
.ruby-version
|
20
|
+
/config/*.yaml
|
21
|
+
/config/*.yml
|
22
|
+
/components
|
23
|
+
*.swp
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 BBC News
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
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:
|
12
|
+
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
# Alephant::Publisher::Request
|
2
|
+
|
3
|
+
Dynamic publishing based on data from an API.
|
4
|
+
|
5
|
+
[](https://travis-ci.org/BBC-News/alephant-publisher-request) [](https://gemnasium.com/BBC-News/alephant-publisher-request) [](http://badge.fury.io/rb/alephant-publisher-request)
|
6
|
+
|
7
|
+
## Dependencies
|
8
|
+
|
9
|
+
- JRuby 1.7.8
|
10
|
+
|
11
|
+
## Migrating from [Alephant::Publisher](https://github.com/BBC-News/alephant-publisher)
|
12
|
+
|
13
|
+
1. Add the new gem in your Gemfile (`gem 'alephant-publisher-request'`).
|
14
|
+
2. Run `bundle install`.
|
15
|
+
3. Require the new gem in your app (`require 'alephant/publisher/queue'`).
|
16
|
+
4. Note that the namespace has changed from `Alephant::Publisher` to `Alephant::Publisher::Request`.
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add this line to your application's Gemfile:
|
21
|
+
|
22
|
+
gem 'alephant-publisher-request'
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
$ bundle
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
|
30
|
+
$ gem install alephant-publisher-request
|
31
|
+
|
32
|
+
## Setup
|
33
|
+
|
34
|
+
You need to run the gem as a rack application, and pass it the nessasary dependencies. Below is a simple example of the minimum needed to run the server:
|
35
|
+
|
36
|
+
### Folder structure
|
37
|
+
|
38
|
+
```bash
|
39
|
+
src
|
40
|
+
├── components
|
41
|
+
│ ├── my_test_component
|
42
|
+
│ │ ├── fixtures
|
43
|
+
│ │ │ ├── responsive.json
|
44
|
+
│ │ ├── templates
|
45
|
+
│ │ │ ├── my_test_component.mustache
|
46
|
+
│ │ ├── models
|
47
|
+
│ │ │ ├── my_test_component.rb
|
48
|
+
│ │ ├── mapper.rb
|
49
|
+
```
|
50
|
+
|
51
|
+
#### Model example
|
52
|
+
|
53
|
+
The model must extend either the JSON or HTML base from the [alephant-renderer](https://www.github.com/BBC-News/alephant-renderer) `Alephant::Renderer::Views::Html` or `Alephant::Renderer::Views::Json`.
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
require 'alephant/renderer/views/html'
|
57
|
+
|
58
|
+
module SomeNameSpace
|
59
|
+
class MyTestComponent Alephant::Renderer::Views::Html
|
60
|
+
|
61
|
+
def some_method
|
62
|
+
@body.some_method
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
#### Data mapper
|
70
|
+
|
71
|
+
When getting data from an API, we use data mappers that give us access to the query string parameters allowing us to construct the correct API path.
|
72
|
+
|
73
|
+
An example of a mapper for an example component would be:
|
74
|
+
|
75
|
+
> components/my_test_component/mapper.rb
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
class MyTestComponentMapper < Alephant::Publisher::Request::DataMapper
|
79
|
+
def data
|
80
|
+
#we have a variable 'context' available that gives us access to the query string params passed to the request.
|
81
|
+
#There's a 'get' method available that is passed the constructed API uri and will return the parsed JSON data.
|
82
|
+
get "/some/endpoint/#{context['qs_param']}"
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
### Application
|
89
|
+
|
90
|
+
> config.ru
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
require 'lib/application'
|
94
|
+
run Application.create
|
95
|
+
```
|
96
|
+
|
97
|
+
> lib/application.rb
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
module Application
|
101
|
+
|
102
|
+
def self.create
|
103
|
+
Alephant::Publisher::Request.create(processor, data_mapper_factory)
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.processor
|
107
|
+
Alephant::Publisher::Request::Processor.new(base_path)
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.base_path
|
111
|
+
File.absolute_path(File.join(File.dirname(__FILE__), '..', 'components'))
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.data_mapper_factory
|
115
|
+
Alephant::Publisher::Request::DataMapperFactory.new(connection, base_path)
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.connection
|
119
|
+
Faraday.new(:url => 'http://www.some-api-endpoint.com')
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
## Usage
|
126
|
+
|
127
|
+
The server is a simple rack server, so you just need to run:
|
128
|
+
|
129
|
+
```bash
|
130
|
+
$: rackup
|
131
|
+
```
|
132
|
+
|
133
|
+
in the `src` directory where the `config.ru` file is.
|
134
|
+
|
135
|
+
To view components, use the name (the name of the component in the components folder) and any params in the browser:
|
136
|
+
|
137
|
+
```bash
|
138
|
+
curl http://localhost:9292/my_test_component?some=param
|
139
|
+
```
|
140
|
+
|
141
|
+
## Preview Server
|
142
|
+
|
143
|
+
`alephant preview`
|
144
|
+
|
145
|
+
The included preview server allows you to see the html generated by your
|
146
|
+
templates, both standalone and in the context of a page.
|
147
|
+
|
148
|
+
**Standalone**
|
149
|
+
|
150
|
+
`/component/:id/?:fixture?`
|
151
|
+
|
152
|
+
### Full page preview
|
153
|
+
|
154
|
+
When viewing the component in the context of a page, you'll need to retrieve a
|
155
|
+
mustache template to provide the page context.
|
156
|
+
|
157
|
+
When performing an update a regex is applied to replace the static hostnames in
|
158
|
+
the retrieved html.
|
159
|
+
|
160
|
+
**Environment Variables**
|
161
|
+
|
162
|
+
```sh
|
163
|
+
STATIC_HOST_REGEX="static.(sandbox.dev|int|test|stage|live).yourapp(i)?.com\/"
|
164
|
+
PREVIEW_TEMPLATE_URL="http://yourapp.com/template"
|
165
|
+
```
|
166
|
+
|
167
|
+
**Example Remote Template**
|
168
|
+
|
169
|
+
`id` is the component/folder name
|
170
|
+
|
171
|
+
`template` is the mustache template file name
|
172
|
+
|
173
|
+
`location_in_page` should be something like (for example) `page_head` (specified within a `preview.mustache` file that the consuming application needs to create).
|
174
|
+
|
175
|
+
- `http://localhost:4567/component/id/template`
|
176
|
+
- `http://localhost:4567/preview/id/template/location_in_page`
|
177
|
+
|
178
|
+
`alephant update`
|
179
|
+
|
180
|
+
**In page**
|
181
|
+
|
182
|
+
`/preview/:id/:region/?:fixture?`
|
183
|
+
|
184
|
+
## Contributing
|
185
|
+
|
186
|
+
1. [Fork it!](http://github.com/BBC-News/alephant-publisher-request/fork)
|
187
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
188
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
189
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
190
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'alephant/publisher/request/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "alephant-publisher-request"
|
8
|
+
spec.version = Alephant::Publisher::Request::VERSION
|
9
|
+
spec.authors = ["Integralist"]
|
10
|
+
spec.email = ["mark.mcdx@gmail.com"]
|
11
|
+
spec.summary = "..."
|
12
|
+
spec.description = "..."
|
13
|
+
spec.homepage = "https://github.com/BBC-News/alephant-publisher-request"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rspec"
|
23
|
+
spec.add_development_dependency "rspec-nc"
|
24
|
+
spec.add_development_dependency "guard"
|
25
|
+
spec.add_development_dependency "guard-rake"
|
26
|
+
spec.add_development_dependency "pry"
|
27
|
+
spec.add_development_dependency "rack-test"
|
28
|
+
spec.add_development_dependency "spurious-ruby-awssdk-helper"
|
29
|
+
spec.add_development_dependency "rake-rspec"
|
30
|
+
|
31
|
+
spec.add_runtime_dependency 'rack'
|
32
|
+
spec.add_runtime_dependency 'rake'
|
33
|
+
spec.add_runtime_dependency 'faraday'
|
34
|
+
spec.add_runtime_dependency 'aws-sdk', '~> 1.0'
|
35
|
+
spec.add_runtime_dependency 'mustache', '>= 0.99.5'
|
36
|
+
spec.add_runtime_dependency 'alephant-logger'
|
37
|
+
spec.add_runtime_dependency 'alephant-renderer'
|
38
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'alephant/logger'
|
2
|
+
require 'alephant/publisher/request/version'
|
3
|
+
require 'alephant/publisher/request/processor'
|
4
|
+
require 'rack/request'
|
5
|
+
require 'rack/response'
|
6
|
+
require 'alephant/publisher/request/error'
|
7
|
+
|
8
|
+
module Alephant
|
9
|
+
module Publisher
|
10
|
+
module Request
|
11
|
+
include Logger
|
12
|
+
|
13
|
+
def self.create(processor, data_mapper_factory, opts = {})
|
14
|
+
Request.new(processor, data_mapper_factory, opts)
|
15
|
+
end
|
16
|
+
|
17
|
+
class Request
|
18
|
+
attr_reader :processor, :data_mapper_factory, :opts
|
19
|
+
|
20
|
+
DEFAULT_CONTENT_TYPE = { "Content-Type" => "text/html" }
|
21
|
+
|
22
|
+
def initialize(processor, data_mapper_factory, opts)
|
23
|
+
@processor = processor
|
24
|
+
@data_mapper_factory = data_mapper_factory
|
25
|
+
@opts = opts
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(env)
|
29
|
+
req = Rack::Request.new(env)
|
30
|
+
response = Rack::Response.new("<h1>Not Found</h1>", 404, DEFAULT_CONTENT_TYPE)
|
31
|
+
|
32
|
+
case req.path_info
|
33
|
+
when /status$/
|
34
|
+
response = Rack::Response.new('', 204, DEFAULT_CONTENT_TYPE)
|
35
|
+
when /component\/(?<id>[^\/]+)$/
|
36
|
+
response = Rack::Response.new(
|
37
|
+
template_data($~['id'], req.params),
|
38
|
+
200,
|
39
|
+
DEFAULT_CONTENT_TYPE
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
response.finish
|
44
|
+
rescue ApiError, ConnectionFailed => e
|
45
|
+
error_response(e, 502)
|
46
|
+
rescue InvalidComponent => e
|
47
|
+
error_response(e, 404)
|
48
|
+
rescue Exception => e
|
49
|
+
error_response e
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
|
54
|
+
def render_component(component_id, params)
|
55
|
+
Rack::Response.new(
|
56
|
+
template_data(component_id, params),
|
57
|
+
200,
|
58
|
+
{ "Content-Type" => "text/html" }
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
def template_data(component_id, params)
|
63
|
+
mapper = data_mapper_factory.create(component_id, params)
|
64
|
+
processor.consume(mapper.data, component_id)
|
65
|
+
end
|
66
|
+
|
67
|
+
def error_response(e = '', code = 500)
|
68
|
+
message = opts.fetch(:debug, false) ? e.message : ''
|
69
|
+
Rack::Response.new(message, code, DEFAULT_CONTENT_TYPE).finish
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Alephant
|
4
|
+
module Publisher
|
5
|
+
module Request
|
6
|
+
class DataMapper
|
7
|
+
attr_reader :connection, :context
|
8
|
+
|
9
|
+
def initialize(connection, context = {})
|
10
|
+
@connection = connection
|
11
|
+
@context = context
|
12
|
+
end
|
13
|
+
|
14
|
+
def data
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def get(uri)
|
21
|
+
response = connection.get(uri)
|
22
|
+
raise InvalidApiResponse, "Status: #{response.status}" unless response.status == 200
|
23
|
+
JSON::parse(response.body, :symbolize_names => true)
|
24
|
+
rescue Faraday::ConnectionFailed
|
25
|
+
raise ConnectionFailed
|
26
|
+
rescue JSON::ParserError
|
27
|
+
raise InvalidApiResponse, "JSON parsing error: #{response.body}"
|
28
|
+
rescue StandardError => e
|
29
|
+
raise ApiError, e.message
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Alephant
|
2
|
+
module Publisher
|
3
|
+
module Request
|
4
|
+
class DataMapperFactory
|
5
|
+
attr_reader :connection, :base_path
|
6
|
+
|
7
|
+
def initialize(connection, base_path)
|
8
|
+
@connection = connection
|
9
|
+
@base_path = base_path
|
10
|
+
raise InvalidComponentBasePath, base_path unless File.directory? base_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def create(component_id, context = {})
|
14
|
+
require base_path_for component_id
|
15
|
+
|
16
|
+
klass = mapper_class_for(component_id)
|
17
|
+
klass.new(connection, context)
|
18
|
+
rescue LoadError
|
19
|
+
raise InvalidComponentName, "Invalid component name: #{component_id}"
|
20
|
+
rescue NameError
|
21
|
+
raise InvalidComponentClassName, "Invalid class name #{klass}"
|
22
|
+
rescue
|
23
|
+
raise InvalidComponent, "Name: #{component_id}, Class: #{klass}"
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def base_path_for(component_id)
|
29
|
+
"#{base_path}/#{component_id}/mapper"
|
30
|
+
end
|
31
|
+
|
32
|
+
def camalize(snake_case)
|
33
|
+
"#{snake_case.split('_').collect(&:capitalize).join}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def mapper_class_for(component_id)
|
37
|
+
Object.const_get("#{camalize component_id}Mapper")
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Alephant
|
2
|
+
module Publisher
|
3
|
+
module Request
|
4
|
+
class Error < StandardError; end
|
5
|
+
class ConnectionFailed < Error; end
|
6
|
+
class InvalidComponent < Error; end
|
7
|
+
class ApiError < Error; end
|
8
|
+
class InvalidApiResponse < ApiError; end
|
9
|
+
class InvalidComponentBasePath < InvalidComponent; end
|
10
|
+
class InvalidComponentName < InvalidComponent; end
|
11
|
+
class InvalidComponentClassName < InvalidComponent; end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'alephant/publisher/request/processor/base'
|
2
|
+
|
3
|
+
module Alephant
|
4
|
+
module Publisher
|
5
|
+
module Request
|
6
|
+
class Processor < BaseProcessor
|
7
|
+
attr_reader :base_path
|
8
|
+
|
9
|
+
def initialize(base_path)
|
10
|
+
@base_path = base_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def consume(data, component)
|
14
|
+
config = config_for component
|
15
|
+
renderer_for(config, data).views[component].render
|
16
|
+
end
|
17
|
+
|
18
|
+
def renderer_for(config, data)
|
19
|
+
Alephant::Renderer.create(config, data)
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def config_for(component)
|
25
|
+
{
|
26
|
+
:renderer_id => component,
|
27
|
+
:view_path => base_path
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Alephant::Publisher::Request::DataMapperFactory do
|
4
|
+
let (:connection) { instance_double(Faraday::Connection) }
|
5
|
+
let (:base_path) { File.join(File.dirname(__FILE__), 'fixtures', 'components') }
|
6
|
+
|
7
|
+
subject { described_class.new(connection, base_path) }
|
8
|
+
|
9
|
+
describe ".create" do
|
10
|
+
let (:context) do
|
11
|
+
{
|
12
|
+
:foo => :bar
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
context "using valid parameters" do
|
17
|
+
let (:component_id) { 'foo' }
|
18
|
+
let (:expected) { FooMapper }
|
19
|
+
|
20
|
+
specify { expect(subject.create(component_id, context)).to be_a expected }
|
21
|
+
end
|
22
|
+
|
23
|
+
context "using invalid path" do
|
24
|
+
let (:base_path) { File.join(File.dirname(__FILE__), 'non_existent_path') }
|
25
|
+
let (:expected_exception) { Alephant::Publisher::Request::InvalidComponentBasePath }
|
26
|
+
|
27
|
+
specify { expect{ subject.create(component_id, context) }.to raise_error expected_exception }
|
28
|
+
end
|
29
|
+
|
30
|
+
context "using invalid component name" do
|
31
|
+
let (:component_id) { 'bar' }
|
32
|
+
let (:expected_exception) { Alephant::Publisher::Request::InvalidComponentName }
|
33
|
+
|
34
|
+
specify { expect{ subject.create(component_id, context) }.to raise_error expected_exception }
|
35
|
+
end
|
36
|
+
|
37
|
+
context "using invalid class name" do
|
38
|
+
let (:component_id) { 'invalid' }
|
39
|
+
let (:expected_exception) { Alephant::Publisher::Request::InvalidComponentClassName }
|
40
|
+
|
41
|
+
specify { expect{ subject.create(component_id, context) }.to raise_error expected_exception }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe FooMapper do
|
4
|
+
let (:connection) { instance_double(Faraday::Connection, :get => nil) }
|
5
|
+
let (:context) do
|
6
|
+
{
|
7
|
+
:foo => :bar
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
subject { described_class.new(connection, context) }
|
12
|
+
|
13
|
+
describe "#data" do
|
14
|
+
let (:expected_raw_data) do
|
15
|
+
{
|
16
|
+
:some => 'data'
|
17
|
+
}
|
18
|
+
end
|
19
|
+
let (:expected_data) { JSON::generate(expected_raw_data, :symbolize_names => true) }
|
20
|
+
let (:expected_response) { instance_double(Faraday::Response, :body => expected_data, :status => 200) }
|
21
|
+
|
22
|
+
context "with a valid endpoint" do
|
23
|
+
before(:each) do
|
24
|
+
allow(connection).to receive(:get).with("/some/test/endpoint/#{context.values[0]}").and_return(expected_response)
|
25
|
+
end
|
26
|
+
|
27
|
+
specify { expect(subject.data).to eq expected_raw_data }
|
28
|
+
end
|
29
|
+
|
30
|
+
context "invalid hostname" do
|
31
|
+
let (:expected_exception) { Alephant::Publisher::Request::ConnectionFailed }
|
32
|
+
let (:faraday_exception) { Faraday::ConnectionFailed.new(StandardError) }
|
33
|
+
|
34
|
+
before(:each) do
|
35
|
+
allow(connection).to receive(:get).and_raise(faraday_exception)
|
36
|
+
end
|
37
|
+
|
38
|
+
specify { expect{ subject.data }.to raise_error expected_exception }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
{{content}}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require_relative "./spec_helper"
|
2
|
+
|
3
|
+
describe Alephant::Publisher::Request do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
let (:response) { instance_double(Faraday::Response, :status => 200, :body => nil) }
|
6
|
+
let (:base_path) { File.join(File.dirname(__FILE__), '..', 'fixtures', 'components') }
|
7
|
+
let (:processor) { Alephant::Publisher::Request::Processor.new(base_path) }
|
8
|
+
let (:connection) { instance_double(Faraday::Connection, :get => response) }
|
9
|
+
let (:data_mapper_factory) { Alephant::Publisher::Request::DataMapperFactory.new(connection, base_path) }
|
10
|
+
let (:app) { subject.create(processor, data_mapper_factory, { :debug => true }) }
|
11
|
+
|
12
|
+
describe "status endpoint (/status)" do
|
13
|
+
before(:each) do
|
14
|
+
get "/status"
|
15
|
+
end
|
16
|
+
|
17
|
+
context "status code" do
|
18
|
+
specify { expect(last_response.status).to eq 204 }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "component endpoint (/component/{component_id}?foo=bar)" do
|
23
|
+
let (:component_id) { "foo" }
|
24
|
+
|
25
|
+
describe "content" do
|
26
|
+
|
27
|
+
context "with a valid component id" do
|
28
|
+
let (:api_response) { "{\"content\":\"#{component_id}\"}" }
|
29
|
+
before(:each) do
|
30
|
+
allow(response).to receive(:body).and_return(api_response)
|
31
|
+
get "/component/#{component_id}"
|
32
|
+
end
|
33
|
+
|
34
|
+
specify { expect(last_response.body.chomp).to eq component_id }
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "status code" do
|
40
|
+
|
41
|
+
context "with an invalid component id" do
|
42
|
+
let (:component_id) { "foo_invalid" }
|
43
|
+
before(:each) do
|
44
|
+
get "/component/#{component_id}"
|
45
|
+
end
|
46
|
+
|
47
|
+
specify { expect(last_response.status).to eq 404 }
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with an invalid API endpoint" do
|
51
|
+
let (:expected_exception) { Alephant::Publisher::Request::InvalidApiResponse }
|
52
|
+
before(:each) do
|
53
|
+
allow(connection).to receive(:get).and_raise expected_exception
|
54
|
+
get "/component/#{component_id}"
|
55
|
+
end
|
56
|
+
|
57
|
+
specify { expect(last_response.status).to eq 502 }
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Alephant::Publisher::Request::DataMapper do
|
4
|
+
let (:context) { { :key => :value } }
|
5
|
+
let (:connection) { instance_double(Faraday) }
|
6
|
+
|
7
|
+
subject { Alephant::Publisher::Request::DataMapper.new(connection, context) }
|
8
|
+
|
9
|
+
describe ".new" do
|
10
|
+
specify { expect(subject).to be_a Alephant::Publisher::Request::DataMapper }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#data" do
|
14
|
+
context "not overridden" do
|
15
|
+
specify { expect { subject.data } .to raise_error NotImplementedError }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Alephant::Publisher::Request::Processor do
|
4
|
+
let (:base_path) { File.join(File.dirname(__FILE__), 'fixtures', 'components') }
|
5
|
+
subject { described_class.new(base_path) }
|
6
|
+
|
7
|
+
describe ".new" do
|
8
|
+
specify { expect(subject).to be_a described_class }
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#consume" do
|
12
|
+
let (:data) do
|
13
|
+
{
|
14
|
+
:content => "Foo Bar"
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
context "using valid data" do
|
19
|
+
let (:component) { "foo" }
|
20
|
+
|
21
|
+
specify { expect(subject.consume(data, component)).to eq "#{data.values.first}\n" }
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Alephant::Publisher::Request do
|
4
|
+
let (:processor) { instance_double(Alephant::Publisher::Request::Processor, :consume => nil) }
|
5
|
+
let (:data_mapper_factory) { instance_double(Alephant::Publisher::Request::DataMapperFactory, :create => nil) }
|
6
|
+
|
7
|
+
describe ".create" do
|
8
|
+
context "Using valid params" do
|
9
|
+
let (:expected) { Alephant::Publisher::Request::Request }
|
10
|
+
|
11
|
+
specify { expect(subject.create(processor, data_mapper_factory)).to be_a expected }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'pry'
|
2
|
+
|
3
|
+
require 'aws-sdk'
|
4
|
+
require 'faraday'
|
5
|
+
require 'alephant/renderer'
|
6
|
+
require 'alephant/publisher/request'
|
7
|
+
require 'alephant/publisher/request/error'
|
8
|
+
require 'alephant/publisher/request/data_mapper'
|
9
|
+
require 'alephant/publisher/request/data_mapper_factory'
|
10
|
+
|
11
|
+
require_relative './fixtures/components/foo/mapper'
|
metadata
ADDED
@@ -0,0 +1,307 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: alephant-publisher-request
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Integralist
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
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: rspec-nc
|
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: guard
|
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: guard-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: pry
|
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: rack-test
|
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: spurious-ruby-awssdk-helper
|
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
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rake-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rack
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rake
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: faraday
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - '>='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: aws-sdk
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ~>
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '1.0'
|
188
|
+
type: :runtime
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ~>
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '1.0'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: mustache
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - '>='
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: 0.99.5
|
202
|
+
type: :runtime
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - '>='
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: 0.99.5
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: alephant-logger
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - '>='
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '0'
|
216
|
+
type: :runtime
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - '>='
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
223
|
+
- !ruby/object:Gem::Dependency
|
224
|
+
name: alephant-renderer
|
225
|
+
requirement: !ruby/object:Gem::Requirement
|
226
|
+
requirements:
|
227
|
+
- - '>='
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '0'
|
230
|
+
type: :runtime
|
231
|
+
prerelease: false
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
requirements:
|
234
|
+
- - '>='
|
235
|
+
- !ruby/object:Gem::Version
|
236
|
+
version: '0'
|
237
|
+
description: '...'
|
238
|
+
email:
|
239
|
+
- mark.mcdx@gmail.com
|
240
|
+
executables: []
|
241
|
+
extensions: []
|
242
|
+
extra_rdoc_files: []
|
243
|
+
files:
|
244
|
+
- .gitignore
|
245
|
+
- .travis.yml
|
246
|
+
- Gemfile
|
247
|
+
- Guardfile
|
248
|
+
- LICENSE.txt
|
249
|
+
- README.md
|
250
|
+
- Rakefile
|
251
|
+
- alephant-publisher-request.gemspec
|
252
|
+
- lib/alephant/publisher/request.rb
|
253
|
+
- lib/alephant/publisher/request/data_mapper.rb
|
254
|
+
- lib/alephant/publisher/request/data_mapper_factory.rb
|
255
|
+
- lib/alephant/publisher/request/error.rb
|
256
|
+
- lib/alephant/publisher/request/processor.rb
|
257
|
+
- lib/alephant/publisher/request/processor/base.rb
|
258
|
+
- lib/alephant/publisher/request/version.rb
|
259
|
+
- spec/data_mapper_factory_spec.rb
|
260
|
+
- spec/data_mapper_spec.rb
|
261
|
+
- spec/fixtures/components/foo/mapper.rb
|
262
|
+
- spec/fixtures/components/foo/models/foo.rb
|
263
|
+
- spec/fixtures/components/foo/templates/foo.mustache
|
264
|
+
- spec/fixtures/components/invalid/mapper.rb
|
265
|
+
- spec/integration/rack_server_spec.rb
|
266
|
+
- spec/integration/spec_helper.rb
|
267
|
+
- spec/not_implemented_mapper_spec.rb
|
268
|
+
- spec/processor_spec.rb
|
269
|
+
- spec/request_spec.rb
|
270
|
+
- spec/spec_helper.rb
|
271
|
+
homepage: https://github.com/BBC-News/alephant-publisher-request
|
272
|
+
licenses:
|
273
|
+
- MIT
|
274
|
+
metadata: {}
|
275
|
+
post_install_message:
|
276
|
+
rdoc_options: []
|
277
|
+
require_paths:
|
278
|
+
- lib
|
279
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
280
|
+
requirements:
|
281
|
+
- - '>='
|
282
|
+
- !ruby/object:Gem::Version
|
283
|
+
version: '0'
|
284
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
285
|
+
requirements:
|
286
|
+
- - '>='
|
287
|
+
- !ruby/object:Gem::Version
|
288
|
+
version: '0'
|
289
|
+
requirements: []
|
290
|
+
rubyforge_project:
|
291
|
+
rubygems_version: 2.2.2
|
292
|
+
signing_key:
|
293
|
+
specification_version: 4
|
294
|
+
summary: '...'
|
295
|
+
test_files:
|
296
|
+
- spec/data_mapper_factory_spec.rb
|
297
|
+
- spec/data_mapper_spec.rb
|
298
|
+
- spec/fixtures/components/foo/mapper.rb
|
299
|
+
- spec/fixtures/components/foo/models/foo.rb
|
300
|
+
- spec/fixtures/components/foo/templates/foo.mustache
|
301
|
+
- spec/fixtures/components/invalid/mapper.rb
|
302
|
+
- spec/integration/rack_server_spec.rb
|
303
|
+
- spec/integration/spec_helper.rb
|
304
|
+
- spec/not_implemented_mapper_spec.rb
|
305
|
+
- spec/processor_spec.rb
|
306
|
+
- spec/request_spec.rb
|
307
|
+
- spec/spec_helper.rb
|