govuk-registers-api-client 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 959a3fe6555fd80a48da2138aadaa41877e2c3c406a6688fac9f994805f4500d
4
- data.tar.gz: 1a7499afdcfa437839871d149d133b3d8f3ea428539c009ed1632188373c7d0a
3
+ metadata.gz: ab771bc838779644bbd8763beea0c5a4c1138a298011af39a4ac4121068c3d40
4
+ data.tar.gz: ae34e34c99dc13883a2865393b54aa386fa04cf0174f835bfd60fa1dae582699
5
5
  SHA512:
6
- metadata.gz: 29934a7dd3ebea408e6d2507d9c05d97c005a4d7a1f338266cf9399a2c6749fe261a9e3b68bdf07fc8a2bf13ea6b928f4775bc93fadbbc3dcdad5651c4ecd109
7
- data.tar.gz: b6eaec466707feb72a7a2a78182542d001729d76115c3d252e6cf4c93041d2a2822e9c084a771083cbb877e2956640c1788b8780d90acc03b9cfb2f06b384aa1
6
+ metadata.gz: cbe860777cef29b1ad7ba40fd47dcc3b10319eff06623637ad03deb83fd8e6771dd60c694acd92642c676270b78c9cfd631e4bda8e62d415bc232d858ea998ee
7
+ data.tar.gz: 735d24e0805542891a4cd14a0504b40455ad8e2d77921e267775abefbf07bb4b4d284c78146ee239b608e182e5f5ce569b5b09ec9fb1eb5b697d560c142257fe
@@ -0,0 +1,5 @@
1
+ ### What did you do? (steps to reproduce)
2
+
3
+ ### What happened? (actual results)
4
+
5
+ ### What should have happened? (expected results)
@@ -0,0 +1,7 @@
1
+ ### Context
2
+
3
+
4
+ ### Changes proposed in this pull request
5
+
6
+
7
+ ### Guidance to review
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
@@ -0,0 +1 @@
1
+ 2.5.0
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.5.0
@@ -0,0 +1 @@
1
+ 1.0.1
@@ -0,0 +1,56 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.5.2)
5
+ public_suffix (>= 2.0.2, < 4.0)
6
+ crack (0.4.3)
7
+ safe_yaml (~> 1.0.0)
8
+ diff-lcs (1.3)
9
+ domain_name (0.5.20170404)
10
+ unf (>= 0.0.5, < 1.0.0)
11
+ hashdiff (0.3.7)
12
+ http-cookie (1.0.3)
13
+ domain_name (~> 0.5)
14
+ mime-types (3.1)
15
+ mime-types-data (~> 3.2015)
16
+ mime-types-data (3.2016.0521)
17
+ netrc (0.11.0)
18
+ public_suffix (3.0.1)
19
+ rake (12.3.0)
20
+ rest-client (2.0.2)
21
+ http-cookie (>= 1.0.2, < 2.0)
22
+ mime-types (>= 1.16, < 4.0)
23
+ netrc (~> 0.8)
24
+ rspec (3.7.0)
25
+ rspec-core (~> 3.7.0)
26
+ rspec-expectations (~> 3.7.0)
27
+ rspec-mocks (~> 3.7.0)
28
+ rspec-core (3.7.0)
29
+ rspec-support (~> 3.7.0)
30
+ rspec-expectations (3.7.0)
31
+ diff-lcs (>= 1.2.0, < 2.0)
32
+ rspec-support (~> 3.7.0)
33
+ rspec-mocks (3.7.0)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.7.0)
36
+ rspec-support (3.7.0)
37
+ safe_yaml (1.0.4)
38
+ unf (0.1.4)
39
+ unf_ext
40
+ unf_ext (0.0.7.4)
41
+ webmock (3.1.1)
42
+ addressable (>= 2.3.6)
43
+ crack (>= 0.3.2)
44
+ hashdiff
45
+
46
+ PLATFORMS
47
+ ruby
48
+
49
+ DEPENDENCIES
50
+ rake
51
+ rest-client (>= 2.0.1)
52
+ rspec (~> 3.7)
53
+ webmock
54
+
55
+ BUNDLED WITH
56
+ 1.16.0
data/README.md CHANGED
@@ -24,7 +24,7 @@ Registers are authoritative lists of information. The data is owned by [custodia
24
24
 
25
25
  In your Gemfile, add:
26
26
  ```
27
- gem 'govuk-register-api-client'
27
+ gem 'govuk-registers-api-client'
28
28
  ```
29
29
 
30
30
  ## Get started
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rspec/core/rake_task'
8
+ RSpec::Core::RakeTask.new(:spec) do |t|
9
+ t.rspec_opts = %w(--format documentation --colour)
10
+ end
11
+
12
+ task default: :spec
13
+
14
+ require 'rdoc/task'
15
+ RDoc::Task.new(:rdoc) do |rd|
16
+ rd.main = 'README.md'
17
+ rd.rdoc_files.include('README.md', 'lib/**/*.rb')
18
+ rd.rdoc_dir = 'rdoc'
19
+ end
20
+
21
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # stub: govuk-registers-api-client 1.0.1 ruby lib
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "govuk-registers-api-client".freeze
6
+ s.version = File.read('.version').chomp
7
+
8
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
+ s.require_paths = ["lib".freeze]
10
+ s.authors = ["GOV.UK Registers Team (Government Digital Service)".freeze]
11
+ s.date = "2017-11-06"
12
+ s.email = "registers ~@nospam@~ digital.cabinet-office.gov.uk".freeze
13
+ s.extra_rdoc_files = ["README.md".freeze]
14
+ s.files = `git ls-files -z`.split("\x0").reject do |f|
15
+ f.match(%r{^(test|spec|features)/})
16
+ end
17
+ s.homepage = "https://github.com/openregister/govuk-registers-api-client".freeze
18
+ s.licenses = ["MIT".freeze]
19
+ s.rdoc_options = ["--main".freeze, "README.md".freeze]
20
+ s.rubygems_version = "2.6.12".freeze
21
+ s.summary = "Client library for GOV.UK Registers".freeze
22
+
23
+ if s.respond_to? :specification_version then
24
+ s.specification_version = 4
25
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
26
+ s.add_runtime_dependency(%q<rest-client>.freeze, ["~> 2"])
27
+ else
28
+ s.add_dependency(%q<rest-client>.freeze, ["~> 2"])
29
+ end
30
+ else
31
+ s.add_dependency(%q<rest-client>.freeze, ["~> 2"])
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ module DataStore
2
+ def add_item(item)
3
+ end
4
+
5
+ def append_entry(entry)
6
+ end
7
+
8
+ def get_item(item_hash)
9
+ end
10
+
11
+ def get_items
12
+ end
13
+
14
+ def get_entry(entry_type, entry_number)
15
+ end
16
+
17
+ def get_entries(entry_type)
18
+ end
19
+
20
+ def get_record(entry_type, key)
21
+ end
22
+
23
+ def get_records(entry_type)
24
+ end
25
+
26
+ def get_latest_entry_number(entry_type)
27
+ end
28
+
29
+ def get_root_hash
30
+ end
31
+
32
+ def set_root_hash(root_hash)
33
+ end
34
+
35
+ def after_load
36
+ end
37
+ end
@@ -0,0 +1,51 @@
1
+ module RegistersClient
2
+ class Entry
3
+ def initialize(rsf_line, entry_number, entry_type)
4
+ @rsf_line = rsf_line
5
+ @entry_number = entry_number
6
+ @entry_type = entry_type
7
+
8
+ @parsed_entry = nil
9
+ end
10
+
11
+ def entry_number
12
+ @entry_number
13
+ end
14
+
15
+ def key
16
+ get_entry[:key]
17
+ end
18
+
19
+ def timestamp
20
+ get_entry[:timestamp]
21
+ end
22
+
23
+ def item_hash
24
+ get_entry[:item_hash]
25
+ end
26
+
27
+ def entry_type
28
+ @entry_type
29
+ end
30
+
31
+ def value
32
+ get_entry
33
+ end
34
+
35
+ private
36
+
37
+ def get_entry
38
+ @parsed_entry ||= parse_entry
39
+ @parsed_entry
40
+ end
41
+
42
+ def parse_entry
43
+ params = @rsf_line.split("\t")
44
+
45
+ # Deallocate rsf line
46
+ @rsf_line = nil
47
+
48
+ { key: params[2], timestamp: params[3], item_hash: params[4] }
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,25 @@
1
+ module RegistersClient
2
+ class EntryCollection
3
+ include Enumerable
4
+
5
+ def initialize(data, page_size = 100)
6
+ @data = data
7
+ @page_size = page_size
8
+ end
9
+
10
+ def each
11
+ @data.each do |entry|
12
+ yield entry
13
+ end
14
+ end
15
+
16
+ def page(page = 1)
17
+ if @data.length <= @page_size
18
+ @data
19
+ else
20
+ start_index = (page - 1) * @page_size
21
+ @data.slice(start_index, @page_size)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ class InvalidHashValueError < StandardError
2
+ def initialize(msg='Value must be a sha-256 hash string prefixed with "sha-256:"')
3
+ super
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ class InvalidRegisterError < StandardError
2
+ end
@@ -0,0 +1,106 @@
1
+ require 'data_store'
2
+ require 'date'
3
+ require 'record_collection'
4
+ require 'record_map_collection'
5
+ require 'entry_collection'
6
+ require 'item_collection'
7
+ require 'entry'
8
+ require 'item'
9
+ require 'record'
10
+
11
+ module RegistersClient
12
+ class InMemoryDataStore
13
+ include DataStore
14
+
15
+ EMPTY_ROOT_HASH = 'sha-256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
16
+
17
+ def initialize(config_options)
18
+ @config_options = config_options
19
+
20
+ @data = {
21
+ records: { user: {}, system: {} },
22
+ entries: { user: [], system: [] },
23
+ items: {},
24
+ root_hash: EMPTY_ROOT_HASH
25
+ }
26
+
27
+ end
28
+
29
+ def add_item(item)
30
+ @data[:items][item.hash.to_s] = item
31
+ end
32
+
33
+ def append_entry(entry)
34
+ entry_type = entry.entry_type == 'user' ? :user : :system
35
+
36
+ @data[:entries][entry_type] << entry
37
+
38
+ if !@data[:records][entry_type].key?(entry.key)
39
+ @data[:records][entry_type][entry.key] = []
40
+ end
41
+
42
+ @data[:records][entry_type][entry.key] << entry.entry_number
43
+ end
44
+
45
+ def get_item(item_hash)
46
+ @data[:items].has_key?(item_hash) ? @data[:items][item_hash] : nil
47
+ end
48
+
49
+ def get_items
50
+ ItemCollection.new(@data[:items].values)
51
+ end
52
+
53
+ def get_entry(entry_type, entry_number)
54
+ if (entry_number < 1 || entry_number > @data[:entries][entry_type].count)
55
+ nil
56
+ else
57
+ @data[:entries][entry_type][entry_number - 1]
58
+ end
59
+ end
60
+
61
+ def get_record(entry_type, key)
62
+ if (@data[:records][entry_type].has_key?(key))
63
+ record_entry_numbers = @data[:records][entry_type][key]
64
+ entry = get_data[:entries][entry_type][record_entry_numbers.last - 1]
65
+
66
+ Record.new(entry, get_data[:items][entry.item_hash])
67
+ end
68
+ end
69
+
70
+ def get_entries(entry_type)
71
+ EntryCollection.new(get_data[:entries][entry_type], @config_options.fetch(:page_size))
72
+ end
73
+
74
+ def get_records(entry_type)
75
+ RecordCollection.new(get_data[:records][entry_type].map do |_k, record_entry_numbers|
76
+ entry = get_data[:entries][entry_type][record_entry_numbers.last - 1]
77
+ item = get_data[:items][entry.item_hash]
78
+
79
+ Record.new(entry, item)
80
+ end, @config_options.fetch(:page_size))
81
+ end
82
+
83
+ def get_latest_entry_number(entry_type)
84
+ entries = @data[:entries][entry_type]
85
+ entry = entries.any? ? entries.last : nil
86
+ entry.nil? ? 0 : entry.entry_number
87
+ end
88
+
89
+ def get_root_hash
90
+ @data[:root_hash]
91
+ end
92
+
93
+ def set_root_hash(root_hash)
94
+ @data[:root_hash] = root_hash
95
+ end
96
+
97
+ def after_load
98
+ end
99
+
100
+ private
101
+
102
+ def get_data
103
+ @data
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,47 @@
1
+ require 'json'
2
+
3
+ module RegistersClient
4
+ class Item
5
+ def initialize(rsf_line)
6
+ @item_json = rsf_line.split("\t")[1]
7
+ @item_hash = 'sha-256:' + Digest::SHA256.hexdigest(@item_json)
8
+
9
+ @parsed_item = nil
10
+ end
11
+
12
+ def hash
13
+ @item_hash
14
+ end
15
+
16
+ def value
17
+ get_item
18
+ end
19
+
20
+ def has_end_date
21
+ if !@item_json.nil?
22
+ !@item_json.index("end-date").nil?
23
+ else
24
+ !@parsed_item['end-date'].nil?
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def get_item
31
+ if @parsed_item.nil?
32
+ @parsed_item = parse_item
33
+ end
34
+
35
+ @parsed_item
36
+ end
37
+
38
+ def parse_item
39
+ parsed_item = JSON.parse(@item_json)
40
+
41
+ # Deallocate item JSON
42
+ @item_json = nil
43
+
44
+ parsed_item
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,25 @@
1
+ module RegistersClient
2
+ class ItemCollection
3
+ include Enumerable
4
+
5
+ def initialize(data, page_size = 100)
6
+ @data = data
7
+ @page_size = page_size
8
+ end
9
+
10
+ def each
11
+ @data.each do |item|
12
+ yield item
13
+ end
14
+ end
15
+
16
+ def page(page = 1)
17
+ if @data.length <= @page_size
18
+ @data
19
+ else
20
+ start_index = (page - 1) * @page_size
21
+ @data.slice(start_index, @page_size)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ module RegistersClient
2
+ class Record
3
+ def initialize(entry, item)
4
+ @entry = entry
5
+ @item = item
6
+ end
7
+
8
+ def entry
9
+ @entry
10
+ end
11
+
12
+ def item
13
+ @item
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ module RegistersClient
2
+ class RecordCollection
3
+ include Enumerable
4
+
5
+ def initialize(data, page_size = 100)
6
+ @data = data
7
+ @page_size = page_size
8
+ end
9
+
10
+ def each
11
+ @data.each do |record|
12
+ yield record
13
+ end
14
+ end
15
+
16
+ def page(page = 1)
17
+ if @data.length <= @page_size
18
+ @data
19
+ else
20
+ start_index = (page - 1) * @page_size
21
+ @data.slice(start_index, @page_size)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module RegistersClient
2
+ class RecordMapCollection
3
+ include Enumerable
4
+
5
+ def initialize(data, page_size = 100)
6
+ @data = data
7
+ @page_size = page_size
8
+ end
9
+
10
+ def each
11
+ @data.each do |key,val|
12
+ result = {key: key, records: val}
13
+ yield result
14
+ end
15
+ end
16
+
17
+ def get_records_for_key(key)
18
+ @data.fetch(key)
19
+ end
20
+
21
+ def paginator
22
+ @data.each_slice(@page_size)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,177 @@
1
+ require 'rest-client'
2
+ require 'json'
3
+ require 'exceptions/invalid_register_error'
4
+ require 'exceptions/invalid_hash_value_error'
5
+
6
+ module RegistersClient
7
+ class RegisterClient
8
+ def initialize(register_url, data_store, page_size)
9
+ @register_url = register_url
10
+ @data_store = data_store
11
+ @page_size = page_size
12
+
13
+ refresh_data
14
+ end
15
+
16
+ def get_item(item_hash)
17
+ @data_store.get_item(item_hash)
18
+ end
19
+
20
+ def get_items
21
+ @data_store.get_items
22
+ end
23
+
24
+ def get_entry(entry_number)
25
+ @data_store.get_entry(:user, entry_number)
26
+ end
27
+
28
+ def get_entries(since_entry_number = 0)
29
+ EntryCollection.new(get_entries_subset_for_entry_type(since_entry_number, :user), @page_size)
30
+ end
31
+
32
+ def get_record(key)
33
+ @data_store.get_record(:user, key)
34
+ end
35
+
36
+ def get_records
37
+ @data_store.get_records(:user)
38
+ end
39
+
40
+ def get_metadata_records
41
+ @data_store.get_records(:system)
42
+ end
43
+
44
+ def get_field_definitions
45
+ ordered_fields = get_register_definition.item.value['fields']
46
+ ordered_records = ordered_fields.map { |f| get_metadata_records.find { |record| record.entry.key == "field:#{f}" } }
47
+ @field_definitions ||= RecordCollection.new(ordered_records, @page_size)
48
+ @field_definitions
49
+ end
50
+
51
+ def get_register_definition
52
+ get_metadata_records.select { |record| record.entry.key.start_with?('register:') }.first
53
+ end
54
+
55
+ def get_custodian
56
+ get_metadata_records.select { |record| record.entry.key == 'custodian'}.first
57
+ end
58
+
59
+ def get_records_with_history(since_entry_number = 0)
60
+ records_with_history = get_records_with_history_for_entry_type(since_entry_number, :user)
61
+
62
+ RecordMapCollection.new(records_with_history, @page_size)
63
+ end
64
+
65
+ def get_metadata_records_with_history(since_entry_number = 0)
66
+ metadata_records_with_history = get_records_with_history_for_entry_type(since_entry_number, :system)
67
+
68
+ RecordMapCollection.new(metadata_records_with_history, @page_size)
69
+ end
70
+
71
+ def get_current_records
72
+ RecordCollection.new(get_records.select { |record| !record.item.has_end_date }, @page_size)
73
+ end
74
+
75
+ def get_expired_records
76
+ RecordCollection.new(get_records.select { |record| record.item.has_end_date }, @page_size)
77
+ end
78
+
79
+ def get_root_hash
80
+ root_hash = @data_store.get_root_hash
81
+ if !is_valid_hash_value(root_hash)
82
+ raise InvalidHashValueError
83
+ end
84
+ return root_hash
85
+ end
86
+
87
+ def refresh_data
88
+ user_entry_number = @data_store.get_latest_entry_number(:user)
89
+ rsf = download_rsf(user_entry_number)
90
+
91
+ validate_register_integrity(rsf, user_entry_number)
92
+ update_data_from_rsf(rsf, user_entry_number, @data_store)
93
+ end
94
+
95
+ private
96
+
97
+ def get_entries_subset_for_entry_type(since_entry_number, entry_type)
98
+ start_index = !since_entry_number.nil? && since_entry_number > 0 ? since_entry_number : 0
99
+ current_entry_number = @data_store.get_latest_entry_number(entry_type)
100
+ length = current_entry_number - start_index
101
+
102
+ @data_store.get_entries(entry_type).to_a.slice(start_index, length)
103
+ end
104
+
105
+ def get_records_with_history_for_entry_type(since_entry_number, entry_type)
106
+ records_with_history = {}
107
+
108
+ get_entries_subset_for_entry_type(since_entry_number, entry_type).each do |entry|
109
+ if (!records_with_history.key?(entry.key))
110
+ records_with_history[entry.key] = []
111
+ end
112
+
113
+ item = @data_store.get_item(entry.item_hash)
114
+ records_with_history[entry.key] << Record.new(entry, item)
115
+ end
116
+
117
+ records_with_history
118
+ end
119
+
120
+ def get_register_proof
121
+ @register_proof = JSON.parse(register_http_request('proof/register/merkle:sha-256'))
122
+ end
123
+
124
+ def validate_register_integrity(rsf, current_entry_number)
125
+ rsf_lines = rsf.split("\n")
126
+ expected_root_hash = rsf_lines[0].split("\t")[1]
127
+ local_root_hash = @data_store.get_root_hash
128
+
129
+ if (local_root_hash != expected_root_hash)
130
+ raise InvalidRegisterError, 'Register has been reloaded with different data - root hashes do not match'
131
+ end
132
+ end
133
+
134
+ def download_rsf(start_entry_number)
135
+ register_http_request("download-rsf/#{start_entry_number}")
136
+ end
137
+
138
+ def update_data_from_rsf(rsf, user_entry_number, data_store)
139
+ system_entry_number = @data_store.get_latest_entry_number(:system)
140
+
141
+ rsf.each_line do |line|
142
+ line.slice!("\n")
143
+ params = line.split("\t")
144
+ command = params[0]
145
+
146
+ if command == 'add-item'
147
+ data_store.add_item(RegistersClient::Item.new(line))
148
+ elsif command == 'append-entry'
149
+ if params[1] == 'user'
150
+ user_entry_number += 1
151
+
152
+ entry = Entry.new(line, user_entry_number, params[1])
153
+ else
154
+ system_entry_number += 1
155
+
156
+ entry = Entry.new(line, system_entry_number, params[1])
157
+ end
158
+
159
+ data_store.append_entry(entry)
160
+ elsif command == 'assert-root-hash'
161
+ root_hash = line.split("\t")[1]
162
+ data_store.set_root_hash(root_hash)
163
+ end
164
+ end
165
+
166
+ data_store.after_load
167
+ end
168
+
169
+ def register_http_request(path)
170
+ RestClient.get(@register_url.merge(path).to_s)
171
+ end
172
+
173
+ def is_valid_hash_value(hash_value)
174
+ (/sha-256:+\h{64}/) === hash_value
175
+ end
176
+ end
177
+ end
@@ -2,7 +2,7 @@ require 'register_client'
2
2
  require 'in_memory_data_store'
3
3
 
4
4
  module RegistersClient
5
- VERSION = '1.0.0'
5
+ VERSION = '1.0.1'
6
6
  class RegisterClientManager
7
7
  def initialize(config_options = {})
8
8
  @config_options = defaults.merge(config_options)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govuk-registers-api-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - GOV.UK Registers Team (Government Digital Service)
@@ -31,9 +31,30 @@ extensions: []
31
31
  extra_rdoc_files:
32
32
  - README.md
33
33
  files:
34
+ - ".github/ISSUE_TEMPLATE.md"
35
+ - ".github/PULL_REQUEST_TEMPLATE.md"
36
+ - ".rspec"
37
+ - ".ruby-version"
38
+ - ".travis.yml"
39
+ - ".version"
34
40
  - Gemfile
41
+ - Gemfile.lock
35
42
  - LICENSE
36
43
  - README.md
44
+ - Rakefile
45
+ - govuk-registers-api-client.gemspec
46
+ - lib/data_store.rb
47
+ - lib/entry.rb
48
+ - lib/entry_collection.rb
49
+ - lib/exceptions/invalid_hash_value_error.rb
50
+ - lib/exceptions/invalid_register_error.rb
51
+ - lib/in_memory_data_store.rb
52
+ - lib/item.rb
53
+ - lib/item_collection.rb
54
+ - lib/record.rb
55
+ - lib/record_collection.rb
56
+ - lib/record_map_collection.rb
57
+ - lib/register_client.rb
37
58
  - lib/register_client_manager.rb
38
59
  homepage: https://github.com/openregister/govuk-registers-api-client
39
60
  licenses: