gds-api-adapters 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,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