insights-api-common 5.0.0 → 5.0.5

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: 2d9fb8764a0f41c1080473eddd2d98dee2df8170f88da25fa2b033470957cf4f
4
- data.tar.gz: eb025888657a1ad4c68054aba85aa099ae4455e0ec028d1208ca7468f1a7a04d
3
+ metadata.gz: b8a770535b65f1da98fa3196078052c5048bd13dcd4253145aa04afa5d6c1bf9
4
+ data.tar.gz: 13f54d55fe5343eaec0116095014e3782232e836e8e1d610a1e1120ea59b68b8
5
5
  SHA512:
6
- metadata.gz: 9faab22d7dc2bd6f140fbf0db52c1f546101fff8c08039d3acef806495a5381baa5969e63eb345b769869fe321534cbc0fe26601eff88ce327aec22a6cf1a3ea
7
- data.tar.gz: 928591637a490109a8a3d38841c4b5b79e13205f565a9f14d145270257952885f40cc4cf84574bc70fdb2bc2b4338f17cc55274636261e5de630b7ae558c7e01
6
+ metadata.gz: d329437271a364dc4d03b31b484400c2ab496b91a17f2e39c80fd57c249a992b7e6764b69724677120fe1de219c315472835809df5001a348fd264bb66a8961c
7
+ data.tar.gz: 93510825434809fde1d60e46b73a8f80f61535d41d77f8ebe2df716e26212d1903a0f9d9911f328bf4e6607c6aaa5a64aea46617ad39a763819f6325cab851d7
data/README.md CHANGED
@@ -30,6 +30,7 @@ Or install it yourself as:
30
30
  | Supported Comparators | Comparator |
31
31
  | --------------------- | ---------- |
32
32
  | Integer | eq |
33
+ | | not_eq |
33
34
  | | gt |
34
35
  | | gte |
35
36
  | | lt |
@@ -37,12 +38,14 @@ Or install it yourself as:
37
38
  | | nil |
38
39
  | | not_nil |
39
40
  | String | eq |
41
+ | | not_eq |
40
42
  | | contains |
41
43
  | | starts_with |
42
44
  | | ends_with |
43
45
  | | nil |
44
46
  | | not_nil |
45
47
  | String (case insensitive) | eq_i |
48
+ | | not_eq_i |
46
49
  | | contains_i |
47
50
  | | starts_with_i |
48
51
  | | ends_with_i |
@@ -53,6 +56,7 @@ After implementing filtering in your application, this is the way to filter via
53
56
  | --------------- | --------------------- | ----------------- |
54
57
  | "?filter[name]=reviews" | { :filter => { :name => "reviews" } } | filter: { name: "reviews" } |
55
58
  | "?filter[name][eq]=reviews" | { :filter => { :name => { :eq => "reviews" } } } | filter: { name: { eq: "reviews" } } |
59
+ | "?filter[name][not_eq]=reviews" | { :filter => { :name => { :not_eq => "reviews" } } } | filter: { name: { not_eq: "reviews" } } |
56
60
  | "?filter[name][starts_with]=a" | { :filter => { :name => { :starts_with => "a" } } } | filter: { name: { starts_with: "a" } } |
57
61
  | "?filter[name][ends_with]=manager" | { :filter => { :name => { :ends_with => "manager" } } } | filter: { name: { ends_with: "manager" } } |
58
62
  | "?filter[name][contains]=openshift" | { :filter => { :name => { :contains => "openshift" } } } | filter: { name: { contains: "openshift" } } |
@@ -44,10 +44,14 @@ module Insights
44
44
  private
45
45
 
46
46
  def id_regexp(primary_collection_name)
47
- @id_regexp ||= begin
47
+ @id_regexp_table ||= {}
48
+
49
+ if @id_regexp_table.empty? || @id_regexp_table[primary_collection_name].nil?
48
50
  id_parameter = id_parameter_from_api_doc(primary_collection_name)
49
- id_parameter ? id_parameter.fetch_path("schema", "pattern") : /^\d+$/
51
+ @id_regexp_table[primary_collection_name] = id_parameter ? id_parameter.fetch_path("schema", "pattern") : /^\d+$/
50
52
  end
