dbee 2.0.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,6 +10,31 @@
10
10
  require 'spec_helper'
11
11
 
12
12
  describe Dbee::Query::Field do
13
+ let(:config) do
14
+ {
15
+ display: 'd',
16
+ key_path: 'a.b.c'
17
+ }
18
+ end
19
+
20
+ let(:config_with_aggregation_and_filters) do
21
+ config.merge(
22
+ aggregator: :ave,
23
+ filters: [
24
+ {
25
+ key_path: 'a.b',
26
+ value: 'something'
27
+ }
28
+ ]
29
+ )
30
+ end
31
+
32
+ subject { described_class.new(config_with_aggregation_and_filters) }
33
+
34
+ let(:subject_without_aggregation_and_filters) do
35
+ described_class.new(config)
36
+ end
37
+
13
38
  it 'should act as hashable' do
14
39
  expect(described_class).to respond_to(:make)
15
40
  expect(described_class).to respond_to(:array)
@@ -24,16 +49,19 @@ describe Dbee::Query::Field do
24
49
  end
25
50
 
26
51
  context 'equality' do
27
- let(:config) { { key_path: 'a.b.c', display: 'd' } }
52
+ specify '#hash produces same output as [aggregator, key_path, and display]' do
53
+ expected = [
54
+ subject.aggregator,
55
+ subject.display,
56
+ subject.filters,
57
+ subject.key_path
58
+ ].hash
28
59
 
29
- subject { described_class.new(config) }
30
-
31
- specify '#hash produces same output as concatenated string hash of key_path and display' do
32
- expect(subject.hash).to eq("#{config[:key_path]}#{config[:display]}".hash)
60
+ expect(subject.hash).to eq(expected)
33
61
  end
34
62
 
35
63
  specify '#== and #eql? compare attributes' do
36
- object2 = described_class.new(config)
64
+ object2 = described_class.new(config_with_aggregation_and_filters)
37
65
 
38
66
  expect(subject).to eq(object2)
39
67
  expect(subject).to eql(object2)
@@ -44,4 +72,24 @@ describe Dbee::Query::Field do
44
72
  expect(subject).not_to eq(nil)
45
73
  end
46
74
  end
75
+
76
+ describe '#aggregator?' do
77
+ it 'returns true if not nil' do
78
+ expect(subject.aggregator?).to be true
79
+ end
80
+
81
+ it 'returns false if nil' do
82
+ expect(subject_without_aggregation_and_filters.aggregator?).to be false
83
+ end
84
+ end
85
+
86
+ describe '#filters?' do
87
+ it 'returns true if at least one filter' do
88
+ expect(subject.filters?).to be true
89
+ end
90
+
91
+ it 'returns false if nil' do
92
+ expect(subject_without_aggregation_and_filters.filters?).to be false
93
+ end
94
+ end
47
95
  end
@@ -9,24 +9,23 @@
9
9
 
10
10
  require 'spec_helper'
11
11
 
12
- describe Dbee::Query::Filters do
13
- FILTERS_CONFIG = { key_path: 'a.b.c', value: :d }.freeze
14
-
15
- FILTER_FACTORIES = {
16
- Dbee::Query::Filters::Contains => FILTERS_CONFIG.merge(type: :contains),
17
- Dbee::Query::Filters::Equals => FILTERS_CONFIG.merge(type: :equals),
18
- Dbee::Query::Filters::GreaterThanOrEqualTo => FILTERS_CONFIG.merge(
19
- type: :greater_than_or_equal_to
20
- ),
21
- Dbee::Query::Filters::GreaterThan => FILTERS_CONFIG.merge(type: :greater_than),
22
- Dbee::Query::Filters::LessThanOrEqualTo => FILTERS_CONFIG.merge(type: :less_than_or_equal_to),
23
- Dbee::Query::Filters::LessThan => FILTERS_CONFIG.merge(type: :less_than),
24
- Dbee::Query::Filters::NotContain => FILTERS_CONFIG.merge(type: :not_contain),
25
- Dbee::Query::Filters::NotEquals => FILTERS_CONFIG.merge(type: :not_equals),
26
- Dbee::Query::Filters::NotStartWith => FILTERS_CONFIG.merge(type: :not_start_with),
27
- Dbee::Query::Filters::StartsWith => FILTERS_CONFIG.merge(type: :starts_with)
28
- }.freeze
12
+ FILTERS_CONFIG = { key_path: 'a.b.c', value: :d }.freeze
13
+ FILTER_FACTORIES = {
14
+ Dbee::Query::Filters::Contains => FILTERS_CONFIG.merge(type: :contains),
15
+ Dbee::Query::Filters::Equals => FILTERS_CONFIG.merge(type: :equals),
16
+ Dbee::Query::Filters::GreaterThanOrEqualTo => FILTERS_CONFIG.merge(
17
+ type: :greater_than_or_equal_to
18
+ ),
19
+ Dbee::Query::Filters::GreaterThan => FILTERS_CONFIG.merge(type: :greater_than),
20
+ Dbee::Query::Filters::LessThanOrEqualTo => FILTERS_CONFIG.merge(type: :less_than_or_equal_to),
21
+ Dbee::Query::Filters::LessThan => FILTERS_CONFIG.merge(type: :less_than),
22
+ Dbee::Query::Filters::NotContain => FILTERS_CONFIG.merge(type: :not_contain),
23
+ Dbee::Query::Filters::NotEquals => FILTERS_CONFIG.merge(type: :not_equals),
24
+ Dbee::Query::Filters::NotStartWith => FILTERS_CONFIG.merge(type: :not_start_with),
25
+ Dbee::Query::Filters::StartsWith => FILTERS_CONFIG.merge(type: :starts_with)
26
+ }.freeze
29
27
 
