insights-api-common 3.8.0 → 3.9.0

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: 7d2889f1fb8a92c64f95de6f5d4a97f111d35e796a2ab4fb6a6b54ade5bbef05
4
- data.tar.gz: 951defb8fb43a566b3f16b49e76f340d95d68f839831c1d6896b72c91dd0428d
3
+ metadata.gz: 484591366a853923b1456ad224f13ea7462cf54c7e2e50091307ab3f6e70dffd
4
+ data.tar.gz: 0cfbc0b28162bcf862173cd1a4941a6cd406e9ca301316be1adb4856dcd834d6
5
5
  SHA512:
6
- metadata.gz: 37ea90782a5dad4f376bcc9b99f051394e855b02f4689c93c8049f7a292cf373da4e4f20f8730efd4f8830e6a680b14e61849c2bf3c5198104b5efb9c167b9ef
7
- data.tar.gz: 8d331fdbfa57116010b32374e8dfc0ef016249336db7e86dc40d5f685f2f382ff2277064aea57dc9eca451e8be1248f40039828f97762358f3d0d7f9c121f9ed
6
+ metadata.gz: 3632e7ccbe7eea9360829aef22960d5605235d95a01b725e7113392787836adb73272ac5280853ea78d610b0bd3b731a507845fd4aecf9dfa2e68d1f5830c72f
7
+ data.tar.gz: 18460ede94fc0d761284dd1b497e31dd9a0993caa17cc949a982098a6faca24b54d1efb3daf78f776a0f0e2ef79f63eeaeb46940fbf1892de99472cba0d06f6a
data/README.md CHANGED
@@ -49,43 +49,76 @@ Or install it yourself as:
49
49
 
50
50
  After implementing filtering in your application, this is the way to filter via parameters on index functions:
51
51
 