53
+
54
+ @id_regexp_table[primary_collection_name]
51
55
  end
52
56
 
53
57
  def id_parameter_from_api_doc(primary_collection_name)
@@ -4,7 +4,8 @@ module Insights
4
4
  class ErrorDocument
5
5
  def add(status = 400, message)
6
6
  @status = status
7
- errors << {"status" => status, "detail" => message}
7
+ safe_message = message.to_s.encode('UTF-8', :invalid => :replace, :undef => :replace)
8
+ errors << {"status" => status, "detail" => safe_message}
8
9
  self
9
10
  end
10
11
 
@@ -2,8 +2,8 @@ module Insights
2
2
  module API
3
3
  module Common
4
4
  class Filter
5
- INTEGER_COMPARISON_KEYWORDS = ["eq", "gt", "gte", "lt", "lte", "nil", "not_nil"].freeze
6
- STRING_COMPARISON_KEYWORDS = ["contains", "contains_i", "eq", "eq_i", "starts_with", "starts_with_i", "ends_with", "ends_with_i", "nil", "not_nil"].freeze
5
+ INTEGER_COMPARISON_KEYWORDS = ["eq", "not_eq", "gt", "gte", "lt", "lte", "nil", "not_nil"].freeze
6
+ STRING_COMPARISON_KEYWORDS = ["contains", "contains_i", "eq", "not_eq", "eq_i", "not_eq_i", "starts_with", "starts_with_i", "ends_with", "ends_with_i", "nil", "not_nil"].freeze
7
7
  ALL_COMPARISON_KEYWORDS = (INTEGER_COMPARISON_KEYWORDS + STRING_COMPARISON_KEYWORDS).uniq.freeze
8
8
 
9
9
  attr_reader :apply, :arel_table, :api_doc_definition, :extra_filterable_attributes, :model
@@ -177,6 +177,20 @@ module Insights
177
177
  end
178
178
  end
179
179
 
180
+ def self.build_filtered_scope(scope, api_version, klass_name, filter)
181
+ return scope unless filter
182
+
183
+ openapi_doc = ::Insights::API::Common::OpenApi::Docs.instance[api_version]
184
+ openapi_schema_name, = ::Insights::API::Common::GraphQL::Generator.openapi_schema(openapi_doc, klass_name)
185
+
186
+ action_parameters = ActionController::Parameters.new(filter)
187
+ definitions = openapi_doc.definitions
188
+
189
+ association_attribute_properties = association_attribute_properties(definitions, action_parameters)
190
+
191
+ new(scope, action_parameters, definitions[openapi_schema_name], association_attribute_properties).apply
192
+ end
193
+
180
194
  def timestamp(k, val)
181
195
  if val.kind_of?(Hash)
182
196
  val.each do |comparator, value|
@@ -242,6 +256,16 @@ module Insights
242
256
  self.query = query.where(model_arel_attribute(key).eq_any(Array(value)))
243
257
  end
244
258
 
259
+ def comparator_not_eq(key, value)
260
+ self.query = query.where.not(model_arel_attribute(key).eq_any(Array(value)))
261
+ end
262
+
263
+ def comparator_not_eq_i(key, value)
264
+ values = Array(value).map { |v| query.sanitize_sql_like(v.downcase) }
265
+
266
+ self.query = query.where.not(model_arel_table(key).grouping(arel_lower(key).matches_any(values)))
267
+ end
268
+
245
269
  def comparator_eq_i(key, value)
246
270
  values = Array(value).map { |v| query.sanitize_sql_like(v.downcase) }
247
271
 
@@ -20,8 +20,32 @@ module Insights
20
20
  openapi_path.split("/")[1..-1]
21
21
  end
22
22
 
23
+ def self.template_file_by(type, root_dir = __dir__)
24
+ Pathname.new(root_dir).join(File.expand_path("templates", root_dir), "#{type}.erb")
25
+ end
26
+
27
+ def self.root_dir
28
+ Rails.root
29
+ end
30
+
31
+ def self.app_name
32
+ Rails.application.class.parent.name.underscore
33
+ end
34
+
35
+ def self.pluggable_template_file_by(type)
36
+ templates_relative_path = "lib/#{app_name}/api/graphql/templates"
37
+ template_path = File.expand_path(templates_relative_path, root_dir)
38
+ Pathname.new(root_dir).join(template_path, "#{type}.erb")
39
+ end
40
+
41
+ def self.template_path_by(type)
42
+ template_path_pluggable = pluggable_template_file_by(type)
43
+ template_path_default = template_file_by(type)
44
+ template_path_pluggable.exist? ? template_path_pluggable : template_path_default
45
+ end
46
+
23
47
  def self.template(type)
