procore-sift 0.13.0 → 0.14.0
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.
- checksums.yaml +5 -5
- data/README.md +31 -0
- data/lib/procore-sift.rb +1 -0
- data/lib/sift/parameter.rb +7 -2
- data/lib/sift/type_validator.rb +5 -1
- data/lib/sift/validators/valid_json_validator.rb +14 -0
- data/lib/sift/value_parser.rb +19 -5
- data/lib/sift/version.rb +1 -1
- data/lib/sift/where_handler.rb +22 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3faa95a328bd7cb7f4252ed830e08bb0032a367526b223f3b24857b955912c2d
|
4
|
+
data.tar.gz: ef52247a7f9fd757d18fa662f7b7d7e4f9ab49e45b80de9ae68e18690fba80d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
|
data/lib/procore-sift.rb
CHANGED
@@ -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
|
data/lib/sift/parameter.rb
CHANGED
@@ -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
|
-
|
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?
|
data/lib/sift/type_validator.rb
CHANGED
@@ -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
|
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
|
data/lib/sift/value_parser.rb
CHANGED
@@ -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 =
|
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?("...")
|
data/lib/sift/version.rb
CHANGED
data/lib/sift/where_handler.rb
CHANGED
@@ -5,7 +5,28 @@ module Sift
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def call(collection, value, _params, _scope_params)
|
8
|
-
|
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.
|
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-
|
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
|
-
|
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.
|