dav4rack_ext 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -13,7 +13,7 @@ group(:example) do
13
13
  end
14
14
 
15
15
  group(:test) do
16
- gem 'eetee', '~> 0.0.1'
16
+ gem 'eetee', '~> 0.0.3'
17
17
  gem 'mocha', '~> 0.12.0'
18
18
  gem 'factory_girl', '~> 4.0'
19
19
  gem 'virtus'
data/Rakefile CHANGED
@@ -13,7 +13,8 @@ task :test do
13
13
  SimpleCov.command_name "E.T."
14
14
  SimpleCov.start do
15
15
  add_filter ".*_spec"
16
- add_filter "/helpers/"
16
+ add_filter "/specs/helpers/"
17
+ add_filter "/example/"
17
18
  end
18
19
  end
19
20
 
@@ -85,6 +85,21 @@ module DAV4Rack
85
85
  # If the client has explicitly stated they want a new contact
86
86
  raise Conflict if (want_new_contact and @contact)
87
87
 
88
+ if_match = request.env['HTTP_IF_MATCH']
89
+ if if_match
90
+ # client wants to update a contact, return an error if no
91
+ # contact was found
92
+ if (if_match == '*') || !@contact
93
+ raise PreconditionFailed unless @contact
94
+
95
+ # client wants to update the contact with specific etag,
96
+ # return an error if the contact was updated by someone else
97
+ elsif (if_match != @contact.etag)
98
+ raise PreconditionFailed
99
+
100
+ end
101
+ end
102
+
88
103
  if @contact
89
104
  Logger.debug("Updating contact #{uid} (#{@contact.object_id})")
90
105
  else
@@ -95,7 +110,10 @@ module DAV4Rack
95
110
  @contact.update_from_vcard(vcf)
96
111
 
97
112
  if @contact.save
98
- @public_path = File.join(@address_book.path, @contact.uid)
113
+ new_public = @public_path.split('/')[0...-1]
114
+ new_public << @contact.uid.to_s
115
+
116
+ @public_path = new_public.join('/')
99
117
  response['ETag'] = @contact.etag
100
118
  Created
101
119
  else
@@ -1,3 +1,3 @@
1
1
  module Dav4rackExt
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -0,0 +1,63 @@
1
+ module HTTPDAVTest
2
+ def propfind(url, properties = :all, opts = {})
3
+ namespaces = {
4
+ 'DAV:' => 'D',
5
+ 'urn:ietf:params:xml:ns:carddav' => 'C',
6
+ 'http://calendarserver.org/ns/' => 'APPLE1'
7
+ }
8
+
9
+ if properties == :all
10
+ body = "<D:allprop />"
11
+
12
+ else
13
+ properties = properties.map do |(name, ns)|
14
+ ns_short = namespaces[ns]
15
+ raise "unknown namespace: #{ns}" unless ns_short
16
+ %.<#{ns_short}:#{name}/>.
17
+ end
18
+
19
+ body = "<D:prop>#{properties.join("\n")}</D:prop>"
20
+ end
21
+
22
+
23
+ data = <<-EOS
24
+ <?xml version="1.0" encoding="UTF-8"?>
25
+ <D:propfind xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:carddav" xmlns:APPLE1="http://calendarserver.org/ns/">
26
+ #{body}
27
+ </D:propfind>
28
+ EOS
29
+
30
+ request('PROPFIND', url, opts.merge(input: data))
31
+ end
32
+
33
+ def ensure_element_exists(response, expr, namespaces = {'D' => 'DAV:'})
34
+ ret = Nokogiri::XML(response.body)
35
+ ret.css(expr, namespaces).tap{|elements| elements.should.not.be.empty? }
36
+ rescue EEtee::AssertionFailed => err
37
+ raise EEtee::AssertionFailed.new("XML did not match: #{expr}")
38
+ end
39
+
40
+ def ensure_element_does_not_exists(response, expr, namespaces = {})
41
+ ret = Nokogiri::XML(response.body)
42
+ ret.css(expr, namespaces).should.be.empty?
43
+ rescue EEtee::AssertionFailed => err
44
+ raise EEtee::AssertionFailed.new("XML did match: #{expr}")
45
+ end
46
+
47
+ def element_content(response, expr, namespaces = {})
48
+ ret = Nokogiri::XML(response.body)
49
+ elements = ret.css(expr, namespaces)
50
+ if elements.empty?
51
+ :missing
52
+ else
53
+ children = elements.first.element_children
54
+ if children.empty?
55
+ :empty
56
+ else
57
+ children.first.text
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ EEtee::Context.__send__(:include, HTTPDAVTest)
data/specs/spec_helper.rb CHANGED
@@ -3,16 +3,6 @@ require 'bundler/setup'
3
3
 
4
4
  require 'eetee'
5
5
 
6
- if ENV['COVERAGE']
7
-
8
- require 'simplecov'
9
- SimpleCov.start do
10
- add_filter ".*_spec"
11
- add_filter "/helpers/"
12
- end
13
-
14
- end
15
-
16
6
  $LOAD_PATH.unshift( File.expand_path('../../lib' , __FILE__) )
17
7
  require 'dav4rack_ext'
18
8
  require 'factory_girl'
@@ -21,6 +11,7 @@ require 'factory_girl'
21
11
  require 'eetee/ext/mocha'
22
12
  require 'eetee/ext/rack'
23
13
 
14
+ require_relative 'helpers/http_dav'
24
15
 