24
- File.read(Pathname.new(__dir__).join(File.expand_path("templates", __dir__), "#{type}.erb").to_s)
48
+ File.read(template_path_by(type))
25
49
  end
26
50
 
27
51
  def self.graphql_type(property_name, property_format, property_type)
@@ -140,8 +164,16 @@ module Insights
140
164
 
141
165
  graphql_model_type_template = ERB.new(template("model_type"), nil, '<>').result(binding)
142
166
  graphql_namespace.module_eval(graphql_model_type_template)
167
+
168
+ unless graphql_namespace.const_defined?("#{klass_name}AggregateType", false)
169
+ graphql_aggregate_model_type_template = ERB.new(template("aggregate_model_type"), nil, '<>').result(binding)
170
+ graphql_namespace.module_eval(graphql_aggregate_model_type_template)
171
+ end
143
172
  end
144
173
 
174
+ graphql_aggregate_type_template = ERB.new(template("aggregate_type"), nil, '<>').result(binding)
175
+ graphql_namespace.module_eval(graphql_aggregate_type_template)
176
+
145
177
  graphql_query_type_template = ERB.new(template("query_type"), nil, '<>').result(binding)
146
178
  graphql_namespace.module_eval(graphql_query_type_template)
147
179
 
@@ -0,0 +1,8 @@
1
+ <%= klass_name %>AggregateType = ::GraphQL::ObjectType.define do
2
+ name "<%= klass_name %>AggregateType"
3
+ description "A <%= klass_name %>AggregateType to wrap metrics aggregation"
4
+
5
+ field :aggregate, AggregateType do
6
+ resolve ->(object, _args, _ctx) { object }
7
+ end
8
+ end
@@ -0,0 +1,14 @@
1
+ AggregateType = ::GraphQL::ObjectType.define do
2
+ name "AggregateType"
3
+ description "A AggregateType type for aggregation metrics"
4
+
5
+ field :total_count, !types.Int do
6
+ resolve ->(object, _args, _ctx) {
7
+ if object.kind_of?(QueryRelation) # from nested aggregation
8
+ object.count # count is array operation
9
+ else
10
+ object
11
+ end
12
+ }
13
+ end
14
+ end
@@ -30,6 +30,19 @@
30
30
  ::Insights::API::Common::GraphQL::AssociationLoader.new(<%= klass_name.constantize %>, "<%= associations %>", args, graphql_options).load(obj)
31
31
  }
32
32
  end
33
+
34
+ field :<%= associations %>_aggregate do
35
+ description "Aggregation of <%= associations %> associated with <%= klass_name %> model."
36
+ type <%= "#{association_class_name}AggregateType" %>
37
+
38
+ associations_name = "<%= associations %>"
39
+ preload :<%= associations %>
40
+
41
+ resolve lambda { |obj, args, _ctx|
42
+ ::Insights::API::Common::GraphQL::AssociationLoader.new(<%= klass_name.constantize %>, "<%= associations %>", args, graphql_options).load(obj)
43
+ }
44
+ end
45
+
33
46
  <% end %>
34
47
  <% end %>
35
48
  end
@@ -31,18 +31,9 @@ QueryType = ::GraphQL::ObjectType.define do
31
31
  scope = model_class
32
32
  end
33
33
 
34
- if args[:filter]
35
- openapi_doc = ::Insights::API::Common::OpenApi::Docs.instance["<%= api_version %>"]
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]))
39
- scope = ::Insights::API::Common::Filter.new(
40
- scope,
41
- ActionController::Parameters.new(args[:filter]),
42
- openapi_doc.definitions[openapi_schema_name],
43
- association_attribute_properties).apply
44
- end
34
+ scope = ::Insights::API::Common::Filter.build_filtered_scope(scope, "<%= api_version %>", klass_name, args[:filter])
45
35
  scope = ::Insights::API::Common::GraphQL.search_options(scope, args)