52
- | Query Parameter | Ruby Client Parameter <br> **GraphQL filter: Parameter** |
53
- | --------------- | -------------------------------------------------------- |
54
- |"?filter[name]=reviews"|`{:filter => { :name => "reviews" }}`<br> **`filter: { name: "reviews" }`**|
55
- |"?filter[name][eq]=reviews"|`{:filter => { :name => { :eq => "reviews" } }}` <br> **`filter: { name: { eq: "reviews" } }`**|
56
- |"?filter[name][starts_with]=a"|`{:filter => { :name => { :starts_with => "a" } }}` <br> **`filter: { name: { starts_with: "a" } }`**|
57
- |"?filter[name][ends_with]=manager"|`{:filter => { :name => { :ends_with => "manager" } }}` <br> **`filter: { name: { ends_with: "manager" } }`**|
58
- |"?filter[name][contains]=openshift"|`{:filter => { :name => { :contains => "openshift" } }}` <br> **`filter: { name: { contains: "openshift" } }`**|
59
- |"?filter[id]=5"|`{:filter => { :id => "5" }}` <br> **`filter: { id: "5" }`**|
60
- |"?filter[id][eq]=5"|`{:filter => { :id => { :eq => "5" }}}` <br> **`filter: { id: { eq: "5" } }`**|
61
- |"?filter[id][gt]=180"|`{:filter => { :id => { :gt => "180" }}}` <br> **`filter: { id: { gt: "180" } }`**|
62
- |"?filter[id][gte]=190"|`{:filter => { :id => { :gte => "190" }}}` <br> **`filter: { id: { gte: "190" } }`**|
63
- |"?filter[id][lt]=5"|`{:filter => { :id => { :lt => "5" }}} ` <br> **`filter: { id: { lt: "5" } }`**|
64
- |"?filter[id][lte]=5"|`{:filter => { :id => { :lte => "5" }}}` <br> **`filter: { id: { lte: "5" } }`**|
65
- |"?filter[id][]=5&filter[id][]=10&filter[id][]=15&filter[id][]=20"|`{:filter => { :id => ["5", "10", "15", "20"]}}` <br> **`filter: { id: ["5", "10", "15", "20"] }`**|
66
- |"?filter[id][eq][]=5&filter[id][eq][]=10&filter[id][eq][]=15&filter[id][eq][]=20"|`{:filter => { :id => { :eq => ["5", "10", "15", "20"]}}}` <br> **`filter: { id: { eq: ["5", "10", "15", "20"] }`**|
52
+ | Query Parameter | Ruby Client Parameter | GraphQL Parameter |
53
+ | --------------- | --------------------- | ----------------- |
54
+ | "?filter[name]=reviews" | { :filter => { :name => "reviews" } } | filter: { name: "reviews" } |
55
+ | "?filter[name][eq]=reviews" | { :filter => { :name => { :eq => "reviews" } } } | filter: { name: { eq: "reviews" } } |
56
+ | "?filter[name][starts_with]=a" | { :filter => { :name => { :starts_with => "a" } } } | filter: { name: { starts_with: "a" } } |
57
+ | "?filter[name][ends_with]=manager" | { :filter => { :name => { :ends_with => "manager" } } } | filter: { name: { ends_with: "manager" } } |
58
+ | "?filter[name][contains]=openshift" | { :filter => { :name => { :contains => "openshift" } } } | filter: { name: { contains: "openshift" } } |
59
+ | "?filter[id]=5" | { :filter => { :id => "5" } } | filter: { id: "5" } |
60
+ | "?filter[id][eq]=5" | { :filter => { :id => { :eq => "5" } } } | filter: { id: { eq: "5" } } |
61
+ | "?filter[id][gt]=180" | { :filter => { :id => { :gt => "180" } } } | filter: { id: { gt: "180" } } |
62
+ | "?filter[id][gte]=190" | { :filter => { :id => { :gte => "190" } } } | filter: { id: { gte: "190" } } |
63
+ | "?filter[id][lt]=5" | { :filter => { :id => { :lt => "5" } } } | filter: { id: { lt: "5" } } |
64
+ | "?filter[id][lte]=5" | { :filter => { :id => { :lte => "5" } } } | filter: { id: { lte: "5" } } |
65
+ | "?filter[id][]=5&filter[id][]=10&filter[id][]=15&filter[id][]=20" | { :filter => { :id => ["5", "10", "15", "20"] } } | filter: { id: ["5", "10", "15", "20"] } |
66
+ | "?filter[id][eq][]=5&filter[id][eq][]=10&filter[id][eq][]=15&filter[id][eq][]=20" | { :filter => { :id => { :eq => ["5", "10", "15", "20"] } } } | filter: { id: { eq: ["5", "10", "15", "20"] } |
67
67
 
68
68
  #### Sorting Results
69
69
 
70
70
  Sorting query results is controlled via the _sort_by_ query parameter. The _sort_by_ parameter is available for both REST API and GraphQL requests.
71
71
 
72
- The syntax for the _sort_by_ parameter supports:
72
+ The _sort_by_ parameter specifies which attribute name to sort the results by, and may include a sort order of ascending _asc_ or descending _desc_. The default behavior when no sorting order is specified is to sort by ascending order.
73
+
74
+ The syntax for the _sort_by_ parameter is as follows:
73
75
 
74
- - a single string representing the attribute name to sort by which may be followed by :asc or :desc
75
- - **attribute** (_default order is ascending_)
76
- - **attribute:asc** (_ascending order_)
77
- - **attribute:desc** (_descending order_)
78
- - an array of strings of the above syntax
76
+ - One or more object keys representing the attribute name(s) to sort by which may be assigned the **asc** or **desc** value for the sort order.
77
+
78
+ - [**attribute**] (_default order is ascending_)
79
+ - [**attribute**]=**asc** (_ascending order_)
80
+ - [**attribute**]=**desc** (_descending order_)
79
81
 
80
82
  ##### Sort_by Examples:
81
83
 
82
- - GET /api/v1.0/sources?sort_by=name
83
- - GET /api/v1.0/vms?sort_by[]=power_state\&sort_by[]=memory:desc
84
+ - GET /api/v2.0/sources?sort_by[name]
85
+ - GET /api/v2.0/vms?sort_by[power_state]&sort_by[memory]=desc
86
+
87
+ | Query Parameter | Ruby Client Parameter | GraphQL Parameter |
88
+ | --------------- | --------------------- | ----------------- |
89
+ | "?sort_by[name]" | { :sort_by => { :name => nil } } | sort_by: { name: null } |
90
+ | "?sort_by[name]=asc" | { :sort_by => { :name => "asc" } } | sort_by: { name: "asc" } |
91
+ | "?sort_by[power_state]&sort_by[memory]=desc" | { :sort_by => { :power_state => nil, :memory => "desc" } } | sort_by: { power_state: null, memory: "desc" } |
92
+
93
+ #### Filtering and Sorting by Association attribute
94
+
95
+ Requests can also be filtered by assocation attribute and sorted by association attribute and count in addition to the direct attribute specified as in the above examples.
96
+
97
+ Single level association can be specified as follows:
98
+
99
+ ##### Filter by association attribute:
100
+
101
+ | Query Parameter | Ruby Client Parameter | GraphQL Parameter |
102
+ | --------------- | --------------------- | ----------------- |
103
+ | "?filter[association][name]=reviews" | { :filter => { :association => { :name => "reviews" } } } | filter: { association: { name: "reviews" } } |
104
+ | "?filter[association][name][eq]=reviews" | { :filter => { :association => { :name => { :eq => "reviews" } } } } | filter: { association: { name: { eq: "reviews" } } } |
105
+
106
+ ##### Sort by association attributes and count:
107
+
108
+ The _sort_by_ parameter can also be used to choose to sort by attributes of association objects as well as sorting by
109
+ the count of association records by specifying the **__count** special attribute as follows:
110
+
111
+ | Query Parameter | Ruby Client Parameter | GraphQL Parameter |
112
+ | --------------- | --------------------- | ----------------- |
113
+ | "?sort_by[association][name]" | { :sort_by => { :association => { :name => nil } } } | sort_by: { association: { name: null } } |
114
+ | "?sort_by[association][name]=desc" | { :sort_by => { :association => { :name => "desc" } } } | sort_by: { association: { name: "desc" } } |
115
+ | "?sort_by[association][__count]=asc" | { :sort_by => { :association => { :__count => "asc" } } } | sort_by: { association: { __count: "asc" } } |
116
+
117
+ ##### Combined Filtering and Sorting example:
84
118
 
85
119
  | Query Parameter | Ruby Client Parameter | GraphQL Parameter |
86
120
  | --------------- | --------------------- | ----------------- |
87
- | "?sort_by=name" | { :sort_by => "name" } | sort_by: "name" |
88
- | "?sort_by[]=power_state\&sort_by[]=memory:desc" | { :sort_by => ["power_state", "memory:desc"] } | sort_by: ["power_state, "memory:desc"] |
121
+ | "?filter[name][starts_with]=sample_&sort_by[application_types][__count]=desc&sort_by[name]=asc" | { :filter => { :name => { :starts_with => "sample_" } }, :sort_by => { :application_types => { :__count => "desc" }, :name => "asc" } } | filter: { name: { starts_with: "sample_" } }, sort_by: { application_types: { __count: "desc" }, name: "asc" } |
89
122
 
90
123
  ## Development
91
124
 
@@ -17,6 +17,10 @@ module Insights
17
17
  self.class.send(:api_doc_definition)
18
18
  end
19
19
 
20
+ def api_doc_definitions
21
+ self.class.send(:api_doc_definitions)
22
+ end
23
+
20
24
  module ClassMethods
21
25
  private
22
26
 
@@ -28,6 +32,10 @@ module Insights
28
32
  @api_doc_definition ||= api_doc.definitions[name.split("::").last[0..-11].singularize]
29
33
  end
30
34
 
35
+ def api_doc_definitions
36
+ @api_doc_definitions ||= api_doc.definitions
37
+ end
38
+
31
39
  def api_version
32
40
  @api_version ||= name.split("::")[1].downcase
33
41
  end
@@ -90,7 +90,11 @@ module Insights
90
90
 
91
91
  def filtered
92
92
  check_if_openapi_enabled
93
- Insights::API::Common::Filter.new(model, safe_params_for_list[:filter], api_doc_definition, extra_attributes_for_filtering).apply
93
+ association_attribute_properties =
94
+ Insights::API::Common::Filter.association_attribute_properties(api_doc_definitions, safe_params_for_list[:filter])
95
+ extra_attribute_properties = extra_attributes_for_filtering.merge(association_attribute_properties)
96
+
97
+ Insights::API::Common::Filter.new(model, safe_params_for_list[:filter], api_doc_definition, extra_attribute_properties).apply
94
98
  end
95
99
 
96
100
  def extra_attributes_for_filtering
@@ -4,6 +4,7 @@ module Insights
4
4
  class Filter
5
5
  INTEGER_COMPARISON_KEYWORDS = ["eq", "gt", "gte", "lt", "lte", "nil", "not_nil"].freeze
6
6
  STRING_COMPARISON_KEYWORDS = ["contains", "contains_i", "eq", "eq_i", "starts_with", "starts_with_i", "ends_with", "ends_with_i", "nil", "not_nil"].freeze
7
+ ALL_COMPARISON_KEYWORDS = (INTEGER_COMPARISON_KEYWORDS + STRING_COMPARISON_KEYWORDS).uniq.freeze
7
8
 
8
9
  attr_reader :apply, :arel_table, :api_doc_definition, :extra_filterable_attributes, :model
9
10
 
@@ -23,18 +24,23 @@ module Insights
23
24
  # A new Filter object, call #apply to get the filtered set of results.
24
25
  #
25
26
  def initialize(model, raw_filter, api_doc_definition, extra_filterable_attributes = {})
26
- self.query = model
27
+ @raw_filter = raw_filter
27
28
  @api_doc_definition = api_doc_definition
28
29
  @arel_table = model.arel_table
29
30
  @extra_filterable_attributes = extra_filterable_attributes
30
31
  @model = model
31
- @raw_filter = raw_filter
32
32
  end
33
33
 
34
+ def query
35
+ @query ||= filter_associations.present? ? model.left_outer_joins(filter_associations) : model
36
+ end
37
+ attr_writer :query
38
+
34
39
  def apply
35
40
  return query if @raw_filter.blank?
36
- @raw_filter.each do |k, v|
37
- next unless attribute = attribute_for_key(k)
41
+
42
+ self.class.compact_filter(@raw_filter).each do |k, v|
43
+ next unless (attribute = attribute_for_key(k))
38
44
 
39
45
  if attribute["type"] == "string"
40
46
  type = determine_string_attribute_type(attribute)
@@ -48,13 +54,86 @@ module Insights
48
54
  query
49
55
  end
50
56
 
57
+ # Compact filters to support association filtering
58
+ #
59
+ # Input: {"source_type"=>{"name"=>{"eq"=>"rhev"}}}
60
+ # Output: {"source_type.name"=>{"eq"=>"rhev"}}
61
+ #
62
+ # Input: {"source_type"=>{"name"=>{"eq"=>["openstack", "openshift"]}}}
63
+ # Output: {"source_type.name"=>{"eq"=>["openstack", "openshift"]}}
64
+ #
65
+ def self.compact_filter(filter)
66
+ result = {}
67
+ return result if filter.blank?
68
+ return filter unless filter.kind_of?(Hash) || filter.kind_of?(ActionController::Parameters)
69
+
70
+ filter = Hash(filter.permit!) if filter.kind_of?(ActionController::Parameters)
71
+
72
+ filter.each do |ak, av|
73
+ if av.kind_of?(Hash)
74
+ av.each do |atk, atv|
75
+ if !ALL_COMPARISON_KEYWORDS.include?(atk)
76
+ result["#{ak}.#{atk}"] = atv
77
+ else
78
+ result[ak] = av
79
+ end
80
+ end
81
+ else
82
+ result[ak] = av
83
+ end
84
+ end
85
+ result
86
+ end
87
+
88
+ def self.association_attribute_properties(api_doc_definitions, raw_filter)
89
+ return {} if raw_filter.blank?
90
+
91
+ association_attributes = compact_filter(raw_filter).keys.select { |key| key.include?('.') }.compact.uniq
92
+ return {} if association_attributes.blank?
93
+
94
+ association_attributes.each_with_object({}) do |key, hash|
95
+ association, attr = key.split('.')
96
+ hash[key] = api_doc_definitions[association.singularize.classify].properties[attr.to_s]
97
+ end
98
+ end
99
+
51
100
  private
52
101
 
53
- attr_accessor :query
54
102
  delegate(:arel_attribute, :to => :model)
55
103
 
56
104
  class Error < ArgumentError; end
57
105
 
106
+ def key_model_attribute(key)
107
+ if key.include?('.')
108
+ association, attr = key.split('.')
109
+ [association.classify.constantize, attr]
110
+ else
111
+ [model, key]
112
+ end
113
+ end
114
+
115
+ def model_arel_attribute(key)
116
+ key_model, attr = key_model_attribute(key)
117
+ key_model.arel_attribute(attr)
118
+ end
119
+
120
+ def model_arel_table(key)
121
+ key_model, attr = key_model_attribute(key)
122
+ key_model.arel_table
123
+ end
124
+
125
+ def filter_associations
126
+ return nil if @raw_filter.blank?
127
+
128
+ @filter_associations ||= begin
129
+ self.class.compact_filter(@raw_filter).keys.collect do |key|
130
+ next unless key.include?('.')
131
+
132
+ key.split('.').first.to_sym
133
+ end.compact.uniq
134
+ end
135
+ end
136
+
58
137
  def attribute_for_key(key)
59
138
  attribute = api_doc_definition.properties[key.to_s]
60
139
  attribute ||= extra_filterable_attributes[key.to_s]
@@ -74,7 +153,7 @@ module Insights
74
153
  end
75
154
 
76
155
  def string(k, val)
77
- if val.kind_of?(ActionController::Parameters)
156
+ if val.kind_of?(Hash)
78
157
  val.each do |comparator, value|
79
158
  add_filter(comparator, STRING_COMPARISON_KEYWORDS, k, value)
80
159
  end
@@ -99,7 +178,7 @@ module Insights
99
178
  end
100
179
 
101
180
  def timestamp(k, val)
102
- if val.kind_of?(ActionController::Parameters)
181
+ if val.kind_of?(Hash)
103
182
  val.each do |comparator, value|
104
183
  add_filter(comparator, INTEGER_COMPARISON_KEYWORDS, k, value)
105
184
  end
@@ -118,7 +197,7 @@ module Insights
118
197
  end
119
198
 
120
199
  def integer(k, val)
121
- if val.kind_of?(ActionController::Parameters)
200
+ if val.kind_of?(Hash)
122
201
  val.each do |comparator, value|
123
202
  add_filter(comparator, INTEGER_COMPARISON_KEYWORDS, k, value)
124
203
  end
@@ -128,69 +207,69 @@ module Insights
128
207
  end
129
208
 
130
209
  def arel_lower(key)
131
- Arel::Nodes::NamedFunction.new("LOWER", [arel_attribute(key)])
210
+ Arel::Nodes::NamedFunction.new("LOWER", [model_arel_attribute(key)])
132
211
  end
133
212
 
134
213
  def comparator_contains(key, value)
135
214
  return value.each { |v| comparator_contains(key, v) } if value.kind_of?(Array)
136
215
 
137
- self.query = query.where(arel_attribute(key).matches("%#{query.sanitize_sql_like(value)}%", nil, true))
216
+ self.query = query.where(model_arel_attribute(key).matches("%#{query.sanitize_sql_like(value)}%", nil, true))
138
217
  end
139
218
 
140
219
  def comparator_contains_i(key, value)
141
220
  return value.each { |v| comparator_contains_i(key, v) } if value.kind_of?(Array)
142
221
 
143
- self.query = query.where(arel_table.grouping(arel_lower(key).matches("%#{query.sanitize_sql_like(value.downcase)}%", nil, true)))
222
+ self.query = query.where(model_arel_table(key).grouping(arel_lower(key).matches("%#{query.sanitize_sql_like(value.downcase)}%", nil, true)))
144
223
  end
145
224
 
146
225
  def comparator_starts_with(key, value)
147
- self.query = query.where(arel_attribute(key).matches("#{query.sanitize_sql_like(value)}%", nil, true))
226
+ self.query = query.where(model_arel_attribute(key).matches("#{query.sanitize_sql_like(value)}%", nil, true))
148
227
  end
149
228
 
150
229
  def comparator_starts_with_i(key, value)
151
- self.query = query.where(arel_table.grouping(arel_lower(key).matches("#{query.sanitize_sql_like(value.downcase)}%", nil, true)))
230
+ self.query = query.where(model_arel_table(key).grouping(arel_lower(key).matches("#{query.sanitize_sql_like(value.downcase)}%", nil, true)))
152
231
  end
153
232
 
154
233
  def comparator_ends_with(key, value)
155
- self.query = query.where(arel_attribute(key).matches("%#{query.sanitize_sql_like(value)}", nil, true))
234
+ self.query = query.where(model_arel_attribute(key).matches("%#{query.sanitize_sql_like(value)}", nil, true))
156
235
  end
157
236
 
158
237
  def comparator_ends_with_i(key, value)
159
- self.query = query.where(arel_table.grouping(arel_lower(key).matches("%#{query.sanitize_sql_like(value.downcase)}", nil, true)))
238
+ self.query = query.where(model_arel_table(key).grouping(arel_lower(key).matches("%#{query.sanitize_sql_like(value.downcase)}", nil, true)))
160
239
  end
161
240
 
162
241
  def comparator_eq(key, value)
163
- self.query = query.where(arel_attribute(key).eq_any(Array(value)))
242
+ self.query = query.where(model_arel_attribute(key).eq_any(Array(value)))
164
243
  end
165
244
 
166
245
  def comparator_eq_i(key, value)
167
246
  values = Array(value).map { |v| query.sanitize_sql_like(v.downcase) }
168
247
 
169
- self.query = query.where(arel_table.grouping(arel_lower(key).matches_any(values)))
248
+ self.query = query.where(model_arel_table(key).grouping(arel_lower(key).matches_any(values)))
170
249
  end
171
250
 
172
251
  def comparator_gt(key, value)
173
- self.query = query.where(arel_attribute(key).gt(value))
252
+ self.query = query.where(model_arel_attribute(key).gt(value))
174
253
  end
175
254
 
176
255
  def comparator_gte(key, value)
177
- self.query = query.where(arel_attribute(key).gteq(value))
256
+ self.query = query.where(model_arel_attribute(key).gteq(value))
178
257
  end
179
258
 
180
259
  def comparator_lt(key, value)
181
- self.query = query.where(arel_attribute(key).lt(value))
260
+ self.query = query.where(model_arel_attribute(key).lt(value))
182
261
  end
183
262
 
184
263
  def comparator_lte(key, value)
185
- self.query = query.where(arel_attribute(key).lteq(value))
264
+ self.query = query.where(model_arel_attribute(key).lteq(value))
186
265
  end
187
266
 
188
267
  def comparator_nil(key, _value = nil)
189
- self.query = query.where(arel_attribute(key).eq(nil))
268
+ self.query = query.where(model_arel_attribute(key).eq(nil))
190
269
  end
191
270
 
192
271
  def comparator_not_nil(key, _value = nil)
193
- self.query = query.where.not(arel_attribute(key).eq(nil))
272
+ self.query = query.where.not(model_arel_attribute(key).eq(nil))
194
273
  end
195
274
  end
196
275
  end
@@ -34,10 +34,13 @@ QueryType = ::GraphQL::ObjectType.define do
34
34
  if args[:filter]
35
35
  openapi_doc = ::Insights::API::Common::OpenApi::Docs.instance["<%= api_version %>"]
36
36
  openapi_schema_name, _schema = ::Insights::API::Common::GraphQL::Generator.openapi_schema(openapi_doc, klass_name)
37
+ association_attribute_properties =
38
+ Insights::API::Common::Filter.association_attribute_properties(openapi_doc.definitions, ActionController::Parameters.new(args[:filter]))
37
39
  scope = ::Insights::API::Common::Filter.new(
38
40
  scope,
39
41
  ActionController::Parameters.new(args[:filter]),
40
- openapi_doc.definitions[openapi_schema_name]).apply
42
+ openapi_doc.definitions[openapi_schema_name],
43
+ association_attribute_properties).apply
41
44
  end
42
45
  scope = ::Insights::API::Common::GraphQL.search_options(scope, args)
43
46
  if <%= graphql_options[:use_pagination_v2] %> == true
@@ -12,7 +12,7 @@ module Insights
12
12
 
13
13
  Service.call(RBACApiClient::GroupApi) do |api|
14
14
  uuids = SortedSet.new
15
- Service.paginate(api, :list_groups, {}).each { |group| uuids << group.uuid }
15
+ Service.paginate(api, :list_groups, {:uuid => @group_uuids.to_a}).each { |group| uuids << group.uuid }
16
16
  missing = @group_uuids - uuids
17
17
  raise Insights::API::Common::InvalidParameter, "The following group uuids are missing #{missing.to_a.join(",")}" unless missing.empty?
18
18
  end
@@ -1,7 +1,7 @@
1
1
  module Insights
2
2
  module API
3
3
  module Common
4
- VERSION = "3.8.0".freeze
4
+ VERSION = "3.9.0".freeze
5
5
  end
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: insights-api-common
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.8.0
4
+ version: 3.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Insights Authors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-24 00:00:00.000000000 Z
11
+ date: 2020-04-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: acts_as_tenant
@@ -134,14 +134,14 @@ dependencies:
134
134
  requirements:
135
135
  - - "~>"
136
136
  - !ruby/object:Gem::Version
137
- version: 0.6.1
137
+ version: 0.10.0
138
138
  type: :runtime
139
139
  prerelease: false
140
140
  version_requirements: !ruby/object:Gem::Requirement
141
141
  requirements:
142
142
  - - "~>"
143
143
  - !ruby/object:Gem::Version
144
- version: 0.6.1
144
+ version: 0.10.0
145
145
  - !ruby/object:Gem::Dependency
146
146
  name: graphql
147
147
  requirement: !ruby/object:Gem::Requirement