28
+ describe Dbee::Query::Filters do
30
29
  FILTER_FACTORIES.each_pair do |constant, config|
31
30
  it "should instantiate #{constant} objects" do
32
31
  expect(described_class.make(config)).to be_a(constant)
@@ -9,9 +9,63 @@
9
9
 
10
10
  require 'spec_helper'
11
11
 
12
+ README_EXAMPLES = {
13
+ 'Get all practices' => {
14
+ fields: [
15
+ { key_path: 'id' },
16
+ { key_path: 'active' },
17
+ { key_path: 'name' }
18
+ ]
19
+ },
20
+ 'Get all practices, limit to 10, and sort by name (descending) then id (ascending)' => {
21
+ fields: [
22
+ { key_path: 'id' },
23
+ { key_path: 'active' },
24
+ { key_path: 'name' }
25
+ ],
26
+ sorters: [
27
+ { key_path: 'name', direction: :descending },
28
+ { key_path: 'id' }
29
+ ],
30
+ limit: 10
31
+ },
32
+ "Get top 5 active practices and patient whose name start with 'Sm':" => {
33
+ fields: [
34
+ { key_path: 'name', display: 'Practice Name' },
35
+ { key_path: 'patients.first', display: 'Patient First Name' },
36
+ { key_path: 'patients.middle', display: 'Patient Middle Name' },
37
+ { key_path: 'patients.last', display: 'Patient Last Name' }
38
+ ],
39
+ filters: [
40
+ { type: :equals, key_path: 'active', value: true },
41
+ { type: :starts_with, key_path: 'patients.last', value: 'Sm' }
42
+ ],
43
+ limit: 5
44
+ },
45
+ 'Get practice IDs, patient IDs, names, and cell phone numbers that starts with 555' => {
46
+ fields: [
47
+ { key_path: 'id', display: 'Practice ID #' },
48
+ { key_path: 'patients.id', display: 'Patient ID #' },
49
+ { key_path: 'patients.first', display: 'Patient First Name' },
50
+ { key_path: 'patients.middle', display: 'Patient Middle Name' },
51
+ { key_path: 'patients.last', display: 'Patient Last Name' },
52
+ { key_path: 'patients.cell_phone_numbers.phone_number', display: 'Patient Cell #' }
53
+ ],
54
+ filters: [
55
+ { type: :equals, key_path: 'active', value: true },
56
+ {
57
+ type: :starts_with,
58
+ key_path: 'patients.cell_phone_numbers.phone_number',
59
+ value: '555'
60
+ }
61
+ ]
62
+ }
63
+ }.freeze
64
+
12
65
  describe Dbee::Query do
13
66
  let(:config) do
