acts_as_sdata 1.0.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.
- 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
|