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
File without changes
|
@@ -0,0 +1,136 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module SData
|
4
|
+
module PayloadMap
|
5
|
+
def define_payload_map(map)
|
6
|
+
include InstanceMethods
|
7
|
+
cattr_accessor :payload_map
|
8
|
+
self.payload_map = PayloadMapHash.new(map)
|
9
|
+
self.payload_map.each do |name, opts|
|
10
|
+
has_sdata_attr(name, opts)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def has_sdata_attr(*args)
|
15
|
+
options = args.last.is_a?(Hash) ? args.pop : {:static_value => nil, :precedence => 50}
|
16
|
+
args.each do | name |
|
17
|
+
options[:method_name] ||= name
|
18
|
+
options[:method_name_with_deleted] = options[:method_name]
|
19
|
+
|
20
|
+
method_name = options[:method_name]
|
21
|
+
payload_map[name] ||= options
|
22
|
+
|
23
|
+
case field_type(options)
|
24
|
+
when :static_value
|
25
|
+
define_static_value_reader method_name, options[:static_value]
|
26
|
+
|
27
|
+
when :baze_field
|
28
|
+
define_baze_field_reference method_name, options[:baze_field]
|
29
|
+
|
30
|
+
when :method
|
31
|
+
|
32
|
+
when :proc
|
33
|
+
define_proc_caller method_name, options[:proc]
|
34
|
+
|
35
|
+
if options.has_key?(:proc_with_deleted)
|
36
|
+
method_name_with_deleted = options[:method_name_with_deleted] = "#{method_name.to_s}_with_deleted"
|
37
|
+
define_proc_with_deleted_caller method_name_with_deleted, options[:proc_with_deleted]
|
38
|
+
end
|
39
|
+
|
40
|
+
else
|
41
|
+
raise SData::Exceptions::VirtualBase::InvalidSDataAttribute.new(
|
42
|
+
"#{args.join(", ")}: must supply a static_value, baze_field, method or proc")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def has_sdata_association(*args)
|
48
|
+
options = args.last.is_a?(Hash) ? args.pop : {:precedence => 50, :type => :association}
|
49
|
+
options[:type] ||= :association
|
50
|
+
raise SData::Exceptions::VirtualBase::InvalidSDataAssociation.new(
|
51
|
+
"#{args.join(", ")}: must supply a proc or method") unless [:proc, :method].any?{|k|options.has_key?(k)}
|
52
|
+
raise SData::Exceptions::VirtualBase::InvalidSDataAssociation.new(
|
53
|
+
"#{args.join(", ")}: invalid association type '#{options[:type]}") unless [:association, :child].include?(options[:type])
|
54
|
+
args.push options
|
55
|
+
has_sdata_attr(*args)
|
56
|
+
end
|
57
|
+
|
58
|
+
module InstanceMethods
|
59
|
+
def payload
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
# Walks the payload, loading each association, descending into children, and yielding the tuple
|
65
|
+
# [payload_map_definition, node_object(s)] for each node. Can use without a block
|
66
|
+
# to fire all faults -- sync uses this when caching changed objects.
|
67
|
+
def associations_with_deleted(expand=:all_children)
|
68
|
+
return if expand == :none
|
69
|
+
payload_map.each_pair do |child_node_name, child_node_data|
|
70
|
+
if child_node_data[:type] == :association
|
71
|
+
expand = :none
|
72
|
+
elsif child_node_data[:type] == :child
|
73
|
+
expand = :all_children
|
74
|
+
else
|
75
|
+
next
|
76
|
+
end
|
77
|
+
child = __send__(child_node_data[:method_name_with_deleted])
|
78
|
+
yield child_node_data.merge(:name => child_node_name), child if block_given?
|
79
|
+
case child
|
80
|
+
when Array
|
81
|
+
child.each{ |grandchild| grandchild.associations_with_deleted(expand) if child.is_a?(SData::VirtualBase)}
|
82
|
+
when SData::VirtualBase
|
83
|
+
associations_with_deleted(expand)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
protected
|
90
|
+
|
91
|
+
def field_type(options)
|
92
|
+
[:static_value, :baze_field, :method, :proc].each do |type|
|
93
|
+
if options.has_key?(type)
|
94
|
+
return type
|
95
|
+
end
|
96
|
+
end
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
|
100
|
+
def define_static_value_reader(method_name, value)
|
101
|
+
class_eval do
|
102
|
+
define_method method_name do
|
103
|
+
value
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def define_baze_field_reference(method_name, baze_field)
|
109
|
+
class_eval do
|
110
|
+
define_method method_name do
|
111
|
+
self.baze.__send__ baze_field
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def define_proc_caller(method_name, block)
|
117
|
+
class_eval do
|
118
|
+
define_method method_name, block
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def define_proc_with_deleted_caller(method_name, block)
|
123
|
+
cache_var = "@#{method_name.to_s}_cached"
|
124
|
+
class_eval do
|
125
|
+
define_method(method_name) do
|
126
|
+
unless instance_variable_get(cache_var)
|
127
|
+
instance_variable_set(cache_var, instance_eval(&block))
|
128
|
+
end
|
129
|
+
instance_variable_get(cache_var)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
SData::VirtualBase.extend SData::PayloadMap
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SData
|
2
|
+
module PayloadMap
|
3
|
+
class PayloadMapHash < Hash
|
4
|
+
def initialize(hash)
|
5
|
+
merge!(hash)
|
6
|
+
end
|
7
|
+
|
8
|
+
def attrs
|
9
|
+
reject{|k,v| ! [:static_value, :baze_field].any?{|type| v.has_key?(type)} }
|
10
|
+
end
|
11
|
+
|
12
|
+
def static_values
|
13
|
+
subset :static_value
|
14
|
+
end
|
15
|
+
|
16
|
+
def baze_fields
|
17
|
+
subset :baze_field
|
18
|
+
end
|
19
|
+
|
20
|
+
def procs
|
21
|
+
subset :proc
|
22
|
+
end
|
23
|
+
|
24
|
+
def procs_with_deleted
|
25
|
+
subset :proc_with_deleted, :proc
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def subset(*mapping_type)
|
31
|
+
mapping_type = [mapping_type].flatten
|
32
|
+
raw_subset = select { |key, value| mapping_type.any?{|type| value.has_key?(type) }}
|
33
|
+
hash = Hash[raw_subset]
|
34
|
+
hash.merge(hash){ |key, value| value[mapping_type.detect{|type| value.include?(type)}] }
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module SData
|
2
|
+
class Predicate < Struct.new(:field, :relation, :value)
|
3
|
+
def self.parse(fields_map, predicate_string)
|
4
|
+
# Gotcha on Rightscale that escaped + before it hits controller, but doesn't escape other special chars!
|
5
|
+
escaped_predicate_string = CGI::unescape(predicate_string.gsub('+', '%2B'))
|
6
|
+
match_data = escaped_predicate_string.match(/(\w+)\s(gt|lt|eq|ne|lteq|gteq)\s('?.+'?|'')/) || []
|
7
|
+
|
8
|
+
canonical_field_name = match_data[1].underscore.to_sym unless match_data[1].nil?
|
9
|
+
self.new fields_map[canonical_field_name], match_data[2], strip_quotes(match_data[3])
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.strip_quotes(value)
|
13
|
+
return value unless value.is_a?(String)
|
14
|
+
value = value.gsub("%27", "'")
|
15
|
+
return value unless value =~ /'.*?'/
|
16
|
+
return value[1,value.length-2]
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_conditions
|
20
|
+
if valid?
|
21
|
+
ConditionsBuilder.build_conditions field, relation.to_sym, value
|
22
|
+
else
|
23
|
+
raise Sage::BusinessLogic::Exception::IncompatibleDataException, "Invalid predicate string"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def valid?
|
28
|
+
field && relation && value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module SData
|
2
|
+
class RouteMapper < Struct.new(:router, :resource_name, :options)
|
3
|
+
def map_sdata_routes!
|
4
|
+
#RADAR: the order of the below statements makes a difference
|
5
|
+
|
6
|
+
map_sdata_show_instance
|
7
|
+
map_sdata_show_instance_with_condition_and_predicate
|
8
|
+
map_sdata_show_instance_with_condition
|
9
|
+
map_sdata_show_instance_with_predicate
|
10
|
+
|
11
|
+
map_sdata_create_link
|
12
|
+
map_sdata_create_instance
|
13
|
+
map_sdata_update_instance
|
14
|
+
|
15
|
+
map_sdata_sync_source
|
16
|
+
map_sdata_sync_source_status
|
17
|
+
map_sdata_sync_source_delete
|
18
|
+
map_sdata_receive_sync_results
|
19
|
+
|
20
|
+
map_sdata_collection_with_condition
|
21
|
+
map_sdata_collection
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.urlize(string)
|
25
|
+
string.gsub("'", "(%27|')").gsub("\s", "(%20|\s)")
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
CONDITION = urlize("{:condition,([$](linked))}")
|
31
|
+
PREDICATE = urlize("{:predicate,[A-z]+\s[A-z]+\s'?[^']*'?}")
|
32
|
+
|
33
|
+
TRACKING_ID = urlize("{:trackingID,'[^']+'}")
|
34
|
+
|
35
|
+
# http://localhost:3000/sdata/billingboss/crmErp/-/tradingAccounts
|
36
|
+
def map_sdata_collection
|
37
|
+
map_route "#{name_in_path}", 'sdata_collection', :get
|
38
|
+
end
|
39
|
+
|
40
|
+
# http://localhost:3000/sdata/billingboss/crmErp/-/tradingAccounts/$syncSource('someid')
|
41
|
+
def map_sdata_sync_source_status
|
42
|
+
map_route "#{name_in_path}\/$syncSource\\(:trackingID\\)", 'sdata_collection_sync_feed_status', :get
|
43
|
+
end
|
44
|
+
|
45
|
+
# http://localhost:3000/sdata/billingboss/crmErp/-/tradingAccounts/$syncSource
|
46
|
+
def map_sdata_sync_source_delete
|
47
|
+
map_route "#{name_in_path}\/$syncSource\\(:trackingID\\)", 'sdata_collection_sync_feed_delete', :delete
|
48
|
+
end
|
49
|
+
|
50
|
+
# http://localhost:3000/sdata/billingboss/crmErp/-/tradingAccounts/$linked
|
51
|
+
def map_sdata_collection_with_condition
|
52
|
+
map_route "#{name_in_path}\/#{CONDITION}", 'sdata_collection', :get
|
53
|
+
end
|
54
|
+
|
55
|
+
# http://localhost:3000/sdata/billingboss/crmErp/-/tradingAccounts('1')
|
56
|
+
def map_sdata_show_instance
|
57
|
+
map_route "#{name_in_path}\\(:instance_id\\)", 'sdata_show_instance', :get
|
58
|
+
end
|
59
|
+
|
60
|
+
# http://localhost:3000/sdata/billingboss/crmErp/-/tradingAccounts/$linked('1')
|
61
|
+
def map_sdata_show_instance_with_condition
|
62
|
+
map_route "#{name_in_path}/#{CONDITION}\\(:instance_id\\)", 'sdata_show_instance', :get
|
63
|
+
end
|
64
|
+
|
65
|
+
# http://localhost:3000/sdata/billingboss/crmErp/-/tradingAccounts(name eq 'asdf')
|
66
|
+
# http://localhost:3000/sdata/billingboss/crmErp/-/tradingAccounts(name eq asdf)
|
67
|
+
# http://localhost:3000/sdata/billingboss/crmErp/-/tradingAccounts(name eq '')
|
68
|
+
def map_sdata_show_instance_with_predicate
|
69
|
+
map_route "#{name_in_path}\\(#{PREDICATE}\\)", 'sdata_show_instance', :get
|
70
|
+
end
|
71
|
+
|
72
|
+
# /$linked(name eq 'Second')
|
73
|
+
def map_sdata_show_instance_with_condition_and_predicate
|
74
|
+
map_route "#{name_in_path}/#{CONDITION}\\(#{PREDICATE}\\)", 'sdata_show_instance', :get
|
75
|
+
end
|
76
|
+
|
77
|
+
# http://localhost:3000/sdata/billingboss/crmErp/-/tradingAccounts
|
78
|
+
def map_sdata_create_instance
|
79
|
+
map_route "#{name_in_path}", 'sdata_create_instance', :post
|
80
|
+
end
|
81
|
+
|
82
|
+
# http://localhost:3000/sdata/billingboss/crmErp/-/tradingAccounts/$linked
|
83
|
+
def map_sdata_create_link
|
84
|
+
map_route "#{name_in_path}/#{CONDITION}", 'sdata_create_link', :post
|
85
|
+
end
|
86
|
+
|
87
|
+
# http://localhost:3000/sdata/billingboss/crmErp/-/tradingAccounts('1')
|
88
|
+
def map_sdata_update_instance
|
89
|
+
map_route "#{name_in_path}\\(:instance_id\\)", 'sdata_update_instance', :put
|
90
|
+
end
|
91
|
+
|
92
|
+
# http://localhost:3000/sdata/billingboss/crmErp/-/tradingAccounts/$syncSource
|
93
|
+
def map_sdata_sync_source
|
94
|
+
map_route "#{name_in_path}/$syncSource", 'sdata_collection_sync_feed', :post
|
95
|
+
end
|
96
|
+
|
97
|
+
# http://localhost:3000/sdata/billingboss/crmErp/-/tradingAccounts/$syncResults
|
98
|
+
def map_sdata_receive_sync_results
|
99
|
+
map_route "#{name_in_path}/$syncResults\\(:trackingID\\)", 'sdata_collection_sync_results', :post
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def map_route(path, action, method)
|
104
|
+
path = prefix.chomp('/') + '/' + path if prefix?
|
105
|
+
path = path + ".sdata" if formatted_paths?
|
106
|
+
|
107
|
+
router.connect path, :controller => controller_with_namespace, :action => action, :conditions => { :method => method }, :priority => 99
|
108
|
+
end
|
109
|
+
|
110
|
+
def controller_with_namespace
|
111
|
+
@controller_with_namespace ||= "#{namespace}#{controller}"
|
112
|
+
end
|
113
|
+
|
114
|
+
def namespace
|
115
|
+
@namespace ||= (options[:namespace] ? "#{options[:namespace]}/" : "")
|
116
|
+
end
|
117
|
+
|
118
|
+
def controller
|
119
|
+
@controller ||= resource_name.to_s.pluralize
|
120
|
+
end
|
121
|
+
|
122
|
+
def name_in_path
|
123
|
+
@name_in_path ||= resource_name.to_s.camelize(:lower).pluralize
|
124
|
+
end
|
125
|
+
|
126
|
+
def formatted_paths?
|
127
|
+
@formatted_paths ||= options[:formatted_paths]
|
128
|
+
end
|
129
|
+
|
130
|
+
def prefix
|
131
|
+
if options[:prefix]
|
132
|
+
@prefix = options[:prefix] + '/{:dataset,([^\/]+)}'
|
133
|
+
else
|
134
|
+
@prefix = '/'
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def prefix?
|
139
|
+
!! prefix
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module SData
|
2
|
+
module RouterMixin
|
3
|
+
def sdata_resource(name, options={})
|
4
|
+
route_creator = SData::RouteMapper.new(self, name, options)
|
5
|
+
route_creator.map_sdata_routes!
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
ActionController::Routing::RouteSet::Mapper.__send__ :include, SData::RouterMixin
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module SData
|
2
|
+
module Sync
|
3
|
+
module ControllerMixin
|
4
|
+
def syncs_sdata(options={})
|
5
|
+
raise "cannot sync sdata unless controller first acts_as_sdata" if self.sdata_options.nil?
|
6
|
+
cattr_accessor :sdata_sync_options
|
7
|
+
self.sdata_sync_options = options
|
8
|
+
|
9
|
+
include InstanceMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
module Actions
|
14
|
+
# TODO: error handling
|
15
|
+
def sdata_collection_sync_feed
|
16
|
+
# get the target digest
|
17
|
+
payload = params[:entry].sdata_payload
|
18
|
+
|
19
|
+
raise "no payload!" if payload.blank?
|
20
|
+
|
21
|
+
sd_sync_run = SData::SdSyncRun.find_by_tracking_id(tracking_id)
|
22
|
+
raise Sage::BusinessLogic::Exception::AccessDeniedException, "Access denied" unless sd_sync_run.nil?
|
23
|
+
|
24
|
+
# prepare a sync run to hold our state
|
25
|
+
sd_sync_run = SData::SdSyncRun.new( :tracking_id => tracking_id,
|
26
|
+
:run_name => params[:runName],
|
27
|
+
# :created_at => params[:runStamp],
|
28
|
+
:sd_class => model_class.sdata_name,
|
29
|
+
:created_by => target_user)
|
30
|
+
sd_sync_run.target_digest = payload.sync_digest
|
31
|
+
|
32
|
+
sd_sync_run.start!
|
33
|
+
sd_sync_run.process!
|
34
|
+
xml = sd_sync_run.to_xml.to_s
|
35
|
+
render :xml => xml, :content_type => "application/xml", :location => sync_source_url(sd_sync_run.tracking_id), :status => 202
|
36
|
+
end
|
37
|
+
|
38
|
+
def sdata_collection_sync_feed_status
|
39
|
+
sd_sync_run = SData::SdSyncRun.find_by_tracking_id(tracking_id)
|
40
|
+
raise Sage::BusinessLogic::Exception::AccessDeniedException, "Access denied" if sd_sync_run.nil?
|
41
|
+
assert_access_to sd_sync_run
|
42
|
+
if sd_sync_run.finished?
|
43
|
+
@total_results = sd_sync_run.total_results
|
44
|
+
feed = build_sdata_feed(:feed => {:title => "#{model_class.sdata_name} synchronization feed #{params[:trackingID]}"})
|
45
|
+
feed[SData.config[:schemas]['sync'], 'syncMode'] << "catchUp"
|
46
|
+
|
47
|
+
feed.sync_digest = sd_sync_run.source_digest
|
48
|
+
|
49
|
+
atom_entries = []
|
50
|
+
errors = []
|
51
|
+
sd_sync_run.objects[zero_based_start_index,records_to_return].each do |obj|
|
52
|
+
begin
|
53
|
+
# RADAR: this (the (params) part) will allow user to insert a different dataset (and possibly
|
54
|
+
# other data) than that which was synchronized during the run. If this is somehow a security
|
55
|
+
# problem, need to freeze that data during synctime. an be also solved by freezing username
|
56
|
+
# in feed and matching dataset against it at accestime.
|
57
|
+
# RADAR: children (e.g. line items) will be embedded in parent (e.g. invoice) during this
|
58
|
+
# request, but from live data rather than syncronized one, is this a potential problem?
|
59
|
+
feed.entries << obj.to_atom(params.merge(:sync => true)){|entry| entry.sync_syncState = obj.sd_sync_state }
|
60
|
+
rescue Exception => e
|
61
|
+
errors << ApplicationDiagnosis.new(:exception => e).to_xml(:feed)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
#TODO: syntactic sugar if possible (such as diagnosing_errors(&block) which does the dirty work)
|
65
|
+
errors.each do |error|
|
66
|
+
feed[SData.config[:schemas]['sdata'], 'diagnosis'] << error
|
67
|
+
end
|
68
|
+
populate_open_search_for(feed)
|
69
|
+
build_feed_links_for(feed)
|
70
|
+
render :xml => feed, :content_type => "application/atom+xml; type=feed"
|
71
|
+
else
|
72
|
+
render :xml => sd_sync_run.to_xml.to_s,
|
73
|
+
:content_type => "application/atom+xml; type=feed",
|
74
|
+
:location => sync_source_url(sd_sync_run.tracking_id),
|
75
|
+
:status => 202
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def sdata_collection_sync_feed_delete
|
80
|
+
sd_sync_run = SData::SdSyncRun.find_by_tracking_id(tracking_id)
|
81
|
+
sd_sync_run.destroy
|
82
|
+
render :text => "OK"
|
83
|
+
end
|
84
|
+
|
85
|
+
def sdata_collection_sync_results
|
86
|
+
render :text => "Not implemented!"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
module AuxiliaryMethods
|
90
|
+
protected
|
91
|
+
|
92
|
+
# TODO: sort out profusion of urls endpoints and config classes!
|
93
|
+
def resource_url
|
94
|
+
case self.action_name
|
95
|
+
when 'sdata_collection_sync_feed_status',
|
96
|
+
'sdata_collection_sync_feed_status',
|
97
|
+
'sdata_collection_sync_feed_delete'
|
98
|
+
super + "/$syncSource('#{tracking_id}')"
|
99
|
+
else
|
100
|
+
super
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def sync_source_url(track)
|
105
|
+
endpoint = target_user.endpoint
|
106
|
+
"#{endpoint}/#{SData.sdata_collection_url_component(model_class)}/$syncSource('#{track}')"
|
107
|
+
end
|
108
|
+
|
109
|
+
def tracking_id
|
110
|
+
CGI::unescape(params[:trackingID]).gsub(/^'/,'').gsub(/'$/,'')
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
end
|
115
|
+
include Actions
|
116
|
+
include AuxiliaryMethods
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
ActionController::Base.extend SData::Sync::ControllerMixin
|
122
|
+
# Atom::Entry.elements "sync:digest", :class => SData::SdDigest::AtomWrapper
|