siren_client 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,6 @@
1
+ module SirenClient
2
+ class StandardError < ::StandardError; end
3
+ class InvalidURIError < StandardError; end
4
+ class InvalidResponseError < StandardError; end
5
+ class InvalidLogger < StandardError; end
6
+ end
@@ -0,0 +1,17 @@
1
+ module SirenClient
2
+ class Field
3
+ attr_reader :payload, :name, :type, :value, :title
4
+
5
+ def initialize(data)
6
+ if data.class != Hash
7
+ raise ArgumentError, "You must pass in a Hash to SirenClient::Action.new"
8
+ end
9
+ @payload = data
10
+
11
+ @name = @payload['name'] || ''
12
+ @type = @payload['type'] || 'text'
13
+ @value = @payload['value'] || ''
14
+ @title = @payload['title'] || ''
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module SirenClient
2
+ class Link
3
+ attr_reader :payload, :rels, :href, :title, :type, :config
4
+
5
+ def initialize(data, config={})
6
+ if data.class != Hash
7
+ raise ArgumentError, "You must pass in a Hash to SirenClient::Link.new"
8
+ end
9
+ @payload = data
10
+ @config = { format: :json }.merge config
11
+
12
+ @rels = @payload['rel'] || []
13
+ @href = @payload['href'] || ''
14
+ @title = @payload['title'] || ''
15
+ @type = @payload['type'] || ''
16
+ end
17
+
18
+ def go
19
+ Entity.new(self.href, @config)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module SirenClient
2
+ VERSION = "0.1"
3
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'siren_client/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "siren_client"
8
+ spec.version = SirenClient::VERSION
9
+ spec.authors = ["Chason Choate"]
10
+ spec.email = ["cha55son@gmail.com"]
11
+ spec.summary = %q{A client to traverse Siren APIs https://github.com/kevinswiber/siren}
12
+ spec.description = %q{SirenClient provides an ActiveRecord-like syntax to traverse Siren APIs.}
13
+ spec.homepage = "https://github.com/cha55son/siren_client"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.test_files = spec.files.grep(%r{^(spec)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "httparty", "~> 0.13"
21
+ spec.add_dependency "activesupport", "~> 4.2"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.5"
24
+ spec.add_development_dependency "rack"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "sinatra"
27
+ spec.add_development_dependency "sinatra-simple-auth"
28
+ spec.add_development_dependency "rspec"
29
+ spec.add_development_dependency "byebug"
30
+ spec.add_development_dependency "guard"
31
+ spec.add_development_dependency "guard-rspec"
32
+ end
@@ -0,0 +1,20 @@
1
+ require 'rack'
2
+ require 'helper/spec_helper'
3
+ require 'support/test_server'
4
+
5
+ # Start a local rack server to serve up dummy data.
6
+ server_thread = Thread.new do
7
+ Rack::Handler::WEBrick.run(
8
+ TestServer.new,
9
+ :Port => 9292,
10
+ :AccessLog => [],
11
+ :Logger => WEBrick::Log::new("/dev/null", 7)
12
+ )
13
+ end
14
+ sleep(1) # wait a sec for the server to be boot
15
+
16
+ RSpec.configure do |config|
17
+ config.after(:suite) {
18
+ server_thread.kill
19
+ }
20
+ end
@@ -0,0 +1,112 @@
1
+ require 'byebug'
2
+ require 'siren_client'
3
+
4
+ RSpec.configure do |config|
5
+ # Ensure we only use `expect` and not `should`.
6
+ config.expect_with :rspec do |c|
7
+ c.syntax = :expect
8
+ end
9
+ end
10
+
11
+ def siren_body
12
+ {
13
+ 'class' => ['graphs', 'collection'],
14
+ 'properties' => {
15
+ 'page' => 1,
16
+ 'limit' => 20
17
+ },
18
+ 'entities' => [
19
+ {
20
+ 'class' => ['graphs'],
21
+ 'rel' => ['/rels/graphs'],
22
+ 'properties' => {
23
+ 'name' => 'test1'
24
+ },
25
+ 'entities' => [
26
+ {
27
+ 'class' => ['messages', 'collection'],
28
+ 'rel' => ['/rels/messages'],
29
+ 'href' => '/graphs/test1/messages'
30
+ },
31
+ {
32
+ 'class' => ['concepts', 'collection'],
33
+ 'rel' => ['/rels/concepts'],
34
+ 'href' => '/graphs/test2/concepts'
35
+ }
36
+ ],
37
+ 'links' => [
38
+ {
39
+ 'rel' => ['self'],
40
+ 'href' => '/graphs/test1'
41
+ }
42
+ ]
43
+ }
44
+ ],
45
+ 'actions' => [
46
+ {
47
+ 'name' => 'filter_concepts',
48
+ 'method' => 'GET',
49
+ 'href' => '/graphs/test1/concepts',
50
+ 'title' => 'Get an optionally filtered list of Concepts',
51
+ 'type' => 'application/x-www-form-urlencoded',
52
+ 'fields' => [
53
+ {
54
+ 'name' => 'limit',
55
+ 'title' => 'Max number of results in each page',
56
+ 'type' => 'NUMBER',
57
+ 'required' => false
58
+ },
59
+ {
60
+ 'name' => 'page',
61
+ 'title' => 'Page number, starting at 1',
62
+ 'type' => 'NUMBER',
63
+ 'required' => false
64
+ },
65
+ {
66
+ 'name' => 'search',
67
+ 'title' => 'Keyword search',
68
+ 'type' => 'TEXT',
69
+ 'required' => true
70
+ }
71
+ ]
72
+ },
73
+ {
74
+ 'name' => 'filter_messages',
75
+ 'method' => 'GET',
76
+ 'href' => '/graphs/test1/messages',
77
+ 'title' => 'Get an optionally filtered list of Messages',
78
+ 'type' => 'application/x-www-form-urlencoded',
79
+ 'fields' => [
80
+ {
81
+ 'name' => 'limit',
82
+ 'title' => 'Max number of results in each page',
83
+ 'type' => 'NUMBER',
84
+ 'required' => false
85
+ },
86
+ {
87
+ 'name' => 'page',
88
+ 'title' => 'Page number, starting at 1',
89
+ 'type' => 'NUMBER',
90
+ 'required' => false
91
+ },
92
+ {
93
+ 'name' => 'search',
94
+ 'title' => 'Keyword search',
95
+ 'type' => 'TEXT',
96
+ 'required' => true
97
+ }
98
+ ]
99
+ }
100
+ ],
101
+ 'links' => [
102
+ {
103
+ 'rel' => ['self'],
104
+ 'href' => '/graphs?limit=1&page=1&order_by=name'
105
+ },
106
+ {
107
+ 'rel' => ['next'],
108
+ 'href' => '/graphs?limit=1&page=2&order_by=name'
109
+ }
110
+ ]
111
+ }
112
+ end
@@ -0,0 +1,101 @@
1
+ require 'helper/live_spec_helper'
2
+
3
+ URL = 'http://localhost:9292'
4
+ describe HTTParty do
5
+ it 'will be blocked by basic auth' do
6
+ expect(HTTParty.get(URL).code).to eq(401)
7
+ end
8
+ it 'can access the server' do
9
+ expect(HTTParty.get(URL, basic_auth: { username: 'admin', password: '1234' }).code).to eq(200)
10
+ end
11
+ end
12
+
13
+ describe SirenClient do
14
+ context 'when creating an entity' do
15
+ let (:headers_ent) {
16
+ SirenClient.get({
17
+ url: URL,
18
+ basic_auth: { username: 'admin', password: '1234' },
19
+ headers: { "Accept" => "application/json" }
20
+ })
21
+ }
22
+ it 'can set HTTP headers' do
23
+ expect {
24
+ expect(headers_ent.config[:headers]).to be_a Hash
25
+ expect(headers_ent.config[:headers]).to eq({
26
+ "Accept" => "application/json"
27
+ })
28
+ }.to_not raise_error
29
+ end
30
+ it 'it\'s actions inherit the same config' do
31
+ expect(headers_ent.filter_concepts_get.config[:headers]).to eq({
32
+ "Accept" => "application/json"
33
+ })
34
+ end
35
+ end
36
+
37
+ let (:client) {
38
+
39
+ SirenClient.get(
40
+ url: URL,
41
+ timeout: 2,
42
+ basic_auth: { username: 'admin', password: '1234' },
43
+ headers: { "Accept" => "application/json" }
44
+ )
45
+ }
46
+ context 'when accessing the root entity' do
47
+ it 'to return an entity' do
48
+ expect(client).to be_a SirenClient::Entity
49
+ end
50
+ it 'to access properties' do
51
+ expect(client.properties['name']).to eq('Test 1')
52
+ end
53
+ it 'to not have entities' do
54
+ expect(client.length).to eq(0)
55
+ end
56
+ end
57
+ context 'when accessing a link' do
58
+ it 'to return a link' do
59
+ expect(client.links['concepts']).to be_a SirenClient::Link
60
+ end
61
+ it 'to follow the link' do
62
+ expect(client.concepts).to be_a SirenClient::Entity
63
+ expect(client.concepts.links['self'].href).to eq(URL + '/concepts')
64
+ end
65
+ end
66
+ context 'when accessing an action with GET' do
67
+ it 'to return an action' do
68
+ expect(client.filter_concepts_get).to be_a SirenClient::Action
69
+ end
70
+ context 'with .where' do
71
+ it 'to execute the action' do
72
+ params = { search: 'obama' }
73
+ expect(client.filter_concepts_get.where(params)).to be_a SirenClient::Entity
74
+ expect(client.filter_concepts_get.where(params).length).to eq(1)
75
+ expect(client.filter_concepts_get.where(params)[0]).to be_a SirenClient::Entity
76
+ expect(client.filter_concepts_get.where(params)[0].category).to eq('PERSON')
77
+ end
78
+ end
79
+ end
80
+ context 'when accessing an action with POST' do
81
+ it 'to return an action' do
82
+ expect(client.filter_concepts_post).to be_a SirenClient::Action
83
+ end
84
+ context 'with .where' do
85
+ it 'to execute the action' do
86
+ params = { search: 'obama' }
87
+ expect(client.filter_concepts_post.where(params)).to be_a SirenClient::Entity
88
+ expect(client.filter_concepts_post.where(params).length).to eq(1)
89
+ expect(client.filter_concepts_post.where(params)[0]).to be_a SirenClient::Entity
90
+ expect(client.filter_concepts_post.where(params)[0].category).to eq('PERSON')
91
+ end
92
+ end
93
+ end
94
+
95
+ let (:concepts) { SirenClient.get({ url: URL, basic_auth: { username: 'admin', password: '1234' }}).concepts }
96
+ context 'when accessing an entity' do
97
+ it 'to return an entity' do
98
+ expect(concepts).to be_a SirenClient::Entity
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,85 @@
1
+ class TestServer < Sinatra::Base
2
+ CON_1 = <<-EOF
3
+ {
4
+ "class":["concepts"],
5
+ "rel":["/rels/concepts"],
6
+ "properties":{
7
+ "text":"barack obama",
8
+ "category":"PERSON"
9
+ },
10
+ "links":[
11
+ {
12
+ "rel":["self"],
13
+ "href":"#{@@url}/concepts/1"
14
+ }
15
+ ]
16
+ }
17
+ EOF
18
+ CON_2 = <<-EOF
19
+ {
20
+ "class":["concepts"],
21
+ "rel":["/rels/concepts"],
22
+ "properties":{
23
+ "text":"tennessee",
24
+ "category":"LOCATION"
25
+ },
26
+ "links":[
27
+ {
28
+ "rel":["self"],
29
+ "href":"#{@@url}/concepts/2"
30
+ }
31
+ ]
32
+ }
33
+ EOF
34
+ post '/concepts/?' do
35
+ query = (request.query_string.length > 0 ? '?' : '') + request.query_string
36
+ <<-EOF
37
+ {
38
+ "class":["concepts","collection"],
39
+ "properties": { "count": 1 },
40
+ "entities": [#{CON_1}],
41
+ "links": [
42
+ {
43
+ "rel": ["self"],
44
+ "href": "#{@@url}/concepts#{query}"
45
+ }
46
+ ]
47
+ }
48
+ EOF
49
+ end
50
+ get '/concepts/?' do
51
+ query = (request.query_string.length > 0 ? '?' : '') + request.query_string
52
+ # search=obama
53
+ if request.params['search']
54
+ <<-EOF
55
+ {
56
+ "class":["concepts","collection"],
57
+ "properties": { "count": 1 },
58
+ "entities": [#{CON_1}],
59
+ "links": [
60
+ {
61
+ "rel": ["self"],
62
+ "href": "#{@@url}/concepts#{query}"
63
+ }
64
+ ]
65
+ }
66
+ EOF
67
+ else
68
+ <<-EOF
69
+ {
70
+ "class":["concepts","collection"],
71
+ "properties":{
72
+ "count":2
73
+ },
74
+ "entities":[#{CON_1}, #{CON_2}],
75
+ "links":[
76
+ {
77
+ "rel":["self"],
78
+ "href":"#{@@url}/concepts#{query}"
79
+ }
80
+ ]
81
+ }
82
+ EOF
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,101 @@
1
+ class TestServer < Sinatra::Base
2
+ get '/?' do
3
+ <<-EOF
4
+ {
5
+ "properties": {
6
+ "page": 1,
7
+ "name": "Test 1"
8
+ },
9
+ "links": [
10
+ {
11
+ "rel": ["self"],
12
+ "href":"#{@@url}/"
13
+ },
14
+ {
15
+ "rel": ["collection", "concepts"],
16
+ "href":"#{@@url}/concepts",
17
+ "title": "Concepts"
18
+ },
19
+ { "rel": ["messages", "collection"],
20
+ "href": "#{@@url}/messages",
21
+ "title":"Messages"
22
+ }
23
+ ],
24
+ "actions": [
25
+ {
26
+ "name":"filter-concepts-get",
27
+ "method":"GET",
28
+ "href":"#{@@url}/concepts",
29
+ "title":"Get an optionally filtered list of Concepts",
30
+ "type":"application/x-www-form-urlencoded",
31
+ "fields":[
32
+ {
33
+ "name":"limit",
34
+ "title":"Max number of results in each page",
35
+ "type":"number"
36
+ },
37
+ {
38
+ "name":"page",
39
+ "title":"Page number, starting at 1",
40
+ "type":"number"
41
+ },
42
+ {
43
+ "name":"search",
44
+ "title":"Keyword search on concept text",
45
+ "type":"text"
46
+ }
47
+ ]
48
+ },
49
+ {
50
+ "name":"filter-concepts-post",
51
+ "method":"POST",
52
+ "href":"#{@@url}/concepts",
53
+ "title":"Get an optionally filtered list of Concepts",
54
+ "type":"application/x-www-form-urlencoded",
55
+ "fields":[
56
+ {
57
+ "name":"limit",
58
+ "title":"Max number of results in each page",
59
+ "type":"number"
60
+ },
61
+ {
62
+ "name":"page",
63
+ "title":"Page number, starting at 1",
64
+ "type":"number"
65
+ },
66
+ {
67
+ "name":"search",
68
+ "title":"Keyword search on concept text",
69
+ "type":"text"
70
+ }
71
+ ]
72
+ },
73
+ {
74
+ "name":"filter-messages",
75
+ "method":"GET",
76
+ "href":"#{@@url}/messages",
77
+ "title":"Get an optionally filtered list of Messages",
78
+ "type":"application/x-www-form-urlencoded",
79
+ "fields":[
80
+ {
81
+ "name":"limit",
82
+ "title":"Max number of results in each page",
83
+ "type":"number"
84
+ },
85
+ {
86
+ "name":"page",
87
+ "title":"Page number, starting at 1",
88
+ "type":"number"
89
+ },
90
+ {
91
+ "name":"search",
92
+ "title":"Keyword search on message body",
93
+ "type":"number"
94
+ }
95
+ ]
96
+ }
97
+ ]
98
+ }
99
+ EOF
100
+ end
101
+ end