36
+
46
37
  if <%= graphql_options[:use_pagination_v2] %> == true
47
38
  ::Insights::API::Common::PaginatedResponseV2.new(
48
39
  base_query: scope, request: nil, limit: args[:limit], offset: args[:offset], sort_by: args[:sort_by]
@@ -54,5 +45,24 @@ QueryType = ::GraphQL::ObjectType.define do
54
45
  end
55
46
  }
56
47
  end
48
+
49
+ field_aggregate = "#{collection}_aggregate"
50
+ field field_aggregate do
51
+ description "The #{collection} aggregation associated with this #{klass_name}"
52
+ klass_name_type = "::Insights::API::Common::GraphQL::Api::#{version_namespace}::#{klass_name}AggregateType".constantize
53
+ argument :filter, ::Insights::API::Common::GraphQL::Types::QueryFilter, "The Query Filter for querying the #{collection}"
54
+
55
+ type klass_name_type
56
+
57
+ resolve lambda { |_obj, args, _ctx|
58
+ if base_query.present?
59
+ scope = base_query.call(model_class, args, ctx)
60
+ else
61
+ scope = model_class
62
+ end
63
+
64
+ ::Insights::API::Common::Filter.build_filtered_scope(scope, "<%= api_version %>", klass_name, args[:filter]).count
65
+ }
66
+ end
57
67
  end
58
68
  end
@@ -17,7 +17,7 @@ module Insights
17
17
 
18
18
  private_class_method def self.ensure_exporter_server
19
19
  require 'socket'
20
- TCPSocket.open("localhost", metrics_port) {}
20
+ TCPSocket.open("127.0.0.1", metrics_port) {}
21
21
  rescue Errno::ECONNREFUSED
22
22
  require 'prometheus_exporter/server'
23
23
  server = PrometheusExporter::Server::WebServer.new(port: metrics_port)
@@ -1,7 +1,7 @@
1
1
  module Insights
2
2
  module API
3
3
  module Common
4
- VERSION = "5.0.0".freeze
4
+ VERSION = "5.0.5".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: 5.0.0
4
+ version: 5.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Insights Authors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-14 00:00:00.000000000 Z
11
+ date: 2021-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: acts_as_tenant
@@ -160,16 +160,16 @@ dependencies:
160
160
  name: graphql
161
161
  requirement: !ruby/object:Gem::Requirement
162
162
  requirements:
163
- - - "~>"
163
+ - - '='
164
164
  - !ruby/object:Gem::Version
165
- version: '1.9'
165
+ version: 1.11.7
166
166
  type: :runtime
167
167
  prerelease: false
168
168
  version_requirements: !ruby/object:Gem::Requirement
169
169
  requirements:
170
- - - "~>"
170
+ - - '='
171
171
  - !ruby/object:Gem::Version
172
- version: '1.9'
172
+ version: 1.11.7
173
173
  - !ruby/object:Gem::Dependency
174
174
  name: graphql-batch
175
175
  requirement: !ruby/object:Gem::Requirement
@@ -396,6 +396,8 @@ files:
396
396
  - lib/insights/api/common/graphql/associated_records.rb
397
397
  - lib/insights/api/common/graphql/association_loader.rb
398
398
  - lib/insights/api/common/graphql/generator.rb
399
+ - lib/insights/api/common/graphql/templates/aggregate_model_type.erb
400
+ - lib/insights/api/common/graphql/templates/aggregate_type.erb
399
401
  - lib/insights/api/common/graphql/templates/model_type.erb
400
402
  - lib/insights/api/common/graphql/templates/query_type.erb
401
403
  - lib/insights/api/common/graphql/templates/schema.erb
@@ -457,7 +459,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
457
459
  - !ruby/object:Gem::Version
458
460
  version: '0'
459
461
  requirements: []
460
- rubygems_version: 3.0.3
462
+ rubygems_version: 3.0.3.1
461
463
  signing_key:
462
464
  specification_version: 4
463
465
  summary: Common Utilites for Insights microservices