gds-api-adapters 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ A set of API adapters to work with the GDS APIs, extracted from the frontend app.
2
+
3
+ Example usage:
4
+
5
+ publisher_api = GdsApi::Publisher.new("environment")
6
+ ostruct_publication = publisher.publication_for_slug('my-published-item')
7
+
8
+ panopticon_api = GdsApi::Panopticon.new("environment")
9
+ ostruct_metadata = panopticon_api.artefact_for_slug('my-published-item')
10
+
11
+ Very much still a work in progress.
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rdoc/task"
5
+ require 'rake/testtask'
6
+
7
+ RDoc::Task.new do |rd|
8
+ rd.rdoc_files.include("lib/**/*.rb")
9
+ rd.rdoc_dir = "rdoc"
10
+ end
11
+
12
+ Rake::TestTask.new("test") do |t|
13
+ t.ruby_opts << "-rubygems"
14
+ t.libs << "test"
15
+ t.test_files = FileList["test/**/*_test.rb"]
16
+ t.verbose = true
17
+ end
18
+
19
+ task :default => :test
@@ -0,0 +1,28 @@
1
+ require_relative 'json_utils'
2
+
3
+ class GdsApi::Base
4
+ include GdsApi::JsonUtils
5
+
6
+ def initialize(platform, endpoint_url = nil)
7
+ adapter_name = self.class.to_s.split("::").last.downcase
8
+
9
+ # This should get simpler if we can be more consistent with our domain names
10
+ if endpoint_url
11
+ self.endpoint = endpoint_url
12
+ elsif platform == 'development'
13
+ self.endpoint = "http://#{adapter_name}.dev.gov.uk"
14
+ else
15
+ self.endpoint = "http://#{adapter_name}.#{platform}.alphagov.co.uk"
16
+ end
17
+ end
18
+
19
+ def url_for_slug(slug,options={})
20
+ base = "#{base_url}/#{slug}.json"
21
+ params = options.map { |k,v| "#{k}=#{v}" }
22
+ base = base + "?#{params.join("&")}" unless options.empty?
23
+ base
24
+ end
25
+
26
+ private
27
+ attr_accessor :endpoint
28
+ end
@@ -0,0 +1,13 @@
1
+ require_relative 'base'
2
+
3
+ # This adapter's a bit different from the others as it assumes we know
4
+ # the full URI for a contact and just want to grab its json serialization
5
+ # and convert it to an ostruct.
6
+ class GdsApi::Contactotron < GdsApi::Base
7
+ def initialize
8
+ end
9
+
10
+ def contact_for_uri(uri)
11
+ to_ostruct get_json(uri)
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class OpenStruct
2
+ def to_json
3
+ table.to_json
4
+ end
5
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'base'
2
+
3
+ class GdsApi::Imminence < GdsApi::Base
4
+ def api_url(type,lat,lon,limit=5)
5
+ "#{@endpoint}/places/#{type}.json?limit=#{limit}&lat=#{lat}&lng=#{lon}"
6
+ end
7
+
8
+ def places(type,lat,lon,limit=5)
9
+ places = get_json(api_url(type,lat,lon,limit)) || []
10
+ places.map do |o|
11
+ o['latitude'] = o['location'][0]
12
+ o['longitude'] = o['location'][1]
13
+ o['address'] = [o['address1'], o['address2']].reject { |a| a.nil? or a == '' }.map { |a| a.strip }.join(', ')
14
+ o
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,45 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'ostruct'
4
+ require_relative 'core-ext/openstruct'
5
+
6
+ module GdsApi::JsonUtils
7
+ USER_AGENT = "GDS Api Client v. #{GdsApi::VERSION}"
8
+
9
+ def get_json(url)
10
+ url = URI.parse(url)
11
+ request = url.path
12
+ request = request + "?" + url.query if url.query
13
+
14
+ response = Net::HTTP.start(url.host, url.port) do |http|
15
+ http.get(request, {'Accept' => 'application/json', 'User-Agent' => USER_AGENT})
16
+ end
17
+ if response.code.to_i != 200
18
+ return nil
19
+ else
20
+ return JSON.parse(response.body)
21
+ end
22
+ end
23
+
24
+ def post_json(url,params)
25
+ url = URI.parse(url)
26
+ Net::HTTP.start(url.host, url.port) do |http|
27
+ post_response = http.post(url.path, params.to_json, {'Content-Type' => 'application/json', 'User-Agent' => USER_AGENT})
28
+ if post_response.code == '200'
29
+ return JSON.parse(post_response.body)
30
+ end
31
+ end
32
+ return nil
33
+ end
34
+
35
+ def to_ostruct(object)
36
+ case object
37
+ when Hash
38
+ OpenStruct.new Hash[object.map { |key, value| [key, to_ostruct(value)] }]
39
+ when Array
40
+ object.map { |k| to_ostruct(k) }
41
+ else
42
+ object
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'base'
2
+
3
+ class GdsApi::Panopticon < GdsApi::Base
4
+
5
+ def artefact_for_slug(slug)
6
+ return nil if slug.nil? or slug == ''
7
+
8
+ to_ostruct get_json(url_for_slug(slug))
9
+ end
10
+
11
+ private
12
+ def base_url
13
+ "#{endpoint}/artefacts"
14
+ end
15
+ end
@@ -0,0 +1,44 @@
1
+ module GdsApi::PartMethods
2
+ def part_index(slug)
3
+ parts.index { |p| p.slug == slug }
4
+ end
5
+
6
+ def find_part(slug)
7
+ return nil unless index = part_index(slug)
8
+ parts[index]
9
+ end
10
+
11
+ def part_after(part)
12
+ return nil unless index = part_index(part.slug)
13
+ next_index = index + 1
14
+ return nil if next_index >= parts.length
15
+ parts[next_index]
16
+ end
17
+
18
+ def has_parts?(part)
19
+ prev_part = has_previous_part?(part)
20
+ next_part = has_next_part?(part)
21
+ if prev_part || next_part
22
+ true
23
+ else
24
+ false
25
+ end
26
+ end
27
+
28
+ def has_previous_part?(part)
29
+ index = part_index(part.slug)
30
+ !index.nil? && index > 0 && true
31
+ end
32
+
33
+ def has_next_part?(part)
34
+ index = part_index(part.slug)
35
+ !index.nil? && (index + 1) < parts.length && true
36
+ end
37
+
38
+ def part_before(part)
39
+ return nil unless index = part_index(part.slug)
40
+ previous_index = index - 1
41
+ return nil if previous_index < 0
42
+ parts[previous_index]
43
+ end
44
+ end
@@ -0,0 +1,41 @@
1
+ require_relative 'base'
2
+ require_relative 'part_methods'
3
+
4
+ class GdsApi::Publisher < GdsApi::Base
5
+ def publications
6
+ get_json(base_url)
7
+ end
8
+
9
+ def publication_for_slug(slug,options = {})
10
+ return nil if slug.nil? or slug == ''
11
+
12
+ publication_hash = get_json(url_for_slug(slug, options))
13
+ if publication_hash
14
+ container = to_ostruct(publication_hash)
15
+ container.extend(GdsApi::PartMethods) if container.parts
16
+ convert_updated_date(container)
17
+ container
18
+ else
19
+ return nil
20
+ end
21
+ end
22
+
23
+ def council_for_transaction(transaction,snac_codes)
24
+ if json = post_json("#{@endpoint}/local_transactions/#{transaction.slug}/verify_snac.json",{'snac_codes' => snac_codes})
25
+ return json['snac']
26
+ else
27
+ return nil
28
+ end
29
+ end
30
+
31
+ private
32
+ def convert_updated_date(container)
33
+ if container.updated_at && container.updated_at.class == String
34
+ container.updated_at = Time.parse(container.updated_at)
35
+ end
36
+ end
37
+
38
+ def base_url
39
+ "#{@endpoint}/publications"
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module GdsApi
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,15 @@
1
+ require 'test_helper'
2
+ require 'gds_api/contactotron'
3
+
4
+ class ContactotronApiTest < MiniTest::Unit::TestCase
5
+ def api
6
+ GdsApi::Contactotron.new
7
+ end
8
+
9
+ def test_should_fetch_and_parse_JSON_into_ostruct
10
+ uri = "http://contactotron.platform/contacts/1"
11
+ stub_request(:get, uri).
12
+ to_return(:status => 200, :body => '{"detail":"value"}')
13
+ assert_equal OpenStruct, api.contact_for_uri(uri).class
14
+ end
15
+ end
@@ -0,0 +1,58 @@
1
+ require 'test_helper'
2
+ require 'gds_api/panopticon'
3
+
4
+ class PanopticonApiTest < MiniTest::Unit::TestCase
5
+ EXPECTED_ENDPOINT = 'http://panopticon.test.alphagov.co.uk'
6
+
7
+ def api
8
+ GdsApi::Panopticon.new('test')
9
+ end
10
+
11
+ def test_given_a_slug__should_fetch_artefact_from_panopticon
12
+ slug = 'an-artefact'
13
+ artefact_json = { name: 'An artefact' }.to_json
14
+ stub_request(:get, "#{EXPECTED_ENDPOINT}/artefacts/#{slug}.json").to_return(body: artefact_json)
15
+
16
+ artefact = api.artefact_for_slug(slug)
17
+ assert_equal 'An artefact', artefact.name
18
+ end
19
+
20
+ def should_fetch_and_parse_JSON_into_hash
21
+ url = "#{EXPECTED_ENDPOINT}/some.json"
22
+ stub_request(:get, url).to_return(body: {}.to_json)
23
+
24
+ assert_equal Hash, api.get_json(url).class
25
+ end
26
+
27
+ def test_should_return_nil_if_404_returned_from_EXPECTED_ENDPOINT
28
+ url = "#{EXPECTED_ENDPOINT}/some.json"
29
+ stub_request(:get, url).to_return(status: Rack::Utils.status_code(:not_found))
30
+
31
+ assert_nil api.get_json(url)
32
+ end
33
+
34
+ def test_should_construct_correct_url_for_a_slug
35
+ assert_equal "#{EXPECTED_ENDPOINT}/artefacts/slug.json", api.url_for_slug('slug')
36
+ end
37
+
38
+ def artefact_with_contact_json
39
+ {
40
+ name: 'An artefact',
41
+ slug: 'an-artefact',
42
+ contact: {
43
+ name: 'Department for Environment, Food and Rural Affairs (Defra)',
44
+ email_address: 'helpline@defra.gsi.gov.uk'
45
+ }
46
+ }.to_json
47
+ end
48
+
49
+ def test_contacts_should_be_deserialised_into_whole_objects
50
+ slug = 'an-artefact'
51
+ artefact_json = artefact_with_contact_json
52
+ stub_request(:get, "#{EXPECTED_ENDPOINT}/artefacts/#{slug}.json").to_return(body: artefact_json)
53
+
54
+ artefact = api.artefact_for_slug(slug)
55
+ assert_equal 'Department for Environment, Food and Rural Affairs (Defra)', artefact.contact.name
56
+ assert_equal 'helpline@defra.gsi.gov.uk', artefact.contact.email_address
57
+ end
58
+ end
@@ -0,0 +1,122 @@
1
+ require 'test_helper'
2
+ require 'gds_api/publisher'
3
+
4
+ class GdsApi::PublisherTest < MiniTest::Unit::TestCase
5
+ EXPECTED_ENDPOINT = "http://publisher.test.alphagov.co.uk"
6
+
7
+ def api
8
+ GdsApi::Publisher.new("test")
9
+ end
10
+
11
+ def test_given_a_slug__should_go_get_resource_from_publisher_app
12
+ slug = "a-publication"
13
+ publication = %@{"audiences":[""],
14
+ "slug":"#{slug}",
15
+ "tags":"",
16
+ "updated_at":"2011-07-28T11:53:03+00:00",
17
+ "type":"answer",
18
+ "body":"Something",
19
+ "title":"A publication"}@
20
+ stub_request(:get, "#{EXPECTED_ENDPOINT}/publications/#{slug}.json").to_return(
21
+ :body => publication,:status=>200)
22
+
23
+ pub = api.publication_for_slug(slug)
24
+
25
+ assert_equal "Something",pub.body
26
+ end
27
+
28
+ def test_should_optionally_accept_an_edition_id
29
+ slug = "a-publication"
30
+ publication = %@{"audiences":[""],
31
+ "slug":"#{slug}",
32
+ "tags":"",
33
+ "updated_at":"2011-07-28T11:53:03+00:00",
34
+ "type":"answer",
35
+ "body":"Something",
36
+ "title":"A publication"}@
37
+ stub_request(:get, "#{EXPECTED_ENDPOINT}/publications/#{slug}.json?edition=678").to_return(
38
+ :body => publication,:status=>200)
39
+
40
+ pub = api.publication_for_slug(slug,{:edition => 678})
41
+ end
42
+
43
+ def test_should_fetch_and_parse_json_into_hash
44
+ url = "#{EXPECTED_ENDPOINT}/some.json"
45
+ stub_request(:get, url).to_return(
46
+ :body => "{}",:status=>200)
47
+ assert_equal Hash,api.get_json(url).class
48
+ end
49
+
50
+ def test_should_return_nil_if_404_returned_from_endpoint
51
+ url = "#{EXPECTED_ENDPOINT}/some.json"
52
+ stub_request(:get, url).to_return(
53
+ :body => "{}",:status=>404)
54
+ assert_nil api.get_json(url)
55
+ end
56
+
57
+ def test_should_construct_correct_url_for_a_slug
58
+ assert_equal "#{EXPECTED_ENDPOINT}/publications/slug.json", api.url_for_slug("slug")
59
+ end
60
+
61
+ def publication_with_parts(slug)
62
+ publication = %@{"audiences":[""],
63
+ "slug":"#{slug}",
64
+ "tags":"",
65
+ "updated_at":"2011-07-28T11:53:03+00:00",
66
+ "type":"guide",
67
+ "body":"Something",
68
+ "parts" : [
69
+ {
70
+ "body" : "You may be financially protected",
71
+ "number" : 1,
72
+ "slug" : "introduction",
73
+ "title" : "Introduction"
74
+ },
75
+ {
76
+ "body" : "All companies selling packag",
77
+ "number" : 2,
78
+ "slug" : "if-you-booked-a-package-holiday",
79
+ "title" : "If you booked a package holiday"
80
+ },
81
+ {
82
+ "body" : "##Know your rights when you b",
83
+ "number" : 3,
84
+ "slug" : "if-you-booked-your-trip-independently",
85
+ "title" : "If you booked your trip independently"
86
+ }],
87
+ "title":"A publication"}@
88
+
89
+ end
90
+
91
+ def test_parts_should_be_deserialised_into_whole_objects
92
+ slug = "a-publication"
93
+ publication = publication_with_parts(slug)
94
+ stub_request(:get, "#{EXPECTED_ENDPOINT}/publications/#{slug}.json").to_return(
95
+ :body => publication,:status=>200)
96
+
97
+ pub = api.publication_for_slug(slug)
98
+ assert_equal 3, pub.parts.size
99
+ assert_equal "introduction", pub.parts.first.slug
100
+ end
101
+
102
+ def test_a_publication_with_parts_should_have_part_specific_methods
103
+ slug = "a-publication"
104
+ publication = publication_with_parts(slug)
105
+ stub_request(:get, "#{EXPECTED_ENDPOINT}/publications/#{slug}.json").to_return(
106
+ :body => publication,:status=>200)
107
+
108
+ pub = api.publication_for_slug(slug)
109
+ assert_equal pub.part_index("introduction"),0
110
+ end
111
+
112
+ def test_updated_at_should_be_a_time_on_deserialisation
113
+ slug = "a-publication"
114
+ publication = publication_with_parts(slug)
115
+ stub_request(:get, "#{EXPECTED_ENDPOINT}/publications/#{slug}.json").to_return(
116
+ :body => publication,:status=>200)
117
+
118
+ pub = api.publication_for_slug(slug)
119
+ assert_equal Time, pub.updated_at.class
120
+ end
121
+
122
+ end
@@ -0,0 +1,7 @@
1
+ require 'minitest/autorun'
2
+ require 'webmock/minitest'
3
+ require 'rack/utils'
4
+ require 'simplecov'
5
+
6
+ SimpleCov.start
7
+ WebMock.disable_net_connect!
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gds-api-adapters
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - James Stewart
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-06 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: plek
16
+ requirement: &70189845417140 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70189845417140
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70189845416360 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 0.9.2.2
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70189845416360
36
+ - !ruby/object:Gem::Dependency
37
+ name: webmock
38
+ requirement: &70189845415640 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '1.7'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70189845415640
47
+ - !ruby/object:Gem::Dependency
48
+ name: rack
49
+ requirement: &70189845414900 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70189845414900
58
+ - !ruby/object:Gem::Dependency
59
+ name: simplecov
60
+ requirement: &70189845414200 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - =
64
+ - !ruby/object:Gem::Version
65
+ version: 0.4.2
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70189845414200
69
+ description:
70
+ email:
71
+ - jystewart@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - lib/gds_api/base.rb
77
+ - lib/gds_api/contactotron.rb
78
+ - lib/gds_api/core-ext/openstruct.rb
79
+ - lib/gds_api/imminence.rb
80
+ - lib/gds_api/json_utils.rb
81
+ - lib/gds_api/panopticon.rb
82
+ - lib/gds_api/part_methods.rb
83
+ - lib/gds_api/publisher.rb
84
+ - lib/gds_api/version.rb
85
+ - README.md
86
+ - Rakefile
87
+ - test/contactotron_api_test.rb
88
+ - test/panopticon_api_test.rb
89
+ - test/publisher_api_test.rb
90
+ - test/test_helper.rb
91
+ homepage:
92
+ licenses: []
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 1.8.10
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: Adapters to work with GDS APIs
115
+ test_files:
116
+ - test/contactotron_api_test.rb
117
+ - test/panopticon_api_test.rb
118
+ - test/publisher_api_test.rb
119
+ - test/test_helper.rb