Commit 14b24b7
Changed files (3)
lib
scim
kit
v2
spec
scim
kit
lib/scim/kit/v2/parser.rb
@@ -37,8 +37,42 @@ subAttr = "." ATTRNAME
; a sub-attribute of a complex attribute
=end
class Parser < Parslet::Parser
+ root :filter
+ rule(:filter) { (attrExp | logExp | valuePath) | (lparen >> filter >> rparen) }
+ rule(:valuePath) { attrPath >> lsquare_bracket >> valFilter >> rsquare_bracket }
+ rule(:valFilter) { logExp | attrExp | (not_op? >> lparen >> valFilter >> rparen) }
+ rule(:attrExp) { (attrPath >> space >> presence) | (attrPath >> space >> compareOp >> space >> quote >> compValue >> quote) }
+ rule(:logExp) { filter >> space >> (and_op | or_op) >> space >> filter }
+ rule(:compValue) { falsey | null | truthy | number | string }
+ rule(:compareOp) { equal | not_equal | contains | starts_with | ends_with | greater_than | less_than | less_than_equals | greater_than_equals }
+ rule(:attrPath) { scim_schema_uri | attrname >> subAttr.maybe }
+ rule(:attrname) { nameChar.repeat(1) }
+ rule(:nameChar) { hyphen | underscore | digit | alpha }
+ rule(:subAttr) { dot >> attrname }
+ rule(:presence) { str('pr') }
+ rule(:and_op) { str('and') }
+ rule(:or_op) { str('or') }
+ rule(:not_op) { str('not') }
+ rule(:not_op?) { not_op.maybe }
+ rule(:falsey) { str('false') }
+ rule(:truthy) { str('true') }
+ rule(:null) { str('null') }
+ rule(:number) { digit.repeat(1) }
+ rule(:scim_schema_uri) { (alpha | digit | dot | colon).repeat(1) }
+ rule(:equal) { str("eq") }
+ rule(:not_equal) { str("ne") }
+ rule(:contains) { str("co") }
+ rule(:starts_with) { str("sw") }
+ rule(:ends_with) { str("ew") }
+ rule(:greater_than) { str("gt") }
+ rule(:less_than) { str("lt") }
+ rule(:greater_than_equals) { str("ge") }
+ rule(:less_than_equals) { str("le") }
+ rule(:string) { (alpha | single_quote).repeat(1) }
rule(:lparen) { str('(') >> space? }
rule(:rparen) { str(')') >> space? }
+ rule(:lsquare_bracket) { str('[') >> space? }
+ rule(:rsquare_bracket) { str(']') >> space? }
rule(:digit) { match(/\d/) }
rule(:quote) { str('"') }
rule(:single_quote) { str("'") }
@@ -48,36 +82,7 @@ subAttr = "." ATTRNAME
rule(:dot) { str('.') }
rule(:colon) { str(':') }
rule(:hyphen) { str('-') }
-
- rule(:attribute) { uri | attribute_name }
- rule(:attribute_name) { alpha.repeat(1) | dot }
- rule(:uri) { (alpha | digit | dot | colon).repeat(1) }
- rule(:date) { (alpha | digit | dot | colon | hyphen).repeat(1) }
- rule(:string) { (alpha | single_quote).repeat(1) }
-
- rule(:operator) { equal | not_equal | contains | starts_with | ends_with | greater_than | less_than | less_than_equals | greater_than_equals }
- rule(:equal) { str("eq") }
- rule(:not_equal) { str("ne") }
- rule(:contains) { str("co") }
- rule(:starts_with) { str("sw") }
- rule(:ends_with) { str("ew") }
- rule(:greater_than) { str("gt") }
- rule(:less_than) { str("lt") }
- rule(:greater_than_equals) { str("ge") }
- rule(:less_than_equals) { str("le") }
-
- rule(:quoted_value) { quote >> value.as(:right) >> quote }
- rule(:value) { (string | date | uri).repeat(1) }
-
- rule(:filter) { attribute.as(:left) >> space >> operator.as(:operator) >> space >> quoted_value }
- root :filter
-
- def pretty_parse(*args)
- puts *args.inspect
- parse(*args)
- rescue Parslet::ParseFailed => error
- puts error.parse_failure_cause.ascii_tree
- end
+ rule(:underscore) { str('_') }
end
end
end
spec/scim/kit/v2/parser_spec.rb
@@ -28,53 +28,102 @@ RSpec.describe Scim::Kit::V2::Parser do
"2011-05-13T04:42:34Z",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User",
].each do |value|
- context "#{attribute} #{operator} #{value}" do
- let(:result) { subject.pretty_parse(%Q(#{attribute} #{operator} \"#{value}\")) }
-
- specify { expect(result).to be_present }
- specify { expect(result[:left].to_s).to eql(attribute) }
- specify { expect(result[:operator].to_s).to eql(operator) }
- specify { expect(result[:right].to_s).to eql(value) }
- end
+ specify { expect(subject).to parse(%Q(#{attribute} #{operator} \"#{value}\")) }
end
end
end
- context "match uri" do
- specify { expect(subject.uri.parse("urn:ietf:params:scim:schemas:core:2.0:User:userName")).to be_present }
- end
-
- context "title pr and userType eq \"Employee\"" do
- let(:result) { subject.pretty_parse(%Q(title pr and userType eq "Employee")) }
+ specify { expect(subject).to parse(%Q(title pr and userType eq "Employee")) }
- specify { expect(result).to be_present }
- specify { expect(result[:left].to_s).to eql(attribute) }
- specify { expect(result[:operator].to_s).to eql(operator) }
- specify { expect(result[:right].to_s).to eql(value) }
+ [
+ 'emails[type eq "work" and value co "@example.com"]',
+ ].each do |x|
+ specify { expect(subject.valuePath).to parse(x) }
end
-end
+ [
+ 'firstName eq "Tsuyoshi"',
+ 'firstName pr',
+ 'firstName eq "Tsuyoshi" and lastName eq "Garret"'
+ ].each do |x|
+ specify { expect(subject.valFilter).to parse(x) }
+ end
-=begin
-filter=title pr
+ [
+ 'firstName eq "Tsuyoshi"',
+ 'firstName pr',
+ ].each do |x|
+ specify { expect(subject.attrExp).to parse(x) }
+ end
-filter=title pr and userType eq "Employee"
+ specify { expect(subject.attrExp).not_to parse('firstName eq "Tsuyoshi" and lastName eq "Garret"') }
-filter=title pr or userType eq "Intern"
+ [
+ 'firstName eq "Tsuyoshi" and lastName eq "Garret"'
+ ].each do |x|
+ specify { expect(subject.logExp).to parse(x) }
+ end
-filter=
+ ['false', 'null', 'true', '1', 'hello'].each do |x|
+ specify { expect(subject.compValue).to parse(x) }
+ end
-filter=userType eq "Employee" and (emails co "example.com" or
- emails.value co "example.org")
+ ['eq', 'ne', 'co', 'sw', 'ew', 'gt', 'lt', 'ge', 'le'].each do |x|
+ specify { expect(subject.compareOp).to parse(x) }
+ end
-filter=userType ne "Employee" and not (emails co "example.com" or
- emails.value co "example.org")
+ [
+ 'userName',
+ 'user_name',
+ 'user-name',
+ 'username1',
+ 'name.familyName',
+ 'urn:ietf:params:scim:schemas:core:2.0:User:userName',
+ 'urn:ietf:params:scim:schemas:core:2.0:User:name.familyName',
+ 'meta.lastModified',
+ 'schemas',
+ ].each do |x|
+ specify { expect(subject.attrPath).to parse(x) }
+ end
-filter=userType eq "Employee" and (emails.type eq "work")
+ [
+ 'userName',
+ 'user_name',
+ 'user-name',
+ 'username1',
+ 'schemas',
+ ].each do |x|
+ specify { expect(subject.attrname).to parse(x) }
+ end
+ ['-', '_', '0', 'a'].each { |x| specify { expect(subject.nameChar).to parse(x) } }
+ specify { expect(subject.subAttr).to parse('.name') }
+ specify { expect(subject.presence).to parse('pr') }
+ specify { expect(subject.and_op).to parse('and') }
+ specify { expect(subject.or_op).to parse('or') }
+ specify { expect(subject.not_op).to parse('not') }
+ 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) } }
-filter=userType eq "Employee" and emails[type eq "work" and
- value co "@example.com"]
+ [
+ 'urn:ietf:params:scim:schemas:core:2.0:User:userName',
+ 'urn:ietf:params:scim:schemas:core:2.0:User:name.familyName',
+ ].each do |x|
+ specify { expect(subject.scim_schema_uri).to parse(x) }
+ end
-filter=emails[type eq "work" and value co "@example.com"] or
- ims[type eq "xmpp" and value co "@foo.com"]
-=end
+ [
+ 'title pr',
+ 'title pr and userType eq "Employee"',
+ 'title pr or userType eq "Intern"',
+ '',
+ 'userType eq "Employee" and (emails co "example.com" or emails.value co "example.org")',
+ 'userType ne "Employee" and not (emails co "example.com" or emails.value co "example.org")',
+ 'userType eq "Employee" and (emails.type eq "work") ',
+ 'userType eq "Employee" and emails[type eq "work" and value co "@example.com"]',
+ 'emails[type eq "work" and value co "@example.com"] or ims[type eq "xmpp" and value co "@foo.com"]',
+ ].each do |x|
+ specify { expect(subject).to parse(x) }
+ end
+end
spec/spec_helper.rb
@@ -5,6 +5,8 @@ require 'scim/kit'
require 'ffaker'
require 'json'
require 'byebug'
+require 'parslet/convenience'
+require 'parslet/rig/rspec'
require 'webmock/rspec'
Scim::Kit.logger = Logger.new('/dev/null')