procore-sift 0.13.0 → 0.14.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
- SHA1:
3
- metadata.gz: f18552ca0480672282156cc7664312b98dbad772
4
- data.tar.gz: 81612b0c72fa8827496fe23af9e804993d4d1484
2
+ SHA256:
3
+ metadata.gz: 3faa95a328bd7cb7f4252ed830e08bb0032a367526b223f3b24857b955912c2d
4
+ data.tar.gz: ef52247a7f9fd757d18fa662f7b7d7e4f9ab49e45b80de9ae68e18690fba80d8
5
5
  SHA512:
6
- metadata.gz: 0dc882e598295f080ead00b2b791ecbd5a86722ac7108ce37d00835b7c47790d4dc90d70140cc0bfd8d0b9f66b8b0861072b24e7c84f5ac69170365aedf82572
7
- data.tar.gz: 753c9f44ea32b532bc1fb99d5d1b840255c3132eaf5bcfab5842eec2778439b2069e0c631d4a17aba4b41dec583b17f243e341c9f7557ea4aae1ae30ab785403
6
+ metadata.gz: aa7c02c0b73fcefc709b7030b636611ec6a821f14df32080fb21c4e4ddf0a50288ad298775bd8919196b47e30652e4d02a1303d1189d9e7703aadc435d7be54e
7
+ data.tar.gz: 4d1dfefa31e8b1537d9605d8724fa2fceb16739ed62300da2e38ec8a8ba4f567a47712b8b7fd82f52dc229495a14524896d7eab9dbbcfcde665fc9113dfe3ea6
data/README.md CHANGED
@@ -46,6 +46,7 @@ Every filter must have a type, so that Sift knows what to do with it. The curren
46
46
  * time - Filter on a time column
47
47
  * datetime - Filter on a datetime column
48
48
  * scope - Filter on an ActiveRecord scope
49
+ * jsonb - Filter on a jsonb column (supported only in PostgreSQL)
49
50
 
50
51
  ### Filter on Scopes
51
52
  Just as your filter values are used to scope queries on a column, values you
@@ -122,6 +123,36 @@ The following types support ranges
122
123
  * time
123
124
  * datetime
124
125
 
126
+ ### Filter on jsonb column
127
+
128
+ Usually JSONB columns stores values as an Array or an Object (key-value), in both cases the parameter needs to be sent in a JSON format
129
+
130
+ **Array**
131
+
132
+ It should be sent an array in the URL Query parameters
133
+ * `?filters[metadata]=[1,2]`
134
+
135
+ **key-value**
136
+
137
+ It can be passed one or more Key values:
138
+ * `?filters[metadata]={"data_1":"test"}`
139
+ * `?filters[metadata]={"data_1":"test","data_2":"[1,2]"}`
140
+
141
+ When the value is an array, it will filter records with those values or more, for example:
142
+
143
+ * `?filters[metadata]={"data_2":"[1,2]"}`
144
+
145
+ Will return records with next values stored in the JSONB column `metadata`:
146
+ ```ruby
147
+ { data_2: 1 }
148
+ { data_2: 2 }
149
+ { data_2: [1] }
150
+ { data_2: [2] }
151
+ { data_2: [1,2] }
152
+ { data_2: [1,2,3] }
153
+ ```
154
+
155
+
125
156
  ### Filter on JSON Array
126
157
  `int` type filters support sending the values as an array in the URL Query parameters. For example `?filters[id]=[1,2]`. This is a way to keep payloads smaller for GET requests. When URI encoded this will become `filters%5Bid%5D=%5B1,2%5D` which is much smaller the standard format of `filters%5Bid%5D%5B%5D=1&&filters%5Bid%5D%5B%5D=2`.
127
158
 
@@ -10,6 +10,7 @@ require "sift/scope_handler"
10
10
  require "sift/where_handler"
11
11
  require "sift/validators/valid_int_validator"
12
12
  require "sift/validators/valid_date_range_validator"
13
+ require "sift/validators/valid_json_validator"
13
14
 
14
15
  module Sift
15
16
  extend ActiveSupport::Concern
@@ -13,7 +13,8 @@ module Sift
13
13
  {
14
14
  supports_boolean: supports_boolean?,
15
15
  supports_ranges: supports_ranges?,
16
- supports_json: supports_json?
16
+ supports_json: supports_json?,
17
+ supports_json_object: supports_json_object?
17
18
  }
18
19
  end
19
20
 
@@ -32,7 +33,11 @@ module Sift
32
33
  end
33
34
 
34
35
  def supports_json?
35
- type == :int
36
+ [:int, :jsonb].include?(type)
37
+ end
38
+
39
+ def supports_json_object?
40
+ type == :jsonb
36
41
  end
37
42
 
38
43
  def supports_boolean?
@@ -4,6 +4,7 @@ module Sift
4
4
  DATETIME_RANGE_PATTERN = { format: { with: /\A.+(?:[^.]\.\.\.[^.]).+\z/, message: "must be a range" }, valid_date_range: true }.freeze
5
5
  DECIMAL_PATTERN = { numericality: true, allow_nil: true }.freeze
6
6
  BOOLEAN_PATTERN = { inclusion: { in: [true, false] }, allow_nil: true }.freeze
