Comparing changes
v0.4.0
→
v0.5.0
7 commits
13 files changed
Commits
Changed files (13)
lib
scim
kit
spec
scim
kit
lib/scim/kit/v2/filter/node.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'parslet'
+
+module Scim
+ module Kit
+ module V2
+ class Filter
+ # @private
+ class Node
+ def initialize(hash)
+ @hash = hash
+ end
+
+ def operator
+ self[:operator].to_sym
+ end
+
+ def attribute
+ self[:attribute].to_s
+ end
+
+ def value
+ self[:value].to_s[1..-2]
+ end
+
+ def not?
+ @hash.key?(:not)
+ end
+
+ def accept(visitor)
+ visitor.visit(self)
+ end
+
+ def left
+ self.class.new(self[:left])
+ end
+
+ def right
+ self.class.new(self[:right])
+ end
+
+ def inspect
+ @hash.inspect
+ end
+
+ private
+
+ def [](key)
+ @hash[key]
+ end
+ end
+ end
+ end
+ end
+end
lib/scim/kit/v2/filter/visitor.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+require 'parslet'
+
+module Scim
+ module Kit
+ module V2
+ class Filter
+ # @private
+ class Visitor
+ VISITORS = {
+ and: :visit_and,
+ co: :visit_contains,
+ eq: :visit_equals,
+ ew: :visit_ends_with,
+ ge: :visit_greater_than_equals,
+ gt: :visit_greater_than,
+ le: :visit_less_than_equals,
+ lt: :visit_less_than,
+ ne: :visit_not_equals,
+ or: :visit_or,
+ pr: :visit_presence,
+ sw: :visit_starts_with
+ }.freeze
+
+ def visit(node)
+ visitor_for(node).call(node)
+ end
+
+ protected
+
+ def visitor_for(node)
+ method(VISITORS.fetch(node.operator, :visit_unknown))
+ end
+
+ def visit_and(node)
+ visit(node.left).merge(visit(node.right))
+ raise error_for(:visit_and)
+ end
+
+ def visit_or(_node)
+ raise error_for(:visit_or)
+ end
+
+ def visit_equals(_node)
+ raise error_for(:visit_equals)
+ end
+
+ def visit_not_equals(_node)
+ raise error_for(:visit_not_equals)
+ end
+
+ def visit_contains(_node)
+ raise error_for(:visit_contains)
+ end
+
+ def visit_starts_with(_node)
+ raise error_for(:visit_starts_with)
+ end
+
+ def visit_ends_with(_node)
+ raise error_for(:visit_ends_with)
+ end
+
+ def visit_greater_than(_node)
+ raise error_for(:visit_greater_than)
+ end
+
+ def visit_greater_than_equals(_node)
+ raise error_for(:visit_greater_than_equals)
+ end
+
+ def visit_less_than(_node)
+ raise error_for(:visit_less_than)
+ end
+
+ def visit_less_than_equals(_node)
+ raise error_for(:visit_less_than_equals)
+ end
+
+ def visit_presence(_node)
+ raise error_for(:visit_presence)
+ end
+
+ def visit_unknown(_node)
+ raise error_for(:visit_unknown)
+ end
+
+ private
+
+ def error_for(method)
+ ::Scim::Kit::NotImplementedError.new("#{method} is not implemented")
+ end
+ end
+ end
+ end
+ end
+end
lib/scim/kit/v2/filter.rb
@@ -125,6 +125,12 @@ module Scim
rule(:underscore) { str('_') }
rule(:version) { digit >> dot >> digit }
rule(:assign) { str('=') }
+
+ class << self
+ def parse(filter)
+ Node.new(new.parse(filter))
+ end
+ end
end
end
end
lib/scim/kit/v2.rb
@@ -12,6 +12,8 @@ require 'scim/kit/v2/mutability'
require 'scim/kit/v2/resource'
require 'scim/kit/v2/error'
require 'scim/kit/v2/filter'
+require 'scim/kit/v2/filter/node'
+require 'scim/kit/v2/filter/visitor'
require 'scim/kit/v2/resource_type'
require 'scim/kit/v2/returned'
require 'scim/kit/v2/schema'
lib/scim/kit/version.rb
@@ -2,6 +2,6 @@
module Scim
module Kit
- VERSION = '0.4.0'
+ VERSION = '0.5.0'
end
end
lib/scim/kit.rb
@@ -20,6 +20,7 @@ module Scim
module Kit
class Error < StandardError; end
class UnknownAttributeError < Error; end
+ class NotImplementedError < Error; end
TYPE_ERROR = ArgumentError.new(:type)
def self.logger
spec/scim/kit/v2/filter_spec.rb
@@ -92,12 +92,12 @@ RSpec.describe Scim::Kit::V2::Filter do
specify { expect(subject.attribute_path.parse_with_debug(x)).to be_truthy }
end
- [
- 'userName',
- 'user_name',
- 'user-name',
- 'username1',
- 'schemas'
+ %w[
+ userName
+ user_name
+ user-name
+ username1
+ schemas
].each do |x|
specify { expect(subject.attribute_name).to parse(x) }
end
@@ -114,6 +114,7 @@ RSpec.describe Scim::Kit::V2::Filter do
specify { expect(subject.falsey).to parse('false') }
specify { expect(subject.truthy).to parse('true') }
specify { expect(subject.null).to parse('null') }
+
1.upto(100).each { |n| specify { expect(subject.number).to parse(n.to_s) } }
[
@@ -166,9 +167,51 @@ RSpec.describe Scim::Kit::V2::Filter do
specify { expect(subject.hyphen).to parse('-') }
specify { expect(subject.underscore).to parse('_') }
+
(0..9).each { |x| specify { expect(subject.digit).to parse(x.to_s) } }
[*'a'..'z', *'A'..'Z'].each { |x| specify { expect(subject.alpha).to parse(x) } }
specify { expect(subject.colon).to parse(':') }
specify { expect(subject.version).to parse('2.0') }
specify { expect(subject.version).to parse('1.0') }
+
+ describe '.parse' do
+ subject { described_class }
+
+ [
+ %(meta.lastModified ge "2011-05-13T04:42:34Z"),
+ %(meta.lastModified gt "2011-05-13T04:42:34Z"),
+ %(meta.lastModified le "2011-05-13T04:42:34Z"),
+ %(meta.lastModified lt "2011-05-13T04:42:34Z"),
+ %(name.familyName co "O'Malley"),
+ %(schemas eq "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"),
+ %(title pr and userType eq "Employee"),
+ %(title pr or userType eq "Intern"),
+ %(title pr),
+ %(urn:ietf:params:scim:schemas:core:2.0:User:userName sw "J"),
+ %(userName eq "bjensen"),
+ %(userName sw "J"),
+ %(userType eq "Employee" and (emails co "example.com" or emails.value co "example.org")),
+ %(userType eq "Employee" and (emails.type eq "work")),
+ %(userType ne "Employee" and not (emails co "example.com" or emails.value co "example.org")),
+ '(emails[(type eq "work") and (value co "@example.com")]) or (ims[(type eq "xmpp") and (value co "@foo.com")])',
+ '(title pr) and (userType eq "Employee")',
+ '(title pr) or (userType eq "Intern")',
+ '(userType eq "Employee") and (emails.type eq "work")',
+ '(userType eq "Employee") and (emails[(type eq "work") and (value co "@example.com")])',
+ 'title pr',
+ 'userName pr and not (userName eq "hello@example.com")'
+ ].each do |filter|
+ let(:visitor) { Scim::Kit::V2::Filter::Visitor.new }
+
+ specify { expect(subject.parse(filter)).to be_instance_of(Scim::Kit::V2::Filter::Node) }
+
+ specify do
+ node = subject.parse(filter)
+
+ expect do
+ node.accept(visitor)
+ end.to raise_error(Scim::Kit::NotImplementedError)
+ end
+ end
+ end
end
spec/scim/kit/v2/resource_spec.rb
@@ -566,6 +566,7 @@ RSpec.describe Scim::Kit::V2::Resource do
specify { expect(subject.name.family_name).to eql('Garrett') }
specify { expect(subject.emails[0][:value]).to eql(email) }
specify { expect(subject.emails[0][:primary]).to be(true) }
+
specify do
attributes = { schemas: schemas.map(&:id), unknown: 'unknown' }
expect do
@@ -575,7 +576,7 @@ RSpec.describe Scim::Kit::V2::Resource do
end
end
- describe Scim::Kit::V2::Messages::ERROR do
+ describe 'Errors' do
subject { described_class.new(schemas: schemas) }
let(:schemas) { [Scim::Kit::V2::Error.default_schema] }
.rubocop.yml
@@ -9,7 +9,7 @@ AllCops:
- 'vendor/**/*'
TargetRubyVersion: 2.5
-Layout/IndentFirstArrayElement:
+Layout/FirstArrayElementIndentation:
EnforcedStyle: consistent
Metrics/BlockLength:
.travis.yml
@@ -3,6 +3,7 @@ sudo: false
language: ruby
cache: bundler
rvm:
- - 2.5.5
- - 2.6.3
+ - 2.5.7
+ - 2.6.5
+ - 2.7.0
before_install: gem install bundler -v 1.17.1
CHANGELOG.md
@@ -1,4 +1,4 @@
-Version 0.4.0
+Version 0.5.0
# Changelog
All notable changes to this project will be documented in this file.
@@ -9,7 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
- nil
+## [0.5.0] - 2020-01-21
+### Added
+- Add API to traverse a SCIM filter AST
+
## [0.4.0] - 2019-06-15
+### Added
- add implementation of SCIM 2.0 filter parser. [RFC-7644](https://tools.ietf.org/html/rfc7644#section-3.4.2.2)
## [0.3.2] - 2019-02-23
@@ -39,7 +44,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- \_assign does not coerce values by default.
- errors are merged together instead of overwritten during attribute validation.
-[Unreleased]: https://github.com/mokhan/scim-kit/compare/v0.4.0...HEAD
+[Unreleased]: https://github.com/mokhan/scim-kit/compare/v0.5.0...HEAD
+[0.4.0]: https://github.com/mokhan/scim-kit/compare/v0.4.0...v0.5.0
[0.4.0]: https://github.com/mokhan/scim-kit/compare/v0.3.2...v0.4.0
[0.3.2]: https://github.com/mokhan/scim-kit/compare/v0.3.1...v0.3.2
[0.3.1]: https://github.com/mokhan/scim-kit/compare/v0.3.0...v0.3.1
Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
- scim-kit (0.4.0)
+ scim-kit (0.5.0)
activemodel (>= 5.2.0)
net-hippie (~> 0.2)
parslet (~> 1.8)
@@ -11,77 +11,79 @@ PATH
GEM
remote: https://rubygems.org/
specs:
- activemodel (5.2.3)
- activesupport (= 5.2.3)
- activesupport (5.2.3)
+ activemodel (6.0.2.1)
+ activesupport (= 6.0.2.1)
+ activesupport (6.0.2.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
- addressable (2.6.0)
- public_suffix (>= 2.0.2, < 4.0)
+ zeitwerk (~> 2.2)
+ addressable (2.7.0)
+ public_suffix (>= 2.0.2, < 5.0)
ast (2.4.0)
bundler-audit (0.6.1)
bundler (>= 1.2.0, < 3)
thor (~> 0.18)
- byebug (11.0.1)
+ byebug (11.1.0)
concurrent-ruby (1.1.5)
crack (0.4.3)
safe_yaml (~> 1.0.0)
diff-lcs (1.3)
- ffaker (2.11.0)
- hashdiff (0.4.0)
- i18n (1.6.0)
+ ffaker (2.13.0)
+ hashdiff (1.0.0)
+ i18n (1.8.2)
concurrent-ruby (~> 1.0)
- jaro_winkler (1.5.2)
+ jaro_winkler (1.5.4)
jbuilder (2.9.1)
activesupport (>= 4.2.0)
- minitest (5.11.3)
- net-hippie (0.2.6)
- parallel (1.17.0)
- parser (2.6.3.0)
+ minitest (5.14.0)
+ net-hippie (0.3.1)
+ parallel (1.19.1)
+ parser (2.7.0.2)
ast (~> 2.4.0)
parslet (1.8.2)
- public_suffix (3.1.0)
+ public_suffix (4.0.3)
rainbow (3.0.0)
- rake (10.5.0)
- rspec (3.8.0)
- rspec-core (~> 3.8.0)
- rspec-expectations (~> 3.8.0)
- rspec-mocks (~> 3.8.0)
- rspec-core (3.8.1)
- rspec-support (~> 3.8.0)
- rspec-expectations (3.8.4)
+ rake (13.0.1)
+ rspec (3.9.0)
+ rspec-core (~> 3.9.0)
+ rspec-expectations (~> 3.9.0)
+ rspec-mocks (~> 3.9.0)
+ rspec-core (3.9.1)
+ rspec-support (~> 3.9.1)
+ rspec-expectations (3.9.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.8.0)
- rspec-mocks (3.8.1)
+ rspec-support (~> 3.9.0)
+ rspec-mocks (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.8.0)
- rspec-support (3.8.2)
- rubocop (0.71.0)
+ rspec-support (~> 3.9.0)
+ rspec-support (3.9.2)
+ rubocop (0.79.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
- parser (>= 2.6)
+ parser (>= 2.7.0.1)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7)
- rubocop-rspec (1.33.0)
- rubocop (>= 0.60.0)
+ rubocop-rspec (1.37.1)
+ rubocop (>= 0.68.1)
ruby-progressbar (1.10.1)
safe_yaml (1.0.5)
thor (0.20.3)
thread_safe (0.3.6)
- tilt (2.0.9)
+ tilt (2.0.10)
tilt-jbuilder (0.7.1)
jbuilder
tilt (>= 1.3.0, < 3)
- tzinfo (1.2.5)
+ tzinfo (1.2.6)
thread_safe (~> 0.1)
- unicode-display_width (1.6.0)
- webmock (3.6.0)
+ unicode-display_width (1.6.1)
+ webmock (3.8.0)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
+ zeitwerk (2.2.2)
PLATFORMS
ruby
@@ -91,7 +93,7 @@ DEPENDENCIES
bundler-audit (~> 0.6)
byebug
ffaker (~> 2.7)
- rake (~> 10.0)
+ rake (~> 13.0)
rspec (~> 3.0)
rubocop (~> 0.52)
rubocop-rspec (~> 1.22)
scim-kit.gemspec
@@ -39,7 +39,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'bundler-audit', '~> 0.6'
spec.add_development_dependency 'byebug'
spec.add_development_dependency 'ffaker', '~> 2.7'
- spec.add_development_dependency 'rake', '~> 10.0'
+ spec.add_development_dependency 'rake', '~> 13.0'
spec.add_development_dependency 'rspec', '~> 3.0'
spec.add_development_dependency 'rubocop', '~> 0.52'
spec.add_development_dependency 'rubocop-rspec', '~> 1.22'