frodata 0.9.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.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +2 -0
  3. data/.gitignore +24 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +75 -0
  8. data/CHANGELOG.md +150 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE.txt +23 -0
  11. data/README.md +427 -0
  12. data/Rakefile +7 -0
  13. data/TODO.md +55 -0
  14. data/frodata.gemspec +34 -0
  15. data/lib/frodata.rb +36 -0
  16. data/lib/frodata/entity.rb +332 -0
  17. data/lib/frodata/entity_container.rb +75 -0
  18. data/lib/frodata/entity_set.rb +161 -0
  19. data/lib/frodata/errors.rb +68 -0
  20. data/lib/frodata/navigation_property.rb +29 -0
  21. data/lib/frodata/navigation_property/proxy.rb +80 -0
  22. data/lib/frodata/properties.rb +32 -0
  23. data/lib/frodata/properties/binary.rb +50 -0
  24. data/lib/frodata/properties/boolean.rb +37 -0
  25. data/lib/frodata/properties/collection.rb +50 -0
  26. data/lib/frodata/properties/complex.rb +114 -0
  27. data/lib/frodata/properties/date.rb +27 -0
  28. data/lib/frodata/properties/date_time.rb +83 -0
  29. data/lib/frodata/properties/date_time_offset.rb +17 -0
  30. data/lib/frodata/properties/decimal.rb +50 -0
  31. data/lib/frodata/properties/enum.rb +62 -0
  32. data/lib/frodata/properties/float.rb +67 -0
  33. data/lib/frodata/properties/geography.rb +13 -0
  34. data/lib/frodata/properties/geography/base.rb +162 -0
  35. data/lib/frodata/properties/geography/line_string.rb +33 -0
  36. data/lib/frodata/properties/geography/point.rb +31 -0
  37. data/lib/frodata/properties/geography/polygon.rb +38 -0
  38. data/lib/frodata/properties/guid.rb +17 -0
  39. data/lib/frodata/properties/integer.rb +107 -0
  40. data/lib/frodata/properties/number.rb +14 -0
  41. data/lib/frodata/properties/string.rb +72 -0
  42. data/lib/frodata/properties/time.rb +40 -0
  43. data/lib/frodata/properties/time_of_day.rb +27 -0
  44. data/lib/frodata/property.rb +139 -0
  45. data/lib/frodata/property_registry.rb +41 -0
  46. data/lib/frodata/query.rb +233 -0
  47. data/lib/frodata/query/criteria.rb +92 -0
  48. data/lib/frodata/query/criteria/comparison_operators.rb +49 -0
  49. data/lib/frodata/query/criteria/date_functions.rb +61 -0
  50. data/lib/frodata/query/criteria/geography_functions.rb +21 -0
  51. data/lib/frodata/query/criteria/lambda_operators.rb +27 -0
  52. data/lib/frodata/query/criteria/string_functions.rb +40 -0
  53. data/lib/frodata/query/in_batches.rb +58 -0
  54. data/lib/frodata/railtie.rb +19 -0
  55. data/lib/frodata/schema.rb +155 -0
  56. data/lib/frodata/schema/complex_type.rb +79 -0
  57. data/lib/frodata/schema/enum_type.rb +95 -0
  58. data/lib/frodata/service.rb +254 -0
  59. data/lib/frodata/service/request.rb +85 -0
  60. data/lib/frodata/service/response.rb +162 -0
  61. data/lib/frodata/service/response/atom.rb +40 -0
  62. data/lib/frodata/service/response/json.rb +41 -0
  63. data/lib/frodata/service/response/plain.rb +36 -0
  64. data/lib/frodata/service/response/xml.rb +40 -0
  65. data/lib/frodata/service_registry.rb +52 -0
  66. data/lib/frodata/version.rb +3 -0
  67. data/spec/fixtures/files/entity_to_xml.xml +17 -0
  68. data/spec/fixtures/files/error.xml +5 -0
  69. data/spec/fixtures/files/metadata.xml +150 -0
  70. data/spec/fixtures/files/product_0.json +10 -0
  71. data/spec/fixtures/files/product_0.xml +28 -0
  72. data/spec/fixtures/files/products.json +106 -0
  73. data/spec/fixtures/files/products.xml +308 -0
  74. data/spec/fixtures/files/supplier_0.json +26 -0
  75. data/spec/fixtures/files/supplier_0.xml +32 -0
  76. data/spec/fixtures/vcr_cassettes/entity_set_specs.yml +1635 -0
  77. data/spec/fixtures/vcr_cassettes/entity_set_specs/bad_entry.yml +183 -0
  78. data/spec/fixtures/vcr_cassettes/entity_set_specs/existing_entry.yml +256 -0
  79. data/spec/fixtures/vcr_cassettes/entity_set_specs/new_entry.yml +185 -0
  80. data/spec/fixtures/vcr_cassettes/entity_specs.yml +285 -0
  81. data/spec/fixtures/vcr_cassettes/navigation_property_proxy_specs.yml +346 -0
  82. data/spec/fixtures/vcr_cassettes/query/result_specs.yml +189 -0
  83. data/spec/fixtures/vcr_cassettes/query_specs.yml +1060 -0
  84. data/spec/fixtures/vcr_cassettes/schema/complex_type_specs.yml +127 -0
  85. data/spec/fixtures/vcr_cassettes/service/request_specs.yml +193 -0
  86. data/spec/fixtures/vcr_cassettes/service_registry_specs.yml +129 -0
  87. data/spec/fixtures/vcr_cassettes/service_specs.yml +127 -0
  88. data/spec/fixtures/vcr_cassettes/usage_example_specs.yml +1330 -0
  89. data/spec/frodata/entity/shared_examples.rb +82 -0
  90. data/spec/frodata/entity_container_spec.rb +38 -0
  91. data/spec/frodata/entity_set_spec.rb +168 -0
  92. data/spec/frodata/entity_spec.rb +151 -0
  93. data/spec/frodata/errors_spec.rb +48 -0
  94. data/spec/frodata/navigation_property/proxy_spec.rb +44 -0
  95. data/spec/frodata/navigation_property_spec.rb +55 -0
  96. data/spec/frodata/properties/binary_spec.rb +50 -0
  97. data/spec/frodata/properties/boolean_spec.rb +72 -0
  98. data/spec/frodata/properties/collection_spec.rb +44 -0
  99. data/spec/frodata/properties/date_spec.rb +23 -0
  100. data/spec/frodata/properties/date_time_offset_spec.rb +30 -0
  101. data/spec/frodata/properties/date_time_spec.rb +23 -0
  102. data/spec/frodata/properties/decimal_spec.rb +51 -0
  103. data/spec/frodata/properties/float_spec.rb +45 -0
  104. data/spec/frodata/properties/geography/line_string_spec.rb +33 -0
  105. data/spec/frodata/properties/geography/point_spec.rb +29 -0
  106. data/spec/frodata/properties/geography/polygon_spec.rb +55 -0
  107. data/spec/frodata/properties/geography/shared_examples.rb +72 -0
  108. data/spec/frodata/properties/guid_spec.rb +17 -0
  109. data/spec/frodata/properties/integer_spec.rb +58 -0
  110. data/spec/frodata/properties/string_spec.rb +46 -0
  111. data/spec/frodata/properties/time_of_day_spec.rb +23 -0
  112. data/spec/frodata/properties/time_spec.rb +15 -0
  113. data/spec/frodata/property_registry_spec.rb +16 -0
  114. data/spec/frodata/property_spec.rb +71 -0
  115. data/spec/frodata/query/criteria_spec.rb +229 -0
  116. data/spec/frodata/query_spec.rb +199 -0
  117. data/spec/frodata/schema/complex_type_spec.rb +96 -0
  118. data/spec/frodata/schema/enum_type_spec.rb +112 -0
  119. data/spec/frodata/schema_spec.rb +97 -0
  120. data/spec/frodata/service/request_spec.rb +49 -0
  121. data/spec/frodata/service/response_spec.rb +85 -0
  122. data/spec/frodata/service_registry_spec.rb +18 -0
  123. data/spec/frodata/service_spec.rb +191 -0
  124. data/spec/frodata/usage_example_spec.rb +188 -0
  125. data/spec/spec_helper.rb +32 -0
  126. data/spec/support/coverage.rb +2 -0
  127. data/spec/support/vcr.rb +9 -0
  128. metadata +401 -0
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ describe FrOData::Schema::EnumType, vcr: {cassette_name: 'schema/enum_type_specs'} do
4
+ before(:example) do
5
+ FrOData::Service.new('http://services.odata.org/V4/OData/OData.svc', name: 'ODataDemo', metadata_file: metadata_file)
6
+ end
7
+
8
+ let(:metadata_file) { 'spec/fixtures/files/metadata.xml' }
9
+ let(:service) { FrOData::ServiceRegistry['ODataDemo'] }
10
+
11
+ let(:enum_type) { service.enum_types['ODataDemo.ProductStatus'] }
12
+ let(:subject) { enum_type.property_class.new('ProductStatus', nil) }
13
+
14
+ describe 'is properly parsed from service metadata' do
15
+ it { expect(enum_type.name).to eq('ProductStatus') }
16
+ it { expect(enum_type.namespace).to eq('ODataDemo') }
17
+ it { expect(enum_type.type).to eq('ODataDemo.ProductStatus') }
18
+ it { expect(enum_type.is_flags?).to eq(false) }
19
+ it { expect(enum_type.underlying_type).to eq('Edm.Byte') }
20
+ it { expect(enum_type.members.values).to eq(%w{Available LowStock Backordered Discontinued}) }
21
+ end
22
+
23
+ # Check property instance inheritance hierarchy
24
+ it { expect(subject).to be_a(FrOData::Property) }
25
+ it { expect(subject).to be_a(FrOData::Properties::Enum) }
26
+
27
+ it { expect(subject).to respond_to(:name) }
28
+ it { expect(subject).to respond_to(:type) }
29
+ it { expect(subject).to respond_to(:members) }
30
+
31
+ describe '#value=' do
32
+ it 'allows setting a valid value' do
33
+ subject.value = 'Available'
34
+ expect(subject.value).to eq('Available')
35
+ end
36
+
37
+ it 'does not allow setting an invalid value' do
38
+ expect {
39
+ subject.value = 'Invalid'
40
+ }.to raise_error(ArgumentError)
41
+ end
42
+
43
+ it 'allows setting by numeric value' do
44
+ expect {
45
+ subject.value = 1
46
+ }.not_to raise_error
47
+ expect(subject.value).to eq('LowStock')
48
+ end
49
+
50
+ context 'when `IsFlags` is false' do
51
+ it 'does not allow setting multiple values' do
52
+ expect {
53
+ subject.value = 'Available, Backordered'
54
+ }.to raise_error(ArgumentError)
55
+ end
56
+ end
57
+
58
+ context 'when `IsFlags` is true' do
59
+ before do
60
+ subject.define_singleton_method(:is_flags?) { true }
61
+ end
62
+
63
+ it 'allows setting multiple values' do
64
+ subject.value = 'Available, Backordered'
65
+ expect(subject.value).to eq(%w[Available Backordered])
66
+ end
67
+
68
+ it 'does not allow setting invalid values' do
69
+ expect {
70
+ subject.value = 'Available, Invalid'
71
+ }.to raise_error(ArgumentError)
72
+ end
73
+
74
+ it 'allows setting by numeric value' do
75
+ expect {
76
+ subject.value = '0, 1'
77
+ }.not_to raise_error
78
+ expect(subject.value).to eq(%w[Available LowStock])
79
+ end
80
+ end
81
+ end
82
+
83
+ describe 'lenient validation' do
84
+ let(:subject) do
85
+ enum_type.property_class.new('ProductStatus', nil, strict: false)
86
+ end
87
+
88
+ describe '#value=' do
89
+ it 'ignores invalid values' do
90
+ expect {
91
+ subject.value = 'Invalid'
92
+ }.not_to raise_error
93
+
94
+ expect(subject.value).to be_nil
95
+ end
96
+
97
+ context 'when `IsFlags` is true' do
98
+ before do
99
+ subject.define_singleton_method(:is_flags?) { true }
100
+ end
101
+
102
+ it 'ignores invalid values' do
103
+ expect {
104
+ subject.value = 'Available, Invalid'
105
+ }.not_to raise_error
106
+
107
+ expect(subject.value).to eq(['Available'])
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ describe FrOData::Schema do
4
+ let(:subject) { FrOData::Schema.new(schema_xml, service) }
5
+ let(:service) do
6
+ FrOData::Service.new('http://services.odata.org/V4/OData/OData.svc', metadata_file: metadata_file)
7
+ end
8
+ let(:metadata_file) { 'spec/fixtures/files/metadata.xml' }
9
+ let(:schema_xml) { service.metadata.xpath('//Schema').first }
10
+
11
+ let(:entity_types) { %w{Product FeaturedProduct ProductDetail Category Supplier Person Customer Employee PersonDetail Advertisement} }
12
+ let(:complex_types) { %w{Address} }
13
+ let(:enum_types) { %w{ProductStatus} }
14
+
15
+ describe '#namespace' do
16
+ it { expect(subject).to respond_to(:namespace) }
17
+ it "returns the schema's namespace attribute" do
18
+ expect(subject.namespace).to eq('ODataDemo')
19
+ end
20
+ end
21
+
22
+ describe '#actions' do
23
+ # TODO add a action definition to metadata
24
+ it { expect(subject).to respond_to(:actions) }
25
+ it { expect(subject.actions.size).to eq(0) }
26
+ end
27
+
28
+ describe '#annotations' do
29
+ # TBD
30
+ end
31
+
32
+ describe '#complex_types' do
33
+ it { expect(subject).to respond_to(:complex_types) }
34
+ it { expect(subject.complex_types.size).to eq(1) }
35
+ it { expect(subject.complex_types.keys).to eq(complex_types) }
36
+ end
37
+
38
+ describe '#entity_types' do
39
+ it { expect(subject).to respond_to(:entity_types) }
40
+ it { expect(subject.entity_types.size).to eq(10) }
41
+ it { expect(subject.entity_types).to eq(entity_types) }
42
+ end
43
+
44
+ describe '#enum_types' do
45
+ it { expect(subject).to respond_to(:enum_types) }
46
+ it { expect(subject.enum_types.size).to eq(1) }
47
+ it { expect(subject.enum_types.keys).to eq(enum_types)}
48
+ end
49
+
50
+ describe '#functions' do
51
+ # TODO add a function definition to metadata
52
+ it { expect(subject).to respond_to(:functions) }
53
+ it { expect(subject.functions.size).to eq(0) }
54
+ end
55
+
56
+ describe '#terms' do
57
+ # TBD
58
+ end
59
+
60
+ describe '#type_definitions' do
61
+ # TODO add a type definition to metadata
62
+ it { expect(subject).to respond_to(:type_definitions) }
63
+ it { expect(subject.type_definitions.size).to eq(0) }
64
+ end
65
+
66
+ describe '#navigation_properties' do
67
+ it { expect(subject).to respond_to(:navigation_properties) }
68
+ it { expect(subject.navigation_properties['Product'].size).to eq(3) }
69
+ it { expect(subject.navigation_properties['Product'].values).to all(be_a(FrOData::NavigationProperty)) }
70
+ end
71
+
72
+ describe '#get_property_type' do
73
+ it { expect(subject).to respond_to(:get_property_type) }
74
+ it { expect(subject.get_property_type('Product', 'ID')).to eq('Edm.Int32') }
75
+ it { expect(subject.get_property_type('Product', 'ProductStatus')).to eq('ODataDemo.ProductStatus') }
76
+ end
77
+
78
+ describe '#primary_key_for' do
79
+ it { expect(subject).to respond_to(:primary_key_for) }
80
+ it { expect(subject.primary_key_for('Product')).to eq('ID') }
81
+ end
82
+
83
+ describe '#properties_for_entity' do
84
+ it { expect(subject).to respond_to(:properties_for_entity) }
85
+ it { expect(subject.properties_for_entity('Product').keys).to eq(%w[
86
+ ID
87
+ Name
88
+ Description
89
+ ReleaseDate
90
+ DiscontinuedDate
91
+ Rating
92
+ Price
93
+ ProductStatus
94
+ ]) }
95
+ it { expect(subject.properties_for_entity('Product').values).to all(be_a(FrOData::Property)) }
96
+ end
97
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe FrOData::Service::Request, vcr: {cassette_name: 'service/request_specs'} do
4
+ let(:subject) { FrOData::Service::Request.new(service, 'Products') }
5
+ let(:service) { FrOData::Service.new(service_url, name: 'ODataDemo', metadata_file: metadata_file) }
6
+ let(:service_url) { 'http://services.odata.org/V4/OData/OData.svc' }
7
+ let(:metadata_file) { 'spec/fixtures/files/metadata.xml' }
8
+
9
+ describe '#url' do
10
+ it 'returns the full request URL' do
11
+ expect(subject.url).to eq('http://services.odata.org/V4/OData/OData.svc/Products')
12
+ end
13
+ end
14
+
15
+ describe '#method' do
16
+ it 'defaults to GET' do
17
+ expect(subject.method).to eq(:get)
18
+ end
19
+ end
20
+
21
+ describe '#format' do
22
+ it 'defaults to :auto' do
23
+ expect(subject.format).to eq(:auto)
24
+ end
25
+ end
26
+
27
+ describe '#content_type' do
28
+ it 'return all acceptable types when format = :auto' do
29
+ expect(subject.content_type).to eq(FrOData::Service::MIME_TYPES.values.join(','))
30
+ end
31
+
32
+ it 'returns the correct MIME type when format = :atom' do
33
+ subject.format = :atom
34
+ expect(subject.content_type).to eq('application/atom+xml')
35
+ end
36
+
37
+ it 'returns the correct MIME type when format = :json' do
38
+ subject.format = :json
39
+ expect(subject.content_type).to eq('application/json')
40
+ end
41
+ end
42
+
43
+ describe '#execute' do
44
+ it 'returns a response object' do
45
+ expect(subject.execute).to be_a(FrOData::Service::Response)
46
+ end
47
+ it 'retries on wrong content type'
48
+ end
49
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples 'a valid response' do
4
+ it { expect(subject).to be_success }
5
+ it { expect(subject.count).to eq(11) }
6
+
7
+ describe '#empty?' do
8
+ it { expect(subject).to respond_to(:empty?) }
9
+ it { expect(subject.empty?).to eq(false) }
10
+ end
11
+
12
+ describe '#each' do
13
+ it { expect(subject).to respond_to(:each) }
14
+ it 'returns just FrOData::Entities of the right type' do
15
+ subject.each do |entity|
16
+ expect(entity).to be_a(FrOData::Entity)
17
+ expect(entity.type).to eq('ODataDemo.Product')
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ describe FrOData::Service::Response, vcr: {cassette_name: 'service/response_specs'} do
24
+ let(:subject) { FrOData::Service::Response.new(service, entity_set.query) { response } }
25
+ let(:service) { FrOData::Service.new(service_url, name: 'ODataDemo', metadata_file: metadata_file) }
26
+ let(:service_url) { 'http://services.odata.org/V4/OData/OData.svc' }
27
+ let(:metadata_file) { 'spec/fixtures/files/metadata.xml' }
28
+ let(:entity_set) { service['Products'] }
29
+ let(:response) do
30
+ response = double('response')
31
+ allow(response).to receive_messages(
32
+ headers: { 'Content-Type' => content_type },
33
+ status: response_status,
34
+ body: response_body
35
+ )
36
+ response
37
+ end
38
+
39
+ context 'with Atom result' do
40
+ let(:content_type) { 'application/atom+xml' }
41
+ let(:response_status) { 200 }
42
+ let(:response_body) { File.read('spec/fixtures/files/products.xml') }
43
+
44
+ it_behaves_like 'a valid response'
45
+ end
46
+
47
+ context 'with JSON result' do
48
+ let(:content_type) { 'application/json' }
49
+ let(:response_status) { 200 }
50
+ let(:response_body) { File.read('spec/fixtures/files/products.json') }
51
+
52
+ it_behaves_like 'a valid response'
53
+ end
54
+
55
+ context 'with XML result' do
56
+ let(:content_type) { 'application/xml' }
57
+ let(:response_status) { 200 }
58
+ let(:response_body) { File.read('spec/fixtures/files/error.xml') }
59
+
60
+ it 'contains no entities' do
61
+ expect(subject.empty?).to eq(true)
62
+ end
63
+
64
+ it 'contains error message' do
65
+ expect(subject.error_message).to match(/Resource not found/)
66
+ end
67
+ end
68
+
69
+ context 'with plain text result' do
70
+ let(:content_type) { 'text/plain' }
71
+ let(:response_status) { 200 }
72
+ let(:response_body) { '123' }
73
+
74
+ it { expect(subject).to be_success }
75
+ it { expect(subject.body).to match(/123/) }
76
+ end
77
+
78
+ context 'with unregistered content type' do
79
+ let(:content_type) { 'text/unknown' }
80
+ let(:response_status) { 200 }
81
+ let(:response_body) { '123' }
82
+
83
+ it { expect { subject }.to raise_error(FrOData::RequestError) }
84
+ end
85
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe FrOData::ServiceRegistry, vcr: {cassette_name: 'service_registry_specs'} do
4
+ let(:subject) { FrOData::ServiceRegistry }
5
+ let(:sample_service) { FrOData::Service.new('http://services.odata.org/V4/OData/OData.svc', name: 'demoService') }
6
+
7
+ it { expect(subject).to respond_to(:add) }
8
+ it { expect(subject).to respond_to(:[]) }
9
+
10
+ describe '#add' do
11
+ before(:example) do
12
+ subject.add(sample_service)
13
+ end
14
+
15
+ it { expect(subject['demoService']).to eq(sample_service) }
16
+ it { expect(subject['http://services.odata.org/V4/OData/OData.svc']).to eq(sample_service) }
17
+ end
18
+ end
@@ -0,0 +1,191 @@
1
+ require 'spec_helper'
2
+
3
+ describe FrOData::Service, vcr: {cassette_name: 'service_specs'} do
4
+ let(:service_url) { 'http://services.odata.org/V4/OData/OData.svc' }
5
+ let(:metadata_file) { 'spec/fixtures/files/metadata.xml' }
6
+ let(:subject) { FrOData::Service.new(service_url, name: 'ODataDemo', metadata_file: metadata_file) }
7
+
8
+ describe '.new' do
9
+ it 'adds itself to FrOData::ServiceRegistry on creation' do
10
+ expect(FrOData::ServiceRegistry['ODataDemo']).to be_nil
11
+ expect(FrOData::ServiceRegistry[service_url]).to be_nil
12
+
13
+ service = FrOData::Service.new(service_url, name: 'ODataDemo')
14
+
15
+ expect(FrOData::ServiceRegistry['ODataDemo']).to eq(service)
16
+ expect(FrOData::ServiceRegistry[service_url]).to eq(service)
17
+ end
18
+
19
+ it 'registers custom types on creation' do
20
+ service = FrOData::Service.new(service_url, name: 'ODataDemo')
21
+
22
+ expect(FrOData::PropertyRegistry['ODataDemo.Address']).to be_a(Class)
23
+ expect(FrOData::PropertyRegistry['ODataDemo.ProductStatus']).to be_a(Class)
24
+ end
25
+
26
+ it 'allows connection to be set by passing it instead of service_url' do
27
+ connection = Faraday.new(service_url)
28
+ service = FrOData::Service.new(connection)
29
+ expect(service.connection).to eq(connection)
30
+ end
31
+
32
+ it 'allows connection to be customized via options hash' do
33
+ service = FrOData::Service.new(service_url, connection: {
34
+ headers: { 'X-Custom-Header' => 'foo' }
35
+ })
36
+ expect(service.connection.headers).to include('X-Custom-Header' => 'foo')
37
+ end
38
+
39
+ it 'allows connection to be customized via block argument' do
40
+ service = FrOData::Service.new(service_url) do |conn|
41
+ conn.headers['X-Custom-Header'] = 'foo'
42
+ conn.adapter Faraday.default_adapter
43
+ end
44
+ expect(service.connection.headers).to include('X-Custom-Header' => 'foo')
45
+ end
46
+
47
+ it 'allows logger to be set via option' do
48
+ logger = Logger.new(STDERR).tap { |l| l.level = Logger::ERROR }
49
+ service = FrOData::Service.new(service_url, logger: logger)
50
+ expect(service.logger).to eq(logger)
51
+ end
52
+ end
53
+
54
+ describe '#connection' do
55
+ it 'returns the connection object used by the service' do
56
+ expect(subject.connection).to be_a(Faraday::Connection)
57
+ end
58
+
59
+ it 'uses the service URL as URL prefix' do
60
+ expect(subject.connection.url_prefix.to_s).to eq(subject.service_url)
61
+ end
62
+ end
63
+
64
+ describe '#logger' do
65
+ let(:logger) { Logger.new(STDERR).tap { |l| l.level = Logger::ERROR } }
66
+
67
+ it 'returns the logger used by the service' do
68
+ expect(subject.logger).to be_a(Logger)
69
+ end
70
+
71
+ it 'returns the default logger if none was set' do
72
+ expect(subject.logger.level).to eq(Logger::WARN)
73
+ end
74
+
75
+ it 'uses Rails logger if available' do
76
+ stub_const 'Rails', Class.new { def self.logger; end }
77
+ allow(Rails).to receive(:logger).and_return(logger)
78
+ expect(subject.logger).to eq(logger)
79
+ end
80
+
81
+ it 'allows logger to be set via attribute writer' do
82
+ expect(subject.logger).not_to eq(logger)
83
+ subject.logger = logger
84
+ expect(subject.logger).to eq(logger)
85
+ end
86
+ end
87
+
88
+ describe '#service_url' do
89
+ it { expect(subject).to respond_to(:service_url) }
90
+ it { expect(subject.service_url).to eq(service_url) }
91
+ end
92
+
93
+ describe '#schemas' do
94
+ it { expect(subject).to respond_to(:schemas) }
95
+ it { expect(subject.schemas.keys).to eq(['ODataDemo']) }
96
+ it { expect(subject.schemas.values).to all(be_a(FrOData::Schema)) }
97
+ it {
98
+ subject.schemas.each do |namespace, schema|
99
+ expect(schema.namespace).to eq(namespace)
100
+ end
101
+ }
102
+ end
103
+
104
+ describe '#entity_types' do
105
+ it { expect(subject).to respond_to(:entity_types) }
106
+ it { expect(subject.entity_types.size).to eq(10) }
107
+ it { expect(subject.entity_types).to eq(%w[
108
+ ODataDemo.Product
109
+ ODataDemo.FeaturedProduct
110
+ ODataDemo.ProductDetail
111
+ ODataDemo.Category
112
+ ODataDemo.Supplier
113
+ ODataDemo.Person
114
+ ODataDemo.Customer
115
+ ODataDemo.Employee
116
+ ODataDemo.PersonDetail
117
+ ODataDemo.Advertisement
118
+ ]) }
119
+ end
120
+
121
+ describe '#entity_sets' do
122
+ it { expect(subject).to respond_to(:entity_sets) }
123
+ it { expect(subject.entity_sets.size).to eq(7) }
124
+ it { expect(subject.entity_sets.keys).to eq(%w[
125
+ Products
126
+ ProductDetails
127
+ Categories
128
+ Suppliers
129
+ Persons
130
+ PersonDetails
131
+ Advertisements
132
+ ]) }
133
+ it { expect(subject.entity_sets.values).to eq(%w[
134
+ ODataDemo.Product
135
+ ODataDemo.ProductDetail
136
+ ODataDemo.Category
137
+ ODataDemo.Supplier
138
+ ODataDemo.Person
139
+ ODataDemo.PersonDetail
140
+ ODataDemo.Advertisement
141
+ ]) }
142
+ end
143
+
144
+ describe '#complex_types' do
145
+ it { expect(subject).to respond_to(:complex_types) }
146
+ it { expect(subject.complex_types.size).to eq(1) }
147
+ it { expect(subject.complex_types.keys).to eq(['ODataDemo.Address']) }
148
+ end
149
+
150
+ describe '#enum_types' do
151
+ it { expect(subject).to respond_to(:enum_types) }
152
+ it { expect(subject.enum_types.size).to eq(1) }
153
+ it { expect(subject.enum_types.keys).to eq(['ODataDemo.ProductStatus'])}
154
+ end
155
+
156
+ describe '#namespace' do
157
+ it { expect(subject.namespace).to eq('ODataDemo') }
158
+ end
159
+
160
+ describe '#[]' do
161
+ let(:entity_sets) { subject.entity_sets.keys.map { |name| subject[name] } }
162
+ it { expect(entity_sets).to all(be_a(FrOData::EntitySet)) }
163
+ it { expect {subject['Nonexistant']}.to raise_error(ArgumentError) }
164
+ end
165
+
166
+ describe '#get_property_type' do
167
+ it { expect(subject).to respond_to(:get_property_type) }
168
+ it { expect(subject.get_property_type('ODataDemo.Product', 'ID')).to eq('Edm.Int32') }
169
+ it { expect(subject.get_property_type('ODataDemo.Product', 'ProductStatus')).to eq('ODataDemo.ProductStatus') }
170
+ end
171
+
172
+ describe '#primary_key_for' do
173
+ it { expect(subject).to respond_to(:primary_key_for) }
174
+ it { expect(subject.primary_key_for('ODataDemo.Product')).to eq('ID') }
175
+ end
176
+
177
+ describe '#properties_for_entity' do
178
+ it { expect(subject).to respond_to(:properties_for_entity) }
179
+ it { expect(subject.properties_for_entity('ODataDemo.Product').keys).to eq(%w[
180
+ ID
181
+ Name
182
+ Description
183
+ ReleaseDate
184
+ DiscontinuedDate
185
+ Rating
186
+ Price
187
+ ProductStatus
188
+ ]) }
189
+ it { expect(subject.properties_for_entity('ODataDemo.Product').values).to all(be_a(FrOData::Property)) }
190
+ end
191
+ end