7
+ JSON_PATTERN = { valid_json: true }.freeze
7
8
 
8
9
  WHITELIST_TYPES = [:int,
9
10
  :decimal,
@@ -13,7 +14,8 @@ module Sift
13
14
  :date,
14
15
  :time,
15
16
  :datetime,
16
- :scope].freeze
17
+ :scope,
18
+ :jsonb].freeze
17
19
 
18
20
  def initialize(param, type)
19
21
  @param = param
@@ -32,6 +34,8 @@ module Sift
32
34
  DECIMAL_PATTERN
33
35
  when :boolean
34
36
  BOOLEAN_PATTERN
37
+ when :jsonb
38
+ JSON_PATTERN
35
39
  end
36
40
  end
37
41
 
@@ -0,0 +1,14 @@
1
+ class ValidJsonValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ record.errors.add attribute, "must be a valid JSON" unless valid_json?(value)
4
+ end
5
+
6
+ private
7
+
8
+ def valid_json?(value)
9
+ value = value.strip if value.is_a?(String)
10
+ JSON.parse(value)
11
+ rescue JSON::ParserError
12
+ false
13
+ end
14
+ end
@@ -5,6 +5,7 @@ module Sift
5
5
  @supports_boolean = options.fetch(:supports_boolean, false)
6
6
  @supports_ranges = options.fetch(:supports_ranges, false)
7
7
  @supports_json = options.fetch(:supports_json, false)
8
+ @supports_json_object = options.fetch(:supports_json_object, false)
8
9
  @value = normalized_value(value, type)
9
10
  end
10
11
 
@@ -15,26 +16,39 @@ module Sift
15
16
  elsif parse_as_boolean?
16
17
  boolean_value
17
18
  elsif parse_as_json?
18
- array_from_json
19
+ supports_json_object ? parse_json_and_values : array_from_json
19
20
  else
20
21
  value
21
22
  end
22
23
  end
23
24
 
25
+ def parse_json(string)
26
+ JSON.parse(string)
27
+ rescue JSON::ParserError
28
+ string
29
+ end
30
+
31
+ def parse_json_and_values
32
+ parsed_jsonb = parse_json(value)
33
+ return parsed_jsonb if parsed_jsonb.is_a?(Array) || parsed_jsonb.is_a?(String)
34
+
35
+ parsed_jsonb.each_with_object({}) do |key_value, hash|
36
+ hash[key_value.first] = parse_json(key_value.last.to_s)
37
+ end
38
+ end
39
+
24
40
  def array_from_json
25
- result = JSON.parse(value)
41
+ result = parse_json(value)
26
42
  if result.is_a?(Array)
27
43
  result
28
44
  else
29
45
  value
30
46
  end
31
- rescue JSON::ParserError
32
- value
33
47
  end
34
48
 
35
49
  private
36
50
 
37
- attr_reader :value, :type, :supports_boolean, :supports_json, :supports_ranges
51
+ attr_reader :value, :type, :supports_boolean, :supports_json, :supports_json_object, :supports_ranges
38
52
 
39
53
  def parse_as_range?(raw_value=value)
40
54
  supports_ranges && raw_value.to_s.include?("...")
@@ -1,3 +1,3 @@
1
1
  module Sift
2
- VERSION = "0.13.0".freeze
2
+ VERSION = "0.14.0".freeze
3
3
  end
@@ -5,7 +5,28 @@ module Sift
5
5
  end
6
6
 
7
7
  def call(collection, value, _params, _scope_params)
8
- collection.where(@param.internal_name => value)
8
+ if @param.type == :jsonb
9
+ apply_jsonb_conditions(collection, value)
10
+ else
11
+ collection.where(@param.internal_name => value)
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def apply_jsonb_conditions(collection, value)
18
+ return collection.where("#{@param.internal_name} @> ?", val.to_s) if value.is_a?(Array)
19
+
20
+ value.each do |key, val|
21
+ condition = if val.is_a?(Array)
22
+ "('{' || TRANSLATE(#{@param.internal_name}->>'#{key}', '[]','') || '}')::int[] && ARRAY[?]"
23
+ else # Single Value
24
+ val = val.to_s
25
+ "#{@param.internal_name}->>'#{key}' = ?"
26
+ end
27
+ collection = collection.where(condition, val)
28
+ end
29
+ collection
9
30
  end
10
31
  end
11
32
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: procore-sift
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Procore Technologies
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-27 00:00:00.000000000 Z
11
+ date: 2020-06-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -115,6 +115,7 @@ files:
115
115
  - lib/sift/type_validator.rb
116
116
  - lib/sift/validators/valid_date_range_validator.rb
117
117
  - lib/sift/validators/valid_int_validator.rb
118
+ - lib/sift/validators/valid_json_validator.rb
118
119
  - lib/sift/value_parser.rb
119
120
  - lib/sift/version.rb
120
121
  - lib/sift/where_handler.rb
@@ -138,8 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
139
  - !ruby/object:Gem::Version
139
140
  version: '0'
140
141
  requirements: []
141
- rubyforge_project:
142
- rubygems_version: 2.6.14
142
+ rubygems_version: 3.1.3
143
143
  signing_key:
144
144
  specification_version: 4
145
145
  summary: Summary of Sift.