usps-imis-api 0.9.8 → 0.9.10.pre.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.
data/Rakefile DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- require 'rubocop/rake_task'
9
-
10
- RuboCop::RakeTask.new
11
-
12
- task default: %i[spec rubocop]
data/bin/console DELETED
@@ -1,21 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'usps/imis'
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # Configure from the environment automatically
11
- require 'dotenv/load'
12
- Usps::Imis.configure do |config|
13
- config.environment = :development
14
- config.imis_id_query_name = ENV['IMIS_ID_QUERY_NAME']
15
-
16
- config.username = ENV['IMIS_USERNAME']
17
- config.password = ENV['IMIS_PASSWORD']
18
- end
19
-
20
- require "irb"
21
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
@@ -1,171 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Usps::Imis::Api do
6
- let(:api) { described_class.new }
7
-
8
- describe '#initialize' do
9
- it 'skips authentication' do
10
- # rubocop:disable RSpec/AnyInstance
11
- expect_any_instance_of(described_class).not_to receive(:authenticate)
12
- # rubocop:enable RSpec/AnyInstance
13
-
14
- described_class.new(skip_authentication: true)
15
- end
16
-
17
- it 'stores the initial imis_id' do
18
- api = described_class.new(imis_id: 42)
19
-
20
- expect(api.imis_id).to eq(42)
21
- end
22
- end
23
-
24
- describe '#imis_id_for' do
25
- it 'gets the iMIS ID' do
26
- expect(api.imis_id_for('E231625')).to eq(31092)
27
- end
28
-
29
- context 'with a query error' do
30
- before { allow(api).to receive(:query).and_raise('Stub') }
31
-
32
- it 'wraps errors' do
33
- expect { api.imis_id_for('E231625') }.to raise_error(
34
- Usps::Imis::Errors::NotFoundError, 'Member not found'
35
- )
36
- end
37
- end
38
- end
39
-
40
- describe '#query' do
41
- let(:query) { api.query('$/ABC/ExampleQueryAll', {}) }
42
-
43
- before do
44
- allow(query).to receive(:fetch).and_return(
45
- { 'Items' => { '$values' => [{ 'key1' => 'value1' }] }, 'HasNext' => true, 'NextOffset' => 1 },
46
- { 'Items' => { '$values' => [{ 'key1' => 'value2' }] }, 'HasNext' => false, 'NextOffset' => 0 }
47
- )
48
- end
49
-
50
- it 'collects all query results' do
51
- expect(query.to_a).to eq([{ 'key1' => 'value1' }, { 'key1' => 'value2' }])
52
- end
53
- end
54
-
55
- describe '#put' do
56
- before { api.imis_id = 31092 }
57
-
58
- it 'sends an update' do
59
- expect(api.on('ABC_ASC_Individual_Demog').put_fields('TotMMS' => 15)).to be_a(Hash)
60
- end
61
-
62
- context 'when receiving a response error' do
63
- let(:warning_text) do
64
- <<~WARNING.chomp
65
- Usps::Imis::Errors::ResponseError: [INTERNAL_SERVER_ERROR] The iMIS API returned an error.
66
- Something went wrong
67
- WARNING
68
- end
69
-
70
- before do
71
- error = Struct.new(:code, :body).new('500', 'Something went wrong')
72
-
73
- # rubocop:disable RSpec/AnyInstance
74
- allow_any_instance_of(Net::HTTP).to receive(:request).and_return(error)
75
- # rubocop:enable RSpec/AnyInstance
76
- end
77
-
78
- it 'wraps the error' do
79
- expect { api.on('ABC_ASC_Individual_Demog').put_fields('TotMMS' => 15) }.to(
80
- raise_error(Usps::Imis::Errors::ResponseError, warning_text)
81
- )
82
- end
83
- end
84
- end
85
-
86
- describe '#with' do
87
- it 'sends an update from put' do
88
- expect(
89
- api.with(31092) { on('ABC_ASC_Individual_Demog').put_fields('TotMMS' => 15) }
90
- ).to be_a(Hash)
91
- end
92
-
93
- it 'sends an update from update' do
94
- expect(api.with(31092) { update(mm: 15) }.first).to be_a(Hash)
95
- end
96
-
97
- it 'uses a panel correctly' do
98
- expect(api.with(6374) { panels.vsc.get(1433) }).to be_a(Hash)
99
- end
100
-
101
- it 'blocks calling imis_id=' do
102
- expect do
103
- api.with(31092) { self.imis_id = 31092 }
104
- end.to raise_error(Usps::Imis::Errors::LockedIdError, 'Cannot change iMIS ID while locked')
105
- end
106
-
107
- it 'blocks calling imis_id_for' do
108
- expect do
109
- api.with(31092) { imis_id_for('E231625') }
110
- end.to raise_error(Usps::Imis::Errors::LockedIdError, 'Cannot change iMIS ID while locked')
111
- end
112
- end
113
-
114
- describe '#on' do
115
- it 'returns a BusinessObject without a block' do
116
- expect(api.on('ABC_ASC_Individual_Demog')).to be_a(Usps::Imis::BusinessObject)
117
- end
118
-
119
- it 'sends an update from put', :aggregate_failures do
120
- result = api.with(31092) do
121
- on('ABC_ASC_Individual_Demog') { put_fields({ 'TotMMS' => 15 }) }
122
- end
123
-
124
- expect(result).to be_a(Hash)
125
- expect(api.imis_id).to be_nil
126
- end
127
-
128
- it 'chains .with().on() to a single block', :aggregate_failures do
129
- result = api.with(31092).on('ABC_ASC_Individual_Demog') do
130
- put_fields({ 'TotMMS' => 15 })
131
- end
132
-
133
- expect(result).to be_a(Hash)
134
- expect(api.imis_id).to eq(31092)
135
- end
136
-
137
- it 'nests on and with', :aggregate_failures do
138
- result = api.on('ABC_ASC_Individual_Demog') do |object|
139
- api.with(31092) { object.put_fields({ 'TotMMS' => 15 }) }
140
- end
141
-
142
- expect(result).to be_a(Hash)
143
- expect(api.imis_id).to be_nil
144
- end
145
- end
146
-
147
- describe '#inspect' do
148
- it 'is configured to exclude the token instance variable' do
149
- expect(api.instance_variables_to_inspect).not_to include(:@token)
150
- end
151
-
152
- it 'does not show the token instance variable' do
153
- pending 'Requires Ruby 3.5' if Gem::Version.new(RUBY_VERSION) < Gem::Version.new(3.5)
154
-
155
- expect(api.inspect).not_to match(/ @token="/)
156
- end
157
- end
158
-
159
- describe '#authorize' do
160
- before { allow(api).to receive(:authenticate) }
161
-
162
- it 'automatically refreshes an expired token' do
163
- api.instance_variable_set(:@token_expiration, Time.now - 60)
164
-
165
- request = Net::HTTP::Put.new('/')
166
- api.send(:authorize, request)
167
-
168
- expect(api).to have_received(:authenticate)
169
- end
170
- end
171
- end
@@ -1,87 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Usps::Imis::BusinessObject do
6
- describe '#put_field' do
7
- let(:business_object) { api.on('ABC_ASC_Individual_Demog') }
8
- let(:api) { Usps::Imis::Api.new.with(31092) }
9
-
10
- before { business_object.put_field('TotMMS', 15) }
11
-
12
- it 'submits the correct update request' do
13
- expect { business_object['TotMMS'] = 16 }.to change { business_object['TotMMS'] }.from(15).to(16)
14
- end
15
- end
16
-
17
- context 'with stubbed data' do
18
- let(:business_object) { described_class.new(api, 'Stub') }
19
- let(:api) { Usps::Imis::Api.new }
20
-
21
- before do
22
- allow(business_object).to receive(:raw_object).and_return(
23
- Usps::Imis::Data[
24
- 'Properties' => {
25
- '$values' => [
26
- { 'Name' => 'ID', 'Value' => { '$value' => '31092' } },
27
- { 'Name' => 'Stub Integer', 'Value' => { '$value' => 42 } },
28
- { 'Name' => 'Stub String', 'Value' => 'something' }
29
- ]
30
- }
31
- ]
32
- )
33
- end
34
-
35
- describe '#get' do
36
- it 'returns multiple values' do
37
- expect(business_object.get('Stub String', 'Stub Integer')).to eq(['something', 42])
38
- end
39
-
40
- describe 'delegation to get_fields' do
41
- before { allow(business_object).to receive(:get_fields).with('Stub String', 'Stub Integer') }
42
-
43
- it 'delegates to get_fields' do
44
- business_object.get('Stub String', 'Stub Integer')
45
-
46
- expect(business_object).to have_received(:get_fields).with('Stub String', 'Stub Integer')
47
- end
48
- end
49
- end
50
-
51
- describe '#get_field' do
52
- it 'returns a string value' do
53
- expect(business_object.get_field('Stub String')).to eq('something')
54
- end
55
-
56
- it 'returns an integer value' do
57
- expect(business_object.get_field('Stub Integer')).to eq(42)
58
- end
59
- end
60
-
61
- describe '#get_fields' do
62
- it 'returns multiple values' do
63
- expect(business_object.get_fields('Stub String', 'Stub Integer')).to eq(['something', 42])
64
- end
65
- end
66
-
67
- describe '#filter_fields' do
68
- let(:expected) do
69
- {
70
- 'Properties' => {
71
- '$values' => [
72
- { 'Name' => 'ID', 'Value' => { '$value' => '31092' } },
73
- { 'Name' => 'Stub Integer', 'Value' => { '$value' => 43 } },
74
- { 'Name' => 'Stub String', 'Value' => 'other' }
75
- ]
76
- }
77
- }
78
- end
79
-
80
- it 'formats fields correctly' do
81
- updated = business_object.send(:filter_fields, 'Stub Integer' => 43, 'Stub String' => 'other')
82
-
83
- expect(updated).to eq(expected)
84
- end
85
- end
86
- end
87
- end
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Usps::Imis::Config do
6
- let(:config) { described_class.new }
7
-
8
- describe 'environment' do
9
- subject { config.environment }
10
-
11
- let(:config) { described_class.new }
12
-
13
- context 'when not specified' do
14
- it { is_expected.to be_development }
15
- end
16
-
17
- context 'when specified' do
18
- before { config.environment = 'something' }
19
-
20
- it { is_expected.to be_something }
21
- end
22
-
23
- context 'when specified on initialize' do
24
- subject { config.environment }
25
-
26
- let(:config) do
27
- described_class.new { it.environment = 'test' }
28
- end
29
-
30
- it { is_expected.to be_test }
31
- end
32
-
33
- context 'when in Rails' do
34
- before { stub_const('Rails', Struct.new(:env).new(ActiveSupport::StringInquirer.new('qa'))) }
35
-
36
- it { is_expected.to be_qa }
37
- end
38
- end
39
-
40
- describe '#hostname' do
41
- context 'with production environment' do
42
- before { config.environment = 'production' }
43
-
44
- it 'returns the production hostname' do
45
- expect(config.hostname).to eq(described_class::IMIS_ROOT_URL_PROD)
46
- end
47
- end
48
-
49
- context 'with unrecognized environment' do
50
- before { config.environment = 'nothing' }
51
-
52
- it 'raises an error' do
53
- expect { config.hostname }.to raise_error(
54
- Usps::Imis::Errors::ConfigError, 'Unexpected API environment: nothing'
55
- )
56
- end
57
- end
58
- end
59
- end
@@ -1,66 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Usps::Imis::Data do
6
- let(:data) do
7
- described_class[
8
- 'EntityTypeName' => 'ABC_ASC_Individual_Demog',
9
- 'Properties' => {
10
- '$values' => [
11
- { 'Name' => 'ID', 'Value' => { '$value' => '31092' } },
12
- { 'Name' => 'Stub Integer', 'Value' => { '$value' => 42 } },
13
- { 'Name' => 'Stub String', 'Value' => 'something' }
14
- ]
15
- }
16
- ]
17
- end
18
-
19
- describe 'missing property' do
20
- it 'returns nil for missing properties' do
21
- expect(data['Not Found']).to be_nil
22
- end
23
- end
24
-
25
- describe '#properties' do
26
- it 'iterates over the properties, excluding IDs' do
27
- expect(data.properties.map { |field, value| "#{field}: #{value}" }).to eq(
28
- ['Stub Integer: 42', 'Stub String: something']
29
- )
30
- end
31
-
32
- it 'iterates over the properties, including IDs' do
33
- expect(data.properties(include_ids: true).map { |field, value| "#{field}: #{value}" }).to eq(
34
- ['ID: 31092', 'Stub Integer: 42', 'Stub String: something']
35
- )
36
- end
37
- end
38
-
39
- describe '#inspect' do
40
- it 'generates the correct inspect string' do
41
- expect(data.inspect).to eq('#<Usps::Imis::Data entity="ABC_ASC_Individual_Demog" imis_id=31092>')
42
- end
43
-
44
- context 'with data from a Panel' do
45
- let(:data) do
46
- described_class[
47
- 'EntityTypeName' => 'ABC_ASC_Individual_Demog',
48
- 'Properties' => {
49
- '$values' => [
50
- { 'Name' => 'ID', 'Value' => { '$value' => '31092' } },
51
- { 'Name' => 'Ordinal', 'Value' => { '$value' => '99' } },
52
- { 'Name' => 'Stub Integer', 'Value' => { '$value' => 42 } },
53
- { 'Name' => 'Stub String', 'Value' => 'something' }
54
- ]
55
- }
56
- ]
57
- end
58
-
59
- it 'generates the correct inspect string with an ordinal' do
60
- expect(data.inspect).to eq(
61
- '#<Usps::Imis::Data entity="ABC_ASC_Individual_Demog" imis_id=31092 ordinal=99>'
62
- )
63
- end
64
- end
65
- end
66
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Usps::Imis::Error do
6
- it 'builds Bugsnag metadata' do
7
- error = described_class.new('Example', something: :else)
8
-
9
- expect(error.bugsnag_meta_data).to eq(api: { something: :else })
10
- end
11
-
12
- it 'ignores Bugsnag metadata with none provided' do
13
- error = described_class.new('Example')
14
-
15
- expect(error.bugsnag_meta_data).to eq({})
16
- end
17
- end
@@ -1,107 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- ApiResponseStub = Struct.new(:code, :body)
6
-
7
- describe Usps::Imis::Errors::ResponseError do
8
- let(:error) { described_class.from(response) }
9
-
10
- describe 'error codes' do
11
- context 'with a 400' do
12
- let(:response) { ApiResponseStub.new('400', 'body') }
13
-
14
- it 'builds the Bugsnag metadata correctly' do
15
- expect(error.bugsnag_meta_data).to eq(api: { status: :bad_request, body: 'body' })
16
- end
17
- end
18
-
19
- context 'with a 401' do
20
- let(:response) { ApiResponseStub.new('401', 'body') }
21
-
22
- it 'builds the Bugsnag metadata correctly' do
23
- expect(error.bugsnag_meta_data).to eq(api: { status: :unauthorized, body: 'body' })
24
- end
25
- end
26
-
27
- context 'with a 404' do
28
- let(:response) { ApiResponseStub.new('404', 'body') }
29
-
30
- it 'builds the Bugsnag metadata correctly' do
31
- expect(error.bugsnag_meta_data).to eq(api: { status: :not_found, body: 'body' })
32
- end
33
- end
34
-
35
- context 'with a 422' do
36
- let(:response) { ApiResponseStub.new('422', 'body') }
37
-
38
- it 'builds the Bugsnag metadata correctly' do
39
- expect(error.bugsnag_meta_data).to eq(api: { status: :unprocessable_entity, body: 'body' })
40
- end
41
- end
42
-
43
- context 'with a 500' do
44
- let(:response) { ApiResponseStub.new('500', 'body') }
45
-
46
- it 'builds the Bugsnag metadata correctly' do
47
- expect(error.bugsnag_meta_data).to eq(api: { status: :internal_server_error, body: 'body' })
48
- end
49
- end
50
-
51
- context 'with a 429' do
52
- let(:response) { ApiResponseStub.new('429', 'body') }
53
-
54
- it 'builds the Bugsnag metadata correctly' do
55
- expect(error.bugsnag_meta_data).to eq(api: { status: '429', body: 'body' })
56
- end
57
- end
58
- end
59
-
60
- context 'with a string response body' do
61
- let(:response) { ApiResponseStub.new('500', 'Body of the API response error') }
62
- let(:warning_text) do
63
- <<~WARNING.chomp
64
- Usps::Imis::Errors::ResponseError: [INTERNAL_SERVER_ERROR] The iMIS API returned an error.
65
- Body of the API response error
66
- WARNING
67
- end
68
-
69
- it 'builds the correct message' do
70
- expect(error.message).to eq(warning_text)
71
- end
72
- end
73
-
74
- context 'with an invalid_grant hash response body' do
75
- let(:response_body) do
76
- { 'error' => 'invalid_grant', 'error_description' => 'description' }
77
- end
78
- let(:response) { ApiResponseStub.new('500', response_body) }
79
- let(:warning_text) do
80
- <<~WARNING.chomp
81
- Usps::Imis::Errors::ResponseError: [INTERNAL_SERVER_ERROR] The iMIS API returned an error.
82
- description
83
- WARNING
84
- end
85
-
86
- it 'builds the correct message' do
87
- expect(error.message).to eq(warning_text)
88
- end
89
- end
90
-
91
- context 'with a generic hash response body' do
92
- let(:response_body) do
93
- { 'error' => 'summary', 'error_description' => 'description' }
94
- end
95
- let(:response) { ApiResponseStub.new('500', response_body) }
96
- let(:warning_text) do
97
- <<~WARNING.chomp
98
- Usps::Imis::Errors::ResponseError: [INTERNAL_SERVER_ERROR] The iMIS API returned an error.
99
- #{response_body}
100
- WARNING
101
- end
102
-
103
- it 'builds the correct message' do
104
- expect(error.message).to eq(warning_text)
105
- end
106
- end
107
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Usps::Imis::Mapper do
6
- let(:api) { described_class.new.api }
7
-
8
- describe 'initialize with imis_id' do
9
- it 'stores the initial imis_id' do
10
- mapper = described_class.new(imis_id: 42)
11
-
12
- expect(mapper.api.imis_id).to eq(42)
13
- end
14
- end
15
-
16
- describe '#fetch' do
17
- before { api.imis_id = 31092 }
18
-
19
- it 'fetches a mapped field' do
20
- expect(api.mapper.fetch(:mm)).to be_a(Integer)
21
- end
22
-
23
- it 'supports Hash access syntax' do
24
- expect(api.mapper[:mm]).to be_a(Integer)
25
- end
26
-
27
- it 'supports Hash access syntax on the Api directly' do
28
- expect(api[:mm]).to be_a(Integer)
29
- end
30
-
31
- it 'raises for unmapped updates' do
32
- expect { api.mapper.fetch(:another) }.to raise_error(
33
- Usps::Imis::Errors::MapperError,
34
- %(Mapper does not recognize field: "another".\n) \
35
- 'Please report what data you are attempting to work with to ITCom leadership.'
36
- )
37
- end
38
- end
39
-
40
- describe '#update' do
41
- before { api.imis_id = 31092 }
42
-
43
- it 'sends a mapped update' do
44
- expect(api.mapper.update(mm: 15).first).to be_a(Hash)
45
- end
46
-
47
- it 'raises for unmapped updates' do
48
- expect { api.mapper.update(something: 'anything') }.to raise_error(
49
- Usps::Imis::Errors::MapperError,
50
- %(Mapper does not recognize field: "something".\n) \
51
- 'Please report what data you are attempting to work with to ITCom leadership.'
52
- )
53
- end
54
- end
55
- end
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Usps::Imis::Mocks::BusinessObject do
6
- let(:mock) { described_class.new(**fields) }
7
- let(:fields) { { TotMMS: 2, Something: 'Another' } }
8
-
9
- let(:data) do
10
- Usps::Imis::Properties.build do |props|
11
- props.add 'TotMMS', 2
12
- props.add 'Something', 'Another'
13
- end
14
- end
15
-
16
- describe 'get' do
17
- it 'returns the correct data' do
18
- expect(mock.get).to eq(data)
19
- end
20
- end
21
-
22
- describe 'get_field' do
23
- it 'returns the correct field value' do
24
- expect(mock.get_field('TotMMS')).to eq(2)
25
- end
26
- end
27
-
28
- describe 'get_fields' do
29
- it 'returns the correct field values' do
30
- expect(mock.get_fields('TotMMS', 'Something')).to eq([2, 'Another'])
31
- end
32
- end
33
-
34
- describe 'put_fields' do
35
- let(:combined_data) do
36
- Usps::Imis::Properties.build do |props|
37
- props.add 'TotMMS', 2
38
- props.add 'Something', 'Another'
39
- props.add 'SomethingElse', 'interesting'
40
- end
41
- end
42
-
43
- it 'returns the correct data' do
44
- expect(mock.put_fields(SomethingElse: 'interesting')).to eq(combined_data)
45
- end
46
- end
47
-
48
- describe 'put' do
49
- it 'returns the correct data' do
50
- expect(mock.put(data)).to eq(data)
51
- end
52
- end
53
-
54
- describe 'post' do
55
- it 'returns the correct data' do
56
- expect(mock.post(data)).to eq(data)
57
- end
58
- end
59
-
60
- describe 'delete' do
61
- it 'returns the correct data' do
62
- expect(mock.delete).to eq('')
63
- end
64
- end
65
- end