25
16
  require_relative '../example/rack_sniffer'
26
17
  require_relative 'support/models'
@@ -93,6 +93,10 @@ module Testing
93
93
  contacts.detect{|c| c.uid == uid.to_s }
94
94
  end
95
95
 
96
+ def ctag
97
+ updated_at
98
+ end
99
+
96
100
  def create_contact(uid)
97
101
  Contact.new(uid: uid).tap do |c|
98
102
  contacts << c
@@ -0,0 +1,96 @@
1
+ require File.expand_path('../../../../spec_helper', __FILE__)
2
+
3
+ describe 'Contact Resource' do
4
+ before do
5
+ @dav_ns = "DAV:"
6
+ @carddav_ns = "urn:ietf:params:xml:ns:carddav"
7
+
8
+ @contact = contact = FactoryGirl.build(:contact, uid: '1234-5678-9000-1')
9
+
10
+
11
+ user_builder = proc do |env|
12
+ FactoryGirl.build(:user, env: env, login: 'john', addressbooks: [
13
+ FactoryGirl.build(:book, path: 'castor', name: "A book", contacts: [contact])
14
+ ])
15
+ end
16
+
17
+ app = Rack::Builder.new do
18
+ # use XMLSniffer
19
+ run DAV4Rack::Carddav.app('/', current_user: user_builder)
20
+ end
21
+
22
+ serve_app(app)
23
+ end
24
+
25
+ should 'update contact and return correct location', :force => true do
26
+ # the url does not need to match the UID
27
+ response = request(:put, '/books/castor/crap',
28
+ input: @contact.vcard.to_s
29
+ )
30
+
31
+ response.status.should == 201
32
+ response.headers['Location'].should == 'http://example.org:80/books/castor/1234-5678-9000-1'
33
+ end
34
+
35
+ should 'return an error if If-Match do not match (rfc2068 14.25)' do
36
+ Testing::Contact.any_instance.expects(:etag).returns("ETAG")
37
+
38
+ headers = {
39
+ 'HTTP_IF_MATCH' => 'ETAG2'
40
+ }
41
+
42
+ # the url does not need to match the UID
43
+ response = request(:put, '/books/castor/1234-5678-9000-1',
44
+ headers.merge(input: @contact.vcard.to_s)
45
+ )
46
+
47
+ response.status.should == 412
48
+ end
49
+
50
+ should 'return an error with If-Match and no contact (rfc2068 14.25)' do
51
+ headers = {
52
+ 'HTTP_IF_MATCH' => 'ETAG2'
53
+ }
54
+
55
+ c = @contact.dup
56
+ c.uid = '55TT'
57
+
58
+ # the url does not need to match the UID
59
+ response = request(:put, '/books/castor/CRAP',
60
+ headers.merge(input: c.vcard.to_s)
61
+ )
62
+
63
+ response.status.should == 412
64
+ end
65
+
66
+
67
+ should 'update contact if If-Match="*" and contact was found (rfc2068 14.25)' do
68
+ headers = {
69
+ 'HTTP_IF_MATCH' => '*'
70
+ }
71
+
72
+ # the url does not need to match the UID
73
+ response = request(:put, '/books/castor/1234-5678-9000-1',
74
+ headers.merge(input: @contact.vcard.to_s)
75
+ )
76
+
77
+ response.status.should == 201
78
+ end
79
+
80
+ should 'not update contact if If-Match="*" and no contact found (rfc2068 14.25)' do
81
+ headers = {
82
+ 'HTTP_IF_MATCH' => '*'
83
+ }
84
+
85
+ c = @contact.dup
86
+ c.uid = '55TT'
87
+
88
+ # the url does not need to match the UID
89
+ response = request(:put, '/books/castor/undefined',
90
+ headers.merge(input: c.vcard.to_s)
91
+ )
92
+
93
+ response.status.should == 412
94
+ end
95
+
96
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dav4rack_ext
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-06 00:00:00.000000000 Z
12
+ date: 2012-12-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: dav4rack
@@ -91,6 +91,7 @@ files:
91
91
  - lib/dav4rack_ext/helpers/properties.rb
92
92
  - lib/dav4rack_ext/version.rb
93
93
  - specs/factories.rb
94
+ - specs/helpers/http_dav.rb
94
95
  - specs/rfc/rfc3744_spec.rb
95
96
  - specs/rfc/rfc5397_spec.rb
96
97
  - specs/rfc/rfc6352_spec.rb
@@ -98,6 +99,7 @@ files:
98
99
  - specs/support/models.rb
99
100
  - specs/unit/carddav/app_spec.rb
100
101
  - specs/unit/carddav/resources/addressbook_collection_resource_spec.rb
102
+ - specs/unit/carddav/resources/contact_resource_spec.rb
101
103
  - specs/unit/carddav/resources/principal_resource_spec.rb
102
104
  - specs/unit/helpers/properties_spec.rb
103
105
  homepage: ''
@@ -114,7 +116,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
114
116
  version: '0'
115
117
  segments:
116
118
  - 0
117
- hash: 1991080700414274761
119
+ hash: 2833907520245342566
118
120
  required_rubygems_version: !ruby/object:Gem::Requirement
119
121
  none: false
120
122
  requirements:
@@ -123,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
125
  version: '0'
124
126
  segments:
125
127
  - 0
126
- hash: 1991080700414274761
128
+ hash: 2833907520245342566
127
129
  requirements: []
128
130
  rubyforge_project:
129
131
  rubygems_version: 1.8.23