farscape 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/Gemfile.lock +52 -68
- data/README.md +35 -19
- data/lib/farscape/agent.rb +32 -7
- data/lib/farscape/discovery.rb +32 -0
- data/lib/farscape/platform_resources.rb +95 -0
- data/lib/farscape/version.rb +1 -1
- data/spec/lib/farscape/agent_spec.rb +26 -0
- data/spec/lib/farscape/integration/entry_point_spec.rb +36 -0
- data/spec/lib/farscape/plugins_spec.rb +0 -1
- data/spec/spec_helper.rb +6 -0
- metadata +19 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 42ff32e415677a4ca1cb6419518d7cbf78a6df1c
|
|
4
|
+
data.tar.gz: 2f7d3081526f4e913c7b4d66f35378db2212de31
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a45910ae4df9afafafa01805c07de5948c5a912367f5ee184b870d26167b75710d2c49055cd34d08fbaae804be542c683fc42c41d409e30007bfc44343b1fcd0
|
|
7
|
+
data.tar.gz: 8ee0a64cefcad9ecfc1e4c50c7f295031467b84846920baf1baffa1a3391dda3fead82eeda67144c78511cf41a8f922caf44c4a49fade8ad1a1fd395dd8324b5
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
GIT
|
|
2
|
-
remote:
|
|
3
|
-
revision: 958dc778d62897f09dd0200f3690f96cb5265b98
|
|
4
|
-
branch: 0-0-stable
|
|
5
|
-
specs:
|
|
6
|
-
representors (0.0.3)
|
|
7
|
-
addressable (~> 2.3)
|
|
8
|
-
rake
|
|
9
|
-
|
|
10
|
-
GIT
|
|
11
|
-
remote: https://www.github.com/mdsol-share/crichton.git
|
|
2
|
+
remote: https://github.com/mdsol-share/crichton.git
|
|
12
3
|
revision: 63c74ce781a41538454b81fd8c44dc36117aa92b
|
|
13
4
|
branch: develop
|
|
14
5
|
specs:
|
|
@@ -24,7 +15,7 @@ GIT
|
|
|
24
15
|
rake
|
|
25
16
|
|
|
26
17
|
GIT
|
|
27
|
-
remote: https://
|
|
18
|
+
remote: https://github.com/mdsol/moya.git
|
|
28
19
|
revision: 6e8b324dee6335e5d56d320ba0a605b6ebae8574
|
|
29
20
|
branch: develop
|
|
30
21
|
specs:
|
|
@@ -40,13 +31,12 @@ GIT
|
|
|
40
31
|
PATH
|
|
41
32
|
remote: .
|
|
42
33
|
specs:
|
|
43
|
-
farscape (1.0
|
|
34
|
+
farscape (1.3.0)
|
|
44
35
|
activesupport
|
|
45
|
-
addressable (~> 2.3
|
|
46
|
-
|
|
47
|
-
faraday (~> 0.8.8)
|
|
48
|
-
faraday_middleware (~> 0.9)
|
|
36
|
+
addressable (~> 2.3)
|
|
37
|
+
faraday (~> 0.9)
|
|
49
38
|
rake
|
|
39
|
+
representors (~> 0.0.5)
|
|
50
40
|
|
|
51
41
|
GEM
|
|
52
42
|
remote: https://rubygems.org/
|
|
@@ -77,47 +67,41 @@ GEM
|
|
|
77
67
|
minitest (~> 5.1)
|
|
78
68
|
thread_safe (~> 0.1)
|
|
79
69
|
tzinfo (~> 1.1)
|
|
80
|
-
activeuuid (0.6.
|
|
70
|
+
activeuuid (0.6.1)
|
|
81
71
|
activerecord (>= 3.1)
|
|
82
72
|
uuidtools
|
|
83
|
-
addressable (2.3.
|
|
73
|
+
addressable (2.3.8)
|
|
84
74
|
arel (5.0.1.20140414130214)
|
|
85
|
-
awesome_print (1.1.0)
|
|
86
75
|
builder (3.2.2)
|
|
87
|
-
|
|
76
|
+
byebug (9.0.5)
|
|
88
77
|
colorize (0.6.0)
|
|
89
|
-
|
|
78
|
+
concurrent-ruby (1.0.2)
|
|
79
|
+
crack (0.4.3)
|
|
90
80
|
safe_yaml (~> 1.0.0)
|
|
91
|
-
dice_bag (0.
|
|
81
|
+
dice_bag (0.9.0)
|
|
92
82
|
rake
|
|
93
83
|
diff-lcs (1.2.5)
|
|
94
84
|
diffy (3.0.7)
|
|
95
85
|
docile (1.1.5)
|
|
96
86
|
erubis (2.7.0)
|
|
97
|
-
faraday (0.
|
|
98
|
-
multipart-post (
|
|
99
|
-
|
|
100
|
-
faraday (>= 0.7.4, < 0.10)
|
|
101
|
-
hike (1.2.3)
|
|
87
|
+
faraday (0.9.2)
|
|
88
|
+
multipart-post (>= 1.2, < 3)
|
|
89
|
+
hashdiff (0.3.0)
|
|
102
90
|
i18n (0.7.0)
|
|
103
|
-
json (1.8.
|
|
91
|
+
json (1.8.3)
|
|
104
92
|
launchy (2.4.3)
|
|
105
93
|
addressable (~> 2.3)
|
|
106
|
-
mail (2.6.
|
|
107
|
-
mime-types (>= 1.16, <
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
multipart-post (
|
|
114
|
-
nokogiri (1.6.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
coderay (~> 1.1.0)
|
|
118
|
-
method_source (~> 0.8.1)
|
|
119
|
-
slop (~> 3.4)
|
|
120
|
-
rack (1.5.2)
|
|
94
|
+
mail (2.6.4)
|
|
95
|
+
mime-types (>= 1.16, < 4)
|
|
96
|
+
mime-types (3.1)
|
|
97
|
+
mime-types-data (~> 3.2015)
|
|
98
|
+
mime-types-data (3.2016.0521)
|
|
99
|
+
mini_portile2 (2.0.0)
|
|
100
|
+
minitest (5.9.0)
|
|
101
|
+
multipart-post (2.0.0)
|
|
102
|
+
nokogiri (1.6.7.2)
|
|
103
|
+
mini_portile2 (~> 2.0.0.rc2)
|
|
104
|
+
rack (1.5.5)
|
|
121
105
|
rack-test (0.6.3)
|
|
122
106
|
rack (>= 1.0)
|
|
123
107
|
rails (4.1.8)
|
|
@@ -135,8 +119,11 @@ GEM
|
|
|
135
119
|
activesupport (= 4.1.8)
|
|
136
120
|
rake (>= 0.8.7)
|
|
137
121
|
thor (>= 0.18.1, < 2.0)
|
|
138
|
-
rake (
|
|
139
|
-
redcarpet (3.
|
|
122
|
+
rake (11.1.2)
|
|
123
|
+
redcarpet (3.3.4)
|
|
124
|
+
representors (0.0.5)
|
|
125
|
+
addressable (~> 2.3)
|
|
126
|
+
rake
|
|
140
127
|
rspec (2.99.0)
|
|
141
128
|
rspec-core (~> 2.99.0)
|
|
142
129
|
rspec-expectations (~> 2.99.0)
|
|
@@ -146,31 +133,28 @@ GEM
|
|
|
146
133
|
diff-lcs (>= 1.1.3, < 2.0)
|
|
147
134
|
rspec-mocks (2.99.3)
|
|
148
135
|
safe_yaml (1.0.4)
|
|
149
|
-
simplecov (0.
|
|
136
|
+
simplecov (0.11.2)
|
|
150
137
|
docile (~> 1.1.0)
|
|
151
|
-
|
|
152
|
-
simplecov-html (~> 0.
|
|
153
|
-
simplecov-html (0.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
rack (~> 1.0)
|
|
159
|
-
tilt (~> 1.1, != 1.3.0)
|
|
160
|
-
sprockets-rails (2.2.4)
|
|
138
|
+
json (~> 1.8)
|
|
139
|
+
simplecov-html (~> 0.10.0)
|
|
140
|
+
simplecov-html (0.10.0)
|
|
141
|
+
sprockets (3.6.0)
|
|
142
|
+
concurrent-ruby (~> 1.0)
|
|
143
|
+
rack (> 1, < 3)
|
|
144
|
+
sprockets-rails (2.3.3)
|
|
161
145
|
actionpack (>= 3.0)
|
|
162
146
|
activesupport (>= 3.0)
|
|
163
147
|
sprockets (>= 2.8, < 4.0)
|
|
164
|
-
sqlite3 (1.3.
|
|
148
|
+
sqlite3 (1.3.11)
|
|
165
149
|
thor (0.19.1)
|
|
166
150
|
thread_safe (0.3.5)
|
|
167
|
-
tilt (1.4.1)
|
|
168
151
|
tzinfo (1.2.2)
|
|
169
152
|
thread_safe (~> 0.1)
|
|
170
153
|
uuidtools (2.1.5)
|
|
171
|
-
webmock (
|
|
172
|
-
addressable (>= 2.
|
|
154
|
+
webmock (2.0.3)
|
|
155
|
+
addressable (>= 2.3.6)
|
|
173
156
|
crack (>= 0.3.2)
|
|
157
|
+
hashdiff
|
|
174
158
|
yajl-ruby (1.2.1)
|
|
175
159
|
yard (0.8.7.6)
|
|
176
160
|
|
|
@@ -178,15 +162,15 @@ PLATFORMS
|
|
|
178
162
|
ruby
|
|
179
163
|
|
|
180
164
|
DEPENDENCIES
|
|
181
|
-
|
|
165
|
+
byebug
|
|
182
166
|
crichton!
|
|
183
167
|
farscape!
|
|
184
168
|
moya!
|
|
185
|
-
|
|
186
|
-
rake (~> 0.9)
|
|
187
|
-
redcarpet
|
|
188
|
-
representors!
|
|
169
|
+
redcarpet (~> 3.3)
|
|
189
170
|
rspec (~> 2.14)
|
|
190
|
-
simplecov (~> 0.
|
|
191
|
-
webmock (~>
|
|
192
|
-
yard
|
|
171
|
+
simplecov (~> 0.11)
|
|
172
|
+
webmock (~> 2.0)
|
|
173
|
+
yard
|
|
174
|
+
|
|
175
|
+
BUNDLED WITH
|
|
176
|
+
1.12.5
|
data/README.md
CHANGED
|
@@ -15,43 +15,59 @@ a response with a supported Hypermedia media-type and a root that lists availabl
|
|
|
15
15
|
|
|
16
16
|
### A Hypermedia API
|
|
17
17
|
For a interacting with an API (or individual service that supports a list of resources at its root), you enter the
|
|
18
|
-
API and follow your nose using the `enter` method on the agent. This method returns a [Farscape::Representor]()
|
|
19
|
-
instance with a simple state-machine interface of `attributes` (data) and `transitions` (link/form affordances) for
|
|
18
|
+
API and follow your nose using the `enter` method on the agent. This method returns a [Farscape::Representor](lib/farscape/representor.rb)
|
|
19
|
+
instance with a simple state-machine interface of `attributes` (data) and `transitions` (link/form affordances) for
|
|
20
20
|
interacting with the resource representations.
|
|
21
21
|
|
|
22
22
|
```ruby
|
|
23
|
-
agent = Farscape::Agent.
|
|
23
|
+
agent = Farscape::Agent.instance
|
|
24
24
|
|
|
25
|
-
resources = agent.enter
|
|
25
|
+
resources = agent.enter('http://example.com/my_api')
|
|
26
26
|
resources.attributes # => { meta: 'data', or: 'other data' }
|
|
27
27
|
resources.transitions.keys # => ['http://example.com/rel/drds', 'http://example.com/rel/leviathans']
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
### A Hypermedia Discovery Service
|
|
31
|
-
For interacting with a discovery service,
|
|
32
|
-
or immediately loading a discoverable resource if known to be registered in the service *a priori*.
|
|
31
|
+
For interacting with a discovery service, you can use enter and follow your nose entry to select a registered resource or setup Farscape with a discovery server.
|
|
33
32
|
|
|
34
33
|
```ruby
|
|
35
|
-
|
|
34
|
+
# Setting discovery service to https://my_discovery_api
|
|
35
|
+
Farscape::Agent.config = { Farscape::Discovery::DISCOVERY_KEY => 'https://my_discovery_api' }
|
|
36
|
+
```
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
The discovery service must return a document with a list of resource names and their root URLs like:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"_links": {
|
|
43
|
+
"self": { "href": "https://my_discovery_api" },
|
|
44
|
+
"boxes": { "href": "https://smallboxesandpoliceboxes.com" },
|
|
45
|
+
"items": { "href": "https://sonicscrewdriversandotherthings.com/v1/{item}" }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
40
49
|
|
|
41
|
-
|
|
42
|
-
drds.attributes # => { total_count: 25, items: [...] }
|
|
43
|
-
drds.transitions.keys # => ['self', 'search', 'create', 'next', 'last']
|
|
50
|
+
Farscape then can be used directly with resource names. Farscape will already do the heavy-lifting of contacting the discovery service and retrieving the root document of the resource.
|
|
44
51
|
|
|
45
|
-
|
|
52
|
+
```ruby
|
|
53
|
+
agent = Farscape::Agent.instance
|
|
54
|
+
boxes = agent.enter('boxes')
|
|
55
|
+
boxes.attributes # => { total_count: 13, items: [...] }
|
|
56
|
+
|
|
57
|
+
agent.enter('unknownresource') # raises Farscape::Discovery::NotFound
|
|
58
|
+
|
|
59
|
+
agent.enter('items', [{ items: 'bow-tie' }]) # Allows template variables
|
|
46
60
|
```
|
|
47
61
|
|
|
62
|
+
|
|
63
|
+
|
|
48
64
|
## API Interaction
|
|
49
|
-
Entering an API takes you into its application state-machine and, as such, the interface for interacting with that
|
|
50
|
-
application state is brain dead simple with Farscape. You have data that you read and hypermedia affordances that tell
|
|
65
|
+
Entering an API takes you into its application state-machine and, as such, the interface for interacting with that
|
|
66
|
+
application state is brain dead simple with Farscape. You have data that you read and hypermedia affordances that tell
|
|
51
67
|
you what you can do next and you can invoke those affordances to do things. That's it.
|
|
52
68
|
|
|
53
|
-
Farscape recognizes a number of media-types that support runtime knowledge of the underlying REST uniform-interface
|
|
54
|
-
methods. For these full-featured media-types, the interaction with with resources is as simple as a browser where
|
|
69
|
+
Farscape recognizes a number of media-types that support runtime knowledge of the underlying REST uniform-interface
|
|
70
|
+
methods. For these full-featured media-types, the interaction with with resources is as simple as a browser where
|
|
55
71
|
implementation of requests is completely abstracted from the user.
|
|
56
72
|
|
|
57
73
|
The following simple examples highlight interacting with resource state-machines using Farscape.
|
|
@@ -128,7 +144,7 @@ new_drd.attributes # => { name: 'Pike' }
|
|
|
128
144
|
new_drd.transitions.keys # => ['self', 'edit', 'delete', 'deactivate', 'leviathan']
|
|
129
145
|
```
|
|
130
146
|
|
|
131
|
-
For more examples and information on using Faraday with media-types that require specifying uniform-interface methods
|
|
147
|
+
For more examples and information on using Faraday with media-types that require specifying uniform-interface methods
|
|
132
148
|
and other protocol idioms when invoking transitions, see [Using Farscape]().
|
|
133
149
|
|
|
134
150
|
## Alternate Interface
|
data/lib/farscape/agent.rb
CHANGED
|
@@ -1,16 +1,32 @@
|
|
|
1
1
|
require 'farscape/representor'
|
|
2
2
|
require 'farscape/clients'
|
|
3
|
+
require 'farscape/discovery'
|
|
3
4
|
|
|
4
5
|
module Farscape
|
|
5
6
|
class Agent
|
|
6
|
-
|
|
7
|
+
|
|
7
8
|
include BaseAgent
|
|
8
|
-
|
|
9
|
+
|
|
9
10
|
PROTOCOL = :http
|
|
10
11
|
|
|
11
12
|
attr_reader :media_type
|
|
12
13
|
attr_reader :entry_point
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
# Prevents multiple threads from accessing the same agent.
|
|
17
|
+
def instance
|
|
18
|
+
Thread.current[:farscape_agent] ||= Agent.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def config
|
|
22
|
+
@farscape_config || {}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def config=(farscape_config)
|
|
26
|
+
@farscape_config = farscape_config
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
14
30
|
def initialize(entry = nil, media = :hale, safe = false, plugin_hash = {})
|
|
15
31
|
@entry_point = entry
|
|
16
32
|
@media_type = media
|
|
@@ -23,10 +39,19 @@ module Farscape
|
|
|
23
39
|
safe? ? SafeRepresentorAgent : RepresentorAgent
|
|
24
40
|
end
|
|
25
41
|
|
|
26
|
-
|
|
27
|
-
|
|
42
|
+
# Discovers provided a key and template_variables.
|
|
43
|
+
# This method is here to be easily overwritten or monkey-patched if needed.
|
|
44
|
+
def discover_entry_point(key, template_variables = {})
|
|
45
|
+
Discovery.new.discover(self.class.config, key, template_variables)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def enter(entry = entry_point, template_variables = {})
|
|
28
49
|
raise "No Entry Point Provided!" unless entry
|
|
29
|
-
|
|
50
|
+
@entry_point ||= entry
|
|
51
|
+
unless Addressable::URI.parse(@entry_point).absolute?
|
|
52
|
+
@entry_point = discover_entry_point(@entry_point, template_variables)
|
|
53
|
+
end
|
|
54
|
+
response = client.invoke(url: @entry_point, headers: get_accept_header(media_type))
|
|
30
55
|
find_exception(response)
|
|
31
56
|
end
|
|
32
57
|
|
|
@@ -100,7 +125,7 @@ module Farscape
|
|
|
100
125
|
end
|
|
101
126
|
|
|
102
127
|
private
|
|
103
|
-
|
|
128
|
+
|
|
104
129
|
def default_plugin_hash
|
|
105
130
|
{
|
|
106
131
|
plugins: Farscape.plugins.dup, # A hash of plugins keyed by the plugin name
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Farscape
|
|
2
|
+
# This class provide discovery capabilities to Farscape.
|
|
3
|
+
class Discovery
|
|
4
|
+
|
|
5
|
+
DISCOVERY_KEY = :discovery_uri
|
|
6
|
+
|
|
7
|
+
class NotFound < NameError
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# This is the simplest type of discovery. We assume that the url we will call will provide us
|
|
11
|
+
# document with links which keys are the names we want to discover and hrefs are root URLS.
|
|
12
|
+
# Templated URLs are supported, an exception will be raised if the template does not match the variables.
|
|
13
|
+
# {
|
|
14
|
+
# "_links": {
|
|
15
|
+
# "boxes": { "href": "https://smallboxesandpoliceboxes.com" },
|
|
16
|
+
# "items": { "href": "https://sonicscrewdriversandotherthings.com/v1/{item}" }
|
|
17
|
+
# }
|
|
18
|
+
# }
|
|
19
|
+
def discover(config, key, url_template_variables)
|
|
20
|
+
discovery_uri = config[DISCOVERY_KEY]
|
|
21
|
+
raise NotFound, "No discovery uri setup for Farscape. Discovery of #{key} unavailable" unless discovery_uri
|
|
22
|
+
raise NotFound, "Discover URL #{discovery_uri} is not a valid URL." unless discovery_uri =~ URI::regexp
|
|
23
|
+
discovery_document = Farscape::Agent.new(discovery_uri).enter
|
|
24
|
+
if discovery_document.kind_of?(Faraday::Response) # Parse errors give us a raw Faraday Response
|
|
25
|
+
raise NotFound, "The discovery document for #{key} is not valid JSON. We got: #{discovery_document.body}"
|
|
26
|
+
end
|
|
27
|
+
found = discovery_document.transitions[key.to_s]
|
|
28
|
+
raise NotFound, "No key '#{key}' found in the response from the discovery service." unless found
|
|
29
|
+
found.uri(url_template_variables)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
module Farscape
|
|
2
|
+
class PlatformResources
|
|
3
|
+
ROOT_ITEMS_KEY = 'items'.freeze
|
|
4
|
+
|
|
5
|
+
class NotFound < StandardError; end
|
|
6
|
+
|
|
7
|
+
def self.agent
|
|
8
|
+
@agent ||= Agent.new # This is only done once in the application because is expensive
|
|
9
|
+
# @agent is global to the application. We dup it so each thread has its own copy
|
|
10
|
+
Thread.current[:farscape_agent] ||= @agent.dup
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.root_for(resource_name, template_variables = {})
|
|
14
|
+
rescuing_farscape(resource_name) do
|
|
15
|
+
Farscape.logger.info("Discovering #{resource_name} providing data >#{template_variables}<")
|
|
16
|
+
root = agent.discover(resource_name, template_variables)
|
|
17
|
+
Farscape.logger.info("Accessing the root #{root} for #{resource_name}")
|
|
18
|
+
agent.instance_variable_set(:@entry_point, root)
|
|
19
|
+
agent.enter(root)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.items_for(resource_name, template_variables = {})
|
|
24
|
+
root_document = root_for(resource_name, template_variables)
|
|
25
|
+
attributes = root_document_data(resource_name, root_document)
|
|
26
|
+
attributes[ROOT_ITEMS_KEY] || []
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.root_document_data(resource_name, root_document)
|
|
30
|
+
if root_document.respond_to?(:attributes)
|
|
31
|
+
Farscape.logger.debug("Called #{resource_name} and got #{root_document.attributes[ROOT_ITEMS_KEY]}")
|
|
32
|
+
root_document.attributes
|
|
33
|
+
elsif root_document.respond_to?(:body) # Farscape will happily return a Faraday Response when it can not be parsed
|
|
34
|
+
Farscape.logger.error("The document retrieved for #{resource_name} is not valid JSON. We got: >#{root_document.body}<")
|
|
35
|
+
{}
|
|
36
|
+
else
|
|
37
|
+
Farscape.logger.error("Unknown response trying to access #{resource_name}. We got: >#{root_document}<")
|
|
38
|
+
{}
|
|
39
|
+
end
|
|
40
|
+
rescue StandardError => e
|
|
41
|
+
log_error("Error accessing the attributes on the root of #{resource_name}", e)
|
|
42
|
+
{}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.rescuing_farscape(resource_name)
|
|
46
|
+
yield
|
|
47
|
+
rescue Net::ReadTimeout => e
|
|
48
|
+
log_error("Network read error accessing #{resource_name}", e)
|
|
49
|
+
rescue Farscape::Exceptions::NotFound => e
|
|
50
|
+
log_error("Resource not found accessing #{resource_name}", e)
|
|
51
|
+
rescue Farscape::Exceptions::Forbidden => e
|
|
52
|
+
log_error("Forbidden access accessing #{resource_name}", e)
|
|
53
|
+
rescue Farscape::Exceptions::InternalServerError => e
|
|
54
|
+
log_error("The server responded with an internal server error when accessing #{resource_name}", e)
|
|
55
|
+
rescue Farscape::Discovery::KeyNotFound => e
|
|
56
|
+
log_error("The resource #{resource_name} is not registered in our Hypermedia discovery service", e)
|
|
57
|
+
rescue StandardError => e
|
|
58
|
+
Farscape.logger.error("Unknown error class '#{e.class}'")
|
|
59
|
+
log_error("Unknown error accessing #{resource_name}", e)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# This is a simple wrapper over Farscape::Agent which provides access to the URL on the enter method.
|
|
63
|
+
# Recreating the agent is very costly. As it tries to load the plugins and these may be heavy
|
|
64
|
+
# Specially the discovery service plugin is trying to load a file from disk every single time.
|
|
65
|
+
# We need to access a variable inside farscape to reuse the agent more than once.
|
|
66
|
+
# Once Farscape has a version which let us reuse the agent we will delete this helper.
|
|
67
|
+
class Agent
|
|
68
|
+
def initialize
|
|
69
|
+
@agent = Farscape::Agent.new
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def enter(root)
|
|
73
|
+
@agent.instance_variable_set(:@entry_point, root)
|
|
74
|
+
@agent.enter(root)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def discover(resource_name, template_variables)
|
|
78
|
+
if @agent.respond_to?(:discover)
|
|
79
|
+
@agent.discover(resource_name)
|
|
80
|
+
else
|
|
81
|
+
@agent.enter(resource_name)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def method_missing(name, *args, &block)
|
|
86
|
+
@agent.send(name, *args, &block)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def self.log_error(message, e)
|
|
91
|
+
Farscape.logger.error("Hippocrates Error, class #{e.class} \n #{message}: '#{e.message}' \n #{e.backtrace.join("\n")}")
|
|
92
|
+
nil
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
data/lib/farscape/version.rb
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Farscape::Agent do
|
|
4
|
+
describe '#instance' do
|
|
5
|
+
it 'returns an Farscape::Agent object' do
|
|
6
|
+
expect(described_class.instance.class).to eq(described_class)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it 'returns the same agent if called twice' do
|
|
10
|
+
agent1 = described_class.instance
|
|
11
|
+
expect(described_class.instance).to eq(agent1)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe '#config' do
|
|
16
|
+
it 'defaults to an empty hash' do
|
|
17
|
+
expect(described_class.config).to eq({})
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'can be set' do
|
|
21
|
+
described_class.config = { me: 'too' }
|
|
22
|
+
expect(described_class.config).to eq( me: 'too' )
|
|
23
|
+
described_class.config = nil
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -3,6 +3,42 @@ require 'spec_helper'
|
|
|
3
3
|
describe Farscape::Agent do
|
|
4
4
|
let(:entry_point) { "http://localhost:#{RAILS_PORT}"}
|
|
5
5
|
|
|
6
|
+
describe '#discover' do
|
|
7
|
+
let(:resource_name) { 'user' }
|
|
8
|
+
let(:links) do
|
|
9
|
+
{
|
|
10
|
+
_links: {
|
|
11
|
+
resource_name => { href: entry_point }
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
before do
|
|
16
|
+
described_class.config = { discovery_uri: "https://www.example.com" }
|
|
17
|
+
stub_request(:any, "https://www.example.com").to_return(body: links.to_json)
|
|
18
|
+
end
|
|
19
|
+
after do
|
|
20
|
+
described_class.config = nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'returns a Farscape::Representor from a name' do
|
|
24
|
+
expect(Farscape::Agent.new(resource_name).enter).to be_a Farscape::RepresentorAgent
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'raises Discovery::NotFound if the name does not exist in the discovery service' do
|
|
28
|
+
expect{ Farscape::Agent.new('unknown').enter }.to raise_error Farscape::Discovery::NotFound
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'raises Discovery::NotFound if the discovery url is not setup' do
|
|
32
|
+
described_class.config = nil
|
|
33
|
+
expect{ Farscape::Agent.new.discover_entry_point(resource_name) }.to raise_error Farscape::Discovery::NotFound
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'raises Discovery::NotFound if the discovery url is not a proper url' do
|
|
37
|
+
described_class.config = { discovery_uri: "aaaaa" }
|
|
38
|
+
expect{ Farscape::Agent.new.discover_entry_point(resource_name) }.to raise_error Farscape::Discovery::NotFound
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
6
42
|
describe '#enter' do
|
|
7
43
|
it 'returns a Farscape::Representor' do
|
|
8
44
|
expect(Farscape::Agent.new(entry_point).enter).to be_a Farscape::RepresentorAgent
|
|
@@ -276,7 +276,6 @@ describe Farscape do
|
|
|
276
276
|
Farscape.register_plugin(name: :Wormlet, type: :discovery, extensions: {Agent: [ServiceCatalogue]})
|
|
277
277
|
expect(Farscape::Agent.new(:moya).enter.transitions.keys).to include("drds")
|
|
278
278
|
expect(Farscape::Agent.new.enter(:moya).transitions.keys).to include("drds")
|
|
279
|
-
expect { Farscape::Agent.new.omitting(:discovery).enter(:moya).transitions.keys }.to raise_error(NoMethodError)
|
|
280
279
|
end
|
|
281
280
|
|
|
282
281
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -5,6 +5,9 @@ require 'crichton'
|
|
|
5
5
|
require 'representors'
|
|
6
6
|
require 'moya'
|
|
7
7
|
|
|
8
|
+
require 'webmock/rspec'
|
|
9
|
+
|
|
10
|
+
|
|
8
11
|
RAILS_PORT = 1234
|
|
9
12
|
SPEC_DIR = File.expand_path("..", __FILE__)
|
|
10
13
|
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
|
@@ -12,6 +15,9 @@ require 'farscape'
|
|
|
12
15
|
|
|
13
16
|
Dir["#{SPEC_DIR}/support/*.rb"].each { |f| require f }
|
|
14
17
|
|
|
18
|
+
|
|
19
|
+
WebMock.disable_net_connect!(allow_localhost: true)
|
|
20
|
+
|
|
15
21
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
|
16
22
|
RSpec.configure do |config|
|
|
17
23
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: farscape
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mark W. Foster
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-
|
|
11
|
+
date: 2016-06-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -122,6 +122,20 @@ dependencies:
|
|
|
122
122
|
- - "~>"
|
|
123
123
|
- !ruby/object:Gem::Version
|
|
124
124
|
version: '2.14'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: webmock
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - "~>"
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '2.0'
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - "~>"
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '2.0'
|
|
125
139
|
- !ruby/object:Gem::Dependency
|
|
126
140
|
name: simplecov
|
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -171,14 +185,17 @@ files:
|
|
|
171
185
|
- lib/farscape/client/base_client.rb
|
|
172
186
|
- lib/farscape/client/http_client.rb
|
|
173
187
|
- lib/farscape/clients.rb
|
|
188
|
+
- lib/farscape/discovery.rb
|
|
174
189
|
- lib/farscape/errors.rb
|
|
175
190
|
- lib/farscape/helpers/partially_ordered_list.rb
|
|
176
191
|
- lib/farscape/logger.rb
|
|
192
|
+
- lib/farscape/platform_resources.rb
|
|
177
193
|
- lib/farscape/plugins.rb
|
|
178
194
|
- lib/farscape/representor.rb
|
|
179
195
|
- lib/farscape/transition.rb
|
|
180
196
|
- lib/farscape/version.rb
|
|
181
197
|
- lib/plugins/plugins.rb
|
|
198
|
+
- spec/lib/farscape/agent_spec.rb
|
|
182
199
|
- spec/lib/farscape/cache_spec.rb
|
|
183
200
|
- spec/lib/farscape/integration/entry_point_spec.rb
|
|
184
201
|
- spec/lib/farscape/integration/interface_spec.rb
|