14
67
  {
68
+ from: 'my_model',
15
69
  fields: [
16
70
  { key_path: 'matt.nick.sam', display: 'some display' },
17
71
  { key_path: 'katie' },
@@ -36,14 +90,6 @@ describe Dbee::Query do
36
90
  end
37
91
 
38
92
  describe '#initialize' do
39
- it 'should raise an ArgumentError if fields keyword is missing' do
40
- expect { described_class.new }.to raise_error(ArgumentError)
41
- end
42
-
43
- it 'should raise a NoFieldsError if no fields were passed in' do
44
- expect { described_class.new(fields: []) }.to raise_error(Dbee::Query::NoFieldsError)
45
- end
46
-
47
93
  it 'should remove duplicate filters (keep first instance)' do
48
94
  query_hash = {
49
95
  fields: [
@@ -124,60 +170,7 @@ describe Dbee::Query do
124
170
  end
125
171
 
126
172
  context 'README examples' do
127
- EXAMPLES = {
128
- 'Get all practices' => {
129
- fields: [
130
- { key_path: 'id' },
131
- { key_path: 'active' },
132
- { key_path: 'name' }
133
- ]
134
- },
135
- 'Get all practices, limit to 10, and sort by name (descending) then id (ascending)' => {
136
- fields: [
137
- { key_path: 'id' },
138
- { key_path: 'active' },
139
- { key_path: 'name' }
140
- ],
141
- sorters: [
142
- { key_path: 'name', direction: :descending },
143
- { key_path: 'id' }
144
- ],
145
- limit: 10
146
- },
147
- "Get top 5 active practices and patient whose name start with 'Sm':" => {
148
- fields: [
149
- { key_path: 'name', display: 'Practice Name' },
150
- { key_path: 'patients.first', display: 'Patient First Name' },
151
- { key_path: 'patients.middle', display: 'Patient Middle Name' },
152
- { key_path: 'patients.last', display: 'Patient Last Name' }
153
- ],
154
- filters: [
155
- { type: :equals, key_path: 'active', value: true },
156
- { type: :starts_with, key_path: 'patients.last', value: 'Sm' }
157
- ],
158
- limit: 5
159
- },
160
- 'Get practice IDs, patient IDs, names, and cell phone numbers that starts with 555' => {
161
- fields: [
162
- { key_path: 'id', display: 'Practice ID #' },
163
- { key_path: 'patients.id', display: 'Patient ID #' },
164
- { key_path: 'patients.first', display: 'Patient First Name' },
165
- { key_path: 'patients.middle', display: 'Patient Middle Name' },
166
- { key_path: 'patients.last', display: 'Patient Last Name' },
167
- { key_path: 'patients.cell_phone_numbers.phone_number', display: 'Patient Cell #' }
168
- ],
169
- filters: [
170
- { type: :equals, key_path: 'active', value: true },
171
- {
172
- type: :starts_with,
173
- key_path: 'patients.cell_phone_numbers.phone_number',
174
- value: '555'
175
- }
176
- ]
177
- }
178
- }.freeze
179
-
180
- EXAMPLES.each_pair do |name, query|
173
+ README_EXAMPLES.each_pair do |name, query|
181
174
  specify name do
182
175
  expect(described_class.make(query)).to be_a(described_class)
183
176
  end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ require 'spec_helper'
11
+ require 'fixtures/models'
12
+
13
+ describe Dbee::SchemaCreator do
14
+ let(:model_hash_graph) do
15
+ {
16
+ 'model1' => {
17
+ relationships: {
18
+ model2: {
19
+ constraints: [
20
+ {
21
+ type: 'reference',
22
+ parent: 'id',
23
+ name: 'model1_id'
24
+ }
25
+ ]
26
+ }
27
+ }
28
+ },
29
+ 'model2' => nil
30
+ }
31
+ end
32
+ let(:schema) { Dbee::Schema.new(model_hash_graph) }
33
+
34
+ let(:model_hash_tree) do
35
+ {
36
+ name: 'model1',
37
+ models: [
38
+ {
39
+ name: 'model2',
40
+ constraints: [
41
+ {
42
+ type: 'reference',
43
+ parent: 'id',
44
+ name: 'model1_id'
45
+ }
46
+ ]
47
+ }
48
+ ]
49
+ }
50
+ end
51
+
52
+ let(:model_tree) { Dbee::Model.make(model_hash_tree) }
53
+
54
+ let(:query_hash) do
55
+ {
56
+ from: 'model1',
57
+ fields: [
58
+ { key_path: :a }
59
+ ]
60
+ }
61
+ end
62
+ let(:query) { Dbee::Query.make(query_hash) }
63
+
64
+ let(:query_hash_no_from) { query_hash.slice(:fields) }
65
+ let(:query_no_from) { Dbee::Query.make(query_hash_no_from) }
66
+
67
+ describe 'query parsing' do
68
+ it 'passes through Dbee::Query instances' do
69
+ expect(described_class.new(schema, query).query).to eq query
70
+ end
71
+
72
+ it 'creates a Dbee::Query from a query hash' do
73
+ expect(described_class.new(schema, query_hash).query).to eq query
74
+ end
75
+
76
+ describe 'the "from" field' do
77
+ it 'raises an error when nil' do
78
+ expect do
79
+ described_class.new(schema, query_hash_no_from)
80
+ end.to raise_error(ArgumentError, 'query requires a from model name')
81
+ end
82
+
83
+ it 'raises an error when the empty string' do
84
+ expect do
85
+ described_class.new(schema, query_hash_no_from.merge(from: ''))
86
+ end.to raise_error(ArgumentError, 'query requires a from model name')
87
+ end
88
+ end
89
+ end
90
+
91
+ describe 'tree based models' do
92
+ it 'creates a schema from a Dbee::Model' do
93
+ expect(described_class.new(model_tree, query).schema).to eq schema
94
+ end
95
+
96
+ describe 'hash detection' do
97
+ it 'creates a schema from a hash model' do
98
+ expect(described_class.new(model_hash_tree, query).schema).to eq schema
99
+ end
100
+
101
+ it 'creates a schema from a minimal hash model with no child models' do
102
+ minimal_tree_hash = { name: :practice }
103
+ minimal_schema = Dbee::Schema.new(practice: nil)
104
+
105
+ expect(described_class.new(minimal_tree_hash, {}).schema).to eq minimal_schema
106
+ end
107
+ end
108
+
109
+ describe 'query "from" field' do
110
+ it 'is set using name of the root model of the tree' do
111
+ expect(described_class.new(model_tree, query_no_from).query).to eq query
112
+ end
113
+
114
+ it 'raises an error the query "from" does not equal the root model name' do
115
+ query_hash_different_from = query_hash_no_from.merge(from: :bogus_model)
116
+
117
+ expect do
118
+ described_class.new(model_tree, query_hash_different_from)
119
+ end.to raise_error(
120
+ ArgumentError,
121
+ "expected from model to be 'model1' but got 'bogus_model'"
122
+ )
123
+ end
124
+ end
125
+ end
126
+
127
+ describe 'graph based models' do
128
+ it 'creates a schema from a hash schema' do
129
+ expect(described_class.new(model_hash_graph, query).schema).to eq schema
130
+ end
131
+
132
+ it 'passes through Dbee::Schema instances' do
133
+ expect(described_class.new(schema, query).schema).to eq schema
134
+ end
135
+
136
+ describe 'when given a Dbee::Base class' do
137
+ let(:dsl_model_class) { PartitionerExamples::Dog }
138
+
139
+ it 'creates a schema' do
140
+ expected_config = yaml_fixture('models.yaml')['Partitioner Example 1']
141
+ expected_schema = Dbee::Schema.new(expected_config)
142
+ dog_query = Dbee::Query.make(query_hash_no_from.merge(from: :dog))
143
+
144
+ expect(described_class.new(dsl_model_class, dog_query).schema).to eq expected_schema
145
+ end
146
+
147
+ it "can infer the query's 'from' field" do
148
+ expect(described_class.new(dsl_model_class, query_no_from).query.from).to eq 'dog'
149
+ end
150
+
151
+ it 'raises an error the query "from" does not equal the DSL model name' do
152
+ cat_query = Dbee::Query.make(query_hash_no_from.merge(from: :cat))
153
+
154
+ expect do
155
+ described_class.new(dsl_model_class, cat_query)
156
+ end.to raise_error(
157
+ ArgumentError,
158
+ "expected from model to be 'dog' but got 'cat'"
159
+ )
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ require 'spec_helper'
11
+ require 'fixtures/models'
12
+
13
+ describe Dbee::SchemaFromTreeBasedModel do
14
+ let(:tree_config) do
15
+ Dbee::Model.make(yaml_fixture('models.yaml')['Theaters, Members, and Movies Tree Based'])
16
+ end
17
+ let(:schema_config) do
18
+ yaml_fixture('models.yaml')['Theaters, Members, and Movies from Tree']
19
+ end
20
+
21
+ it 'converts the theaters model' do
22
+ expected_schema = Dbee::Schema.new(schema_config)
23
+ expect(described_class.convert(tree_config)).to eq expected_schema
24
+ end
25
+
26
+ it 'does not raise any errors when converting the readme model' do
27
+ tree_config = Dbee::Model.make(yaml_fixture('models.yaml')['Readme Tree Based'])
28
+
29
+ expect { described_class.convert(tree_config) }.not_to raise_error
30
+ end
31
+ end