acts_as_sdata 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/MIT-LICENSE +20 -0
- data/README.textile +200 -0
- data/Rakefile +20 -0
- data/VERSION +1 -0
- data/config/sdata.yml +13 -0
- data/config/sdata.yml.example +20 -0
- data/config/sdata.yml.tmpl.staging +14 -0
- data/generators/acts_as_sdata/acts_as_sdata_generator.rb +9 -0
- data/generators/acts_as_sdata/templates/migration.rb +69 -0
- data/init.rb +36 -0
- data/lib/s_data/active_record_extensions/base.rb +7 -0
- data/lib/s_data/active_record_extensions/mixin.rb +157 -0
- data/lib/s_data/active_record_extensions/sdata_uuid_mixin.rb +133 -0
- data/lib/s_data/atom_extensions/content_mixin.rb +14 -0
- data/lib/s_data/atom_extensions/entry_mixin.rb +41 -0
- data/lib/s_data/atom_extensions/nodes/digest.rb +48 -0
- data/lib/s_data/atom_extensions/nodes/payload.rb +34 -0
- data/lib/s_data/atom_extensions/nodes/sync_state.rb +14 -0
- data/lib/s_data/conditions_builder.rb +59 -0
- data/lib/s_data/controller_mixin.rb +11 -0
- data/lib/s_data/controller_mixin/actions.rb +87 -0
- data/lib/s_data/controller_mixin/collection_scope.rb +57 -0
- data/lib/s_data/controller_mixin/s_data_feed.rb +87 -0
- data/lib/s_data/controller_mixin/s_data_instance.rb +35 -0
- data/lib/s_data/diagnosis/application_controller_mixin.rb +16 -0
- data/lib/s_data/diagnosis/diagnosis.rb +130 -0
- data/lib/s_data/diagnosis/diagnosis_mapper.rb +39 -0
- data/lib/s_data/exceptions.rb +10 -0
- data/lib/s_data/formatting.rb +13 -0
- data/lib/s_data/namespace_definitions.rb +19 -0
- data/lib/s_data/payload.rb +158 -0
- data/lib/s_data/payload_map.rb +0 -0
- data/lib/s_data/payload_map/payload_map.rb +136 -0
- data/lib/s_data/payload_map/payload_map_hash.rb +39 -0
- data/lib/s_data/predicate.rb +31 -0
- data/lib/s_data/route_mapper.rb +143 -0
- data/lib/s_data/router_mixin.rb +10 -0
- data/lib/s_data/sync/controller_mixin.rb +122 -0
- data/lib/s_data/sync/sdata_syncing_mixin.rb +17 -0
- data/lib/s_data/virtual_base.rb +114 -0
- data/test/functional/Rakefile +0 -0
- data/test/unit/active_record_mixin/active_record_mixin_spec.rb +20 -0
- data/test/unit/active_record_mixin/acts_as_sdata_spec.rb +41 -0
- data/test/unit/active_record_mixin/find_by_sdata_instance_id_spec.rb +34 -0
- data/test/unit/active_record_mixin/payload_spec.rb +622 -0
- data/test/unit/active_record_mixin/to_atom_spec.rb +85 -0
- data/test/unit/atom_entry_mixin/atom_entry_mixin_spec.rb +11 -0
- data/test/unit/atom_entry_mixin/to_attributes_spec.rb +30 -0
- data/test/unit/class_stubs/address.rb +19 -0
- data/test/unit/class_stubs/contact.rb +25 -0
- data/test/unit/class_stubs/customer.rb +70 -0
- data/test/unit/class_stubs/model_base.rb +17 -0
- data/test/unit/class_stubs/payload.rb +15 -0
- data/test/unit/class_stubs/sd_uuid.rb +28 -0
- data/test/unit/class_stubs/user.rb +40 -0
- data/test/unit/conditions_builder_spec.rb +54 -0
- data/test/unit/controller_mixin/acts_as_sdata_spec.rb +29 -0
- data/test/unit/controller_mixin/build_sdata_feed_spec.rb +50 -0
- data/test/unit/controller_mixin/controller_mixin_spec.rb +22 -0
- data/test/unit/controller_mixin/diagnosis_spec.rb +232 -0
- data/test/unit/controller_mixin/sdata_collection_spec.rb +78 -0
- data/test/unit/controller_mixin/sdata_create_instance_spec.rb +173 -0
- data/test/unit/controller_mixin/sdata_opensearch_and_links_spec.rb +382 -0
- data/test/unit/controller_mixin/sdata_scope/linked_model_spec.rb +58 -0
- data/test/unit/controller_mixin/sdata_scope/non_linked_model_spec.rb +66 -0
- data/test/unit/controller_mixin/sdata_scope/scoping_in_config_spec.rb +64 -0
- data/test/unit/controller_mixin/sdata_show_instance_spec.rb +98 -0
- data/test/unit/controller_mixin/sdata_update_instance_spec.rb +65 -0
- data/test/unit/payload_map/payload_map_hash_spec.rb +84 -0
- data/test/unit/payload_map/payload_map_spec.rb +144 -0
- data/test/unit/predicate_spec.rb +59 -0
- data/test/unit/router_mixin/routes_spec.rb +138 -0
- data/test/unit/spec.opts +4 -0
- data/test/unit/spec_helper.rb +47 -0
- data/test/unit/spec_helpers/nokogiri_extensions.rb +16 -0
- data/test/unit/sync_controller_mixin/controller_mixin_spec.rb +22 -0
- data/test/unit/sync_controller_mixin/sdata_collection_sync_feed_spec.rb +69 -0
- metadata +175 -0
@@ -0,0 +1,133 @@
|
|
1
|
+
module SData
|
2
|
+
module ActiveRecordExtensions
|
3
|
+
module SdataUuidMixin
|
4
|
+
|
5
|
+
def acts_as_sdata_uuid
|
6
|
+
self.__send__ :extend, UuidClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module UuidClassMethods
|
10
|
+
|
11
|
+
|
12
|
+
def find_by_virtual_model_and_uuid(virtual_model, uuid)
|
13
|
+
sd_uuids = SData::SdUuid.find(:all, :conditions => {:sd_class => virtual_model.sdata_name, :bb_model_type => virtual_model.baze_class_name, :uuid => uuid})
|
14
|
+
|
15
|
+
sd_uuid = enforce_uniqueness(sd_uuids, virtual_model.sdata_name, virtual_model.baze_class_name)
|
16
|
+
|
17
|
+
raise "#{virtual_model.sdata_name} with UUID '#{uuid}' not found" unless sd_uuid
|
18
|
+
virtual_model.build_for(sd_uuid.bb_model)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Handling multiple uuids: when linking and the resource already had a uuid on both sides, one provider wins and
|
22
|
+
# stores it's uuid for the resource on the other provider (http://interop.sage.com/daisy/sdataSync/Link/525-DSY.html)
|
23
|
+
# At a later date sdata will provide an algorithm for uuid propagation; for the time being we assume the last stored
|
24
|
+
# is the correct one.
|
25
|
+
def find_for_virtual_instance(virtual_instance, baze=nil)
|
26
|
+
baze ||= virtual_instance.baze
|
27
|
+
SData::SdUuid.first(:conditions => {:sd_class => virtual_instance.sdata_name,
|
28
|
+
:bb_model_type => baze.class.name.demodulize,
|
29
|
+
:bb_model_id => baze.id},
|
30
|
+
:order => "updated_at DESC" )
|
31
|
+
end
|
32
|
+
|
33
|
+
def enforce_uniqueness(sd_uuids, sd_class, baze_class_name)
|
34
|
+
return nil if sd_uuids.nil? || sd_uuids.empty?
|
35
|
+
if sd_uuids.count > 1
|
36
|
+
RAILS_DEFAULT_LOGGER.fatal("SdUuid uniqueness violation for #{sd_class} - #{baze_class_name} - #{uuid}. Using first created")
|
37
|
+
sd_uuids.sort_by!{|sid| sid.updated_at}
|
38
|
+
end
|
39
|
+
sd_uuids.first
|
40
|
+
end
|
41
|
+
|
42
|
+
# This method is used to respond to a PUT to $linked('uuid'), to change what bb_model instance a uuid points to
|
43
|
+
# TODO this method makes no attempt to handle multiple baze_classes
|
44
|
+
# RADAR this method also has a RACE, but which should only happen when the provider/linking engine
|
45
|
+
# is doing something fubarred
|
46
|
+
def reassign_uuid!(uuid, virtual_model, new_id)
|
47
|
+
raise "Cannot edit uuid for virtual_model (#{virtual_model.name}) with no fixed baze_class." if virtual_model.baze_class.nil?
|
48
|
+
sd_uuids = SData::SdUuid.find(:all, :conditions => {:sd_class => virtual_model.sdata_name, :bb_model_type => virtual_model.baze_class_name, :uuid => uuid})
|
49
|
+
sd_uuid = enforce_uniqueness(sd_uuids, virtual_model.sdata_name, virtual_model.baze_class_name)
|
50
|
+
sd_uuid.update_attributes!({:bb_model_id => new_id})
|
51
|
+
end
|
52
|
+
|
53
|
+
# TODO: handle case where virtual model depends on multiple models whose updated_at matter.
|
54
|
+
# This method can change the uuid of a given underlying BB model. It CANNOT set a uuid for the tuple [sd_class,
|
55
|
+
# bb_model_type] for a given bb_model_id if an instance of the tuple [sd_class, bb_model_type] with a different
|
56
|
+
# bb_model_id exists. To swap which bb_model_id of a [sd_class, bb_model_type] a uuid points to, use reassign_uuid!.
|
57
|
+
# Move the delete case to a separate method, and the controller should determine whether the request is deleting
|
58
|
+
# or not (does spec actually say you can delete a uuid by POST ing empty uuid to it? don't you have to do a DELETE?).
|
59
|
+
# Because of unique index on [sd_class, bb_model, uuid] this has a race condition and can theoretically throw, but
|
60
|
+
# only if a linking engine is mistakenly sending multiple requests rapidly. The controller can just report a
|
61
|
+
# generic error.
|
62
|
+
def create_or_update_uuid_for(virtual_instance, uuid)
|
63
|
+
raise "Cannot create_or_update_uuid_for virtual_model (#{virtual_model.name}) with no fixed baze_class." if virtual_instance.class.baze_class.nil?
|
64
|
+
raise "virtual_instance #{virtual_instance.inspect} has no baze" unless virtual_instance.baze
|
65
|
+
return if uuid.blank?
|
66
|
+
|
67
|
+
sd_uuid = SData::SdUuid.find_for_virtual_instance(virtual_instance)
|
68
|
+
params = {:sd_class => virtual_instance.sdata_name,
|
69
|
+
:bb_model_type => virtual_instance.baze_class_name,
|
70
|
+
:bb_model_id => virtual_instance.baze.id,
|
71
|
+
:uuid => uuid}
|
72
|
+
if sd_uuid
|
73
|
+
sd_uuid.update_attributes!(params)
|
74
|
+
else
|
75
|
+
begin
|
76
|
+
SData::SdUuid.create(params)
|
77
|
+
rescue ::ActiveRecord::StatementInvalid
|
78
|
+
raise Sage::BusinessLogic::Exception::IncompatibleDataException, "UUID already exists for another resource"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# return all the bb_records which form the basis of this resource and which have been linked
|
84
|
+
# MJ TODO -- check if there is a way to use the bb_model polymorphic assoc to autmatically create this query
|
85
|
+
def linked_bb_records(endpoint=nil)
|
86
|
+
klasses = baze_classes || [self]
|
87
|
+
records = {}
|
88
|
+
klasses.each do |klass|
|
89
|
+
klassname = klass.name.demodulize
|
90
|
+
tablename = klassname.tableize
|
91
|
+
conditions = { :bb_model_type => klassname }
|
92
|
+
# conditions[:endpoint] = endpoint # we don't need this now
|
93
|
+
records[klassname.to_sym] = klass.all(:joins => "INNER JOIN sd_uuids ON sd_uuids.bb_model_id = #{tablename}.id",
|
94
|
+
:conditions => {:sd_uuids => conditions})
|
95
|
+
end
|
96
|
+
records
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
module SdataUuidableMixin
|
102
|
+
def has_sdata_uuid
|
103
|
+
self.__send__ :include, UuidableInstanceMethods
|
104
|
+
end
|
105
|
+
|
106
|
+
module UuidableInstanceMethods
|
107
|
+
|
108
|
+
def uuid
|
109
|
+
record = sd_uuid
|
110
|
+
record ? record.uuid : nil
|
111
|
+
end
|
112
|
+
|
113
|
+
# WARN: don't cache this, it will potentially break things
|
114
|
+
# RADAR: This finds the most recently updated of potentially many sd_uuids -- see
|
115
|
+
# http://interop.sage.com/daisy/sdataSync/Link/525-DSY.html, linking scenario 3
|
116
|
+
def sd_uuid
|
117
|
+
SData::SdUuid.find_for_virtual_instance(self)
|
118
|
+
end
|
119
|
+
|
120
|
+
def create_or_update_uuid!(value)
|
121
|
+
SData::SdUuid.create_or_update_uuid_for(self, value)
|
122
|
+
end
|
123
|
+
|
124
|
+
def linked?
|
125
|
+
!sd_uuid.nil?
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
::ActiveRecord::Base.extend SdataUuidMixin
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module SData
|
2
|
+
module AtomExtensions
|
3
|
+
module ContentMixin
|
4
|
+
class Diagnosis < Atom::Content::Base
|
5
|
+
attribute :type, :'xml:lang'
|
6
|
+
def to_xml(*params)
|
7
|
+
self[0] #magic done in diagnosis.rb
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
Atom::Content.__send__ :include, SData::AtomExtensions::ContentMixin
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# the following should stay together, to ensure that when adding custom nodes, the namespcaes
|
2
|
+
# they are in are available to ratom, or it will silently decide to make the node an Atom::Content
|
3
|
+
SData.config[:schemas].each do |prefix, namespace|
|
4
|
+
Atom::Feed.add_extension_namespace(prefix.to_s, namespace)
|
5
|
+
Atom::Entry.add_extension_namespace(prefix.to_s, namespace)
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
Atom::Entry.element "sdata:payload",
|
10
|
+
:class => SData::AtomExtensions::Nodes::Payload,
|
11
|
+
:namespace => SData.config[:schemas][:sdata]
|
12
|
+
|
13
|
+
#TODO the rest should be done like payload
|
14
|
+
Atom::Entry.element :diagnosis, :class => Atom::Content
|
15
|
+
|
16
|
+
Atom::Entry.element "sync:syncState",
|
17
|
+
:class => SData::AtomExtensions::Nodes::SyncState,
|
18
|
+
:namespace => SData.config[:schemas][:sync]
|
19
|
+
Atom::Feed.element "sync:digest",
|
20
|
+
:class => SData::AtomExtensions::Nodes::Digest,
|
21
|
+
:namespace => SData.config[:schemas][:sync]
|
22
|
+
|
23
|
+
module Atom
|
24
|
+
class Entry
|
25
|
+
def extended_element(element_with_namespace)
|
26
|
+
namespace, element = element_with_namespace.split(':')
|
27
|
+
self.simple_extensions.keys.each do |key|
|
28
|
+
return self.simple_extensions[key][0] if key == "{#{SData.config[:schemas][namespace]},#{element}}"
|
29
|
+
end
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_attributes
|
34
|
+
attributes = {}
|
35
|
+
self['http://sdata.sage.com/schemes/attributes'].each_pair do |name, values|
|
36
|
+
attributes[name] = values.first
|
37
|
+
end
|
38
|
+
attributes
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module SData
|
2
|
+
module AtomExtensions
|
3
|
+
module Nodes
|
4
|
+
|
5
|
+
class DigestEntry
|
6
|
+
include Atom::Xml::Parseable
|
7
|
+
add_extension_namespace "sdata", SData.config[:schemas][:sdata]
|
8
|
+
add_extension_namespace 'sync', SData.config[:schemas][:sync]
|
9
|
+
element "sync:endpoint", :namespace => SData.config[:schemas][:sync]
|
10
|
+
element "sync:tick", :namespace => SData.config[:schemas][:sync]
|
11
|
+
element "sync:stamp", :namespace => SData.config[:schemas][:sync]
|
12
|
+
element "sync:conflictPriority", :namespace => SData.config[:schemas][:sync]
|
13
|
+
|
14
|
+
def initialize(xml=nil)
|
15
|
+
if xml
|
16
|
+
# puts "DigestEntry call xml.read"
|
17
|
+
xml.read
|
18
|
+
parse(xml)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.parse(xml)
|
23
|
+
new(xml)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Digest
|
28
|
+
include Atom::Xml::Parseable
|
29
|
+
add_extension_namespace "sdata", SData.config[:schemas][:sdata]
|
30
|
+
add_extension_namespace 'sync', SData.config[:schemas][:sync]
|
31
|
+
elements "sync:digestEntry", :class => SData::AtomExtensions::Nodes::DigestEntry, :namespace => SData.config[:schemas][:sync]
|
32
|
+
uri_attribute "sdata:url"
|
33
|
+
element "sync:origin", :namespace => SData.config[:schemas][:sync]
|
34
|
+
|
35
|
+
def initialize(xml=nil)
|
36
|
+
xml.read
|
37
|
+
parse(xml)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.parse(xml)
|
41
|
+
new(xml)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
module SData
|
3
|
+
module AtomExtensions
|
4
|
+
module Nodes
|
5
|
+
class Payload
|
6
|
+
attr_accessor :raw_xml
|
7
|
+
include Atom::Xml::Parseable
|
8
|
+
add_extension_namespace "sdata", SData.config[:schemas][:sdata]
|
9
|
+
add_extension_namespace 'sync', SData.config[:schemas][:sync]
|
10
|
+
|
11
|
+
element 'sync:digest', :class => SData::AtomExtensions::Nodes::Digest, :namespace => SData.config[:schemas][:sync]
|
12
|
+
|
13
|
+
def initialize(xml=nil)
|
14
|
+
# temporary, until have time to add Linking atom extension
|
15
|
+
@raw_xml = xml.read_inner_xml
|
16
|
+
xml.read
|
17
|
+
parse(xml)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse(xml)
|
21
|
+
new(xml)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_xml(*params)
|
25
|
+
node = XML::Node.new("sdata:payload")
|
26
|
+
node << content
|
27
|
+
return node
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module SData
|
2
|
+
module AtomExtensions
|
3
|
+
module Nodes # the reason I didn't name this Atom is that I didn't feel like adding :: in a gawdjillion places ATM
|
4
|
+
class SyncState
|
5
|
+
def initialize(xml=nil)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.parse(xml)
|
9
|
+
# no need to parse sync states at this time
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module SData
|
2
|
+
class ConditionsBuilder
|
3
|
+
SQL_RELATIONS_MAP = {
|
4
|
+
:binary => {
|
5
|
+
:eq => '%s = ?',
|
6
|
+
:ne => '%s <> ?',
|
7
|
+
:lt => '%s < ?',
|
8
|
+
:lteq => '%s <= ?',
|
9
|
+
:gt => '%s > ?',
|
10
|
+
:gteq => '%s >= ?'
|
11
|
+
},
|
12
|
+
|
13
|
+
:ternary => {
|
14
|
+
:between => '%s BETWEEN ? AND ?'
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
attr_accessor :field, :relation, :values
|
19
|
+
|
20
|
+
def self.build_conditions(field, relation, *values)
|
21
|
+
self.new(field, relation, *values).conditions
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(field, relation, *values)
|
25
|
+
@field = field
|
26
|
+
@relation = relation
|
27
|
+
@values = values
|
28
|
+
end
|
29
|
+
|
30
|
+
def conditions
|
31
|
+
arguments_invalid? ?
|
32
|
+
[] :
|
33
|
+
[template_with_field_name] + values
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def arguments_invalid?
|
39
|
+
field.nil? or relation.nil? or values.nil? or template.nil?
|
40
|
+
end
|
41
|
+
|
42
|
+
def template_with_field_name
|
43
|
+
template % quoted_field_name
|
44
|
+
end
|
45
|
+
|
46
|
+
def quoted_field_name
|
47
|
+
ActiveRecord::Base.connection.quote_column_name(@field)
|
48
|
+
end
|
49
|
+
|
50
|
+
def template
|
51
|
+
case values.size
|
52
|
+
when 1:
|
53
|
+
SQL_RELATIONS_MAP[:binary][relation]
|
54
|
+
when 2:
|
55
|
+
SQL_RELATIONS_MAP[:ternary][relation]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module SData
|
4
|
+
module ControllerMixin
|
5
|
+
module Actions
|
6
|
+
def sdata_collection
|
7
|
+
begin
|
8
|
+
errors = []
|
9
|
+
collection = build_sdata_feed
|
10
|
+
sdata_scope.each do |entry|
|
11
|
+
begin
|
12
|
+
collection.entries << entry.to_atom(params)
|
13
|
+
rescue Exception => e
|
14
|
+
errors << ApplicationDiagnosis.new(:exception => e).to_xml(:feed)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
#TODO: syntactic sugar if possible (such as diagnosing_errors(&block) which does the dirty work)
|
18
|
+
errors.each do |error|
|
19
|
+
collection[SData.config[:schemas]['sdata'], 'diagnosis'] << error
|
20
|
+
end
|
21
|
+
populate_open_search_for(collection)
|
22
|
+
build_feed_links_for(collection)
|
23
|
+
render :xml => collection, :content_type => "application/atom+xml; type=feed"
|
24
|
+
rescue Exception => e
|
25
|
+
handle_exception(e)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def sdata_show_instance
|
30
|
+
begin
|
31
|
+
instance = sdata_instance
|
32
|
+
assert_access_to instance
|
33
|
+
render :xml => instance.to_atom(params), :content_type => "application/atom+xml; type=entry"
|
34
|
+
rescue Exception => e
|
35
|
+
handle_exception(e)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def sdata_create_instance
|
40
|
+
raise "not currently supported"
|
41
|
+
end
|
42
|
+
|
43
|
+
def sdata_update_instance
|
44
|
+
raise "not currently supported"
|
45
|
+
end
|
46
|
+
|
47
|
+
def sdata_create_link
|
48
|
+
begin
|
49
|
+
payload_xml = params['entry'].sdata_payload.raw_xml
|
50
|
+
payload = Nokogiri::XML(payload_xml).root
|
51
|
+
id = payload.attributes['key'].value.to_i
|
52
|
+
uuid = payload.attributes['uuid'].value
|
53
|
+
instance = model_class.find(id)
|
54
|
+
assert_access_to instance
|
55
|
+
instance.create_or_update_uuid! uuid
|
56
|
+
render :xml => instance.to_atom(params), :content_type => "application/atom+xml; type=entry", :status => "201"
|
57
|
+
rescue Exception => e
|
58
|
+
handle_exception(e)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
protected
|
63
|
+
|
64
|
+
def model_class
|
65
|
+
self.class.sdata_options[:model]
|
66
|
+
end
|
67
|
+
|
68
|
+
def assert_access_to(instance)
|
69
|
+
raise "Unauthenticated" unless logged_in?
|
70
|
+
# Not returning Access Denied on purpose so that users cannot fish for existence of emails or other data.
|
71
|
+
# As far as user should be concerned, all requests are scoped to his/her own data.
|
72
|
+
# Data which is found but which belongs to someone else should be as good as data that doesn't exist.
|
73
|
+
raise Sage::BusinessLogic::Exception::IncompatibleDataException, "Conditions scope must contain exactly one entry" if (instance.owner != target_user)
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
def handle_exception(exception)
|
78
|
+
diagnosis = SData::Diagnosis::DiagnosisMapper.map(exception)
|
79
|
+
render :xml => diagnosis.to_xml(:root), :status => diagnosis.http_status_code || 500
|
80
|
+
end
|
81
|
+
|
82
|
+
include SDataInstance
|
83
|
+
include SDataFeed
|
84
|
+
include CollectionScope
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|