api06: diff upload: don't read the whole request into memory.
[rails.git] / app / controllers / changeset_controller.rb
1 # The ChangesetController is the RESTful interface to Changeset objects
2
3 class ChangesetController < ApplicationController
4   require 'xml/libxml'
5
6   before_filter :authorize, :only => [:create, :update, :delete, :upload]
7   before_filter :check_write_availability, :only => [:create, :update, :delete, :upload]
8   before_filter :check_read_availability, :except => [:create, :update, :delete, :upload]
9   after_filter :compress_output
10
11   # Create a changeset from XML.
12   def create
13     if request.put?
14       cs = Changeset.from_xml(request.raw_post, true)
15
16       if cs
17         cs.user_id = @user.id
18         cs.save_with_tags!
19         render :text => cs.id.to_s, :content_type => "text/plain"
20       else
21         render :nothing => true, :status => :bad_request
22       end
23     else
24       render :nothing => true, :status => :method_not_allowed
25     end
26   end
27
28   def create_prim(ids, prim, nd)
29     prim.version = 0
30     prim.user_id = @user.id
31     prim.visible = true
32     prim.save_with_history!
33
34     ids[nd['id'].to_i] = prim.id
35   end
36
37   def fix_way(w, node_ids)
38     w.nds = w.instance_eval { @nds }.
39       map { |nd| node_ids[nd] || nd }
40     return w
41   end
42
43   def fix_rel(r, ids)
44     r.members = r.instance_eval { @members }.
45       map { |memb| [memb[0], ids[memb[0]][memb[1].to_i] || memb[1], memb[2]] }
46     return r
47   end
48   
49   def read
50     begin
51       changeset = Changeset.find(params[:id])
52       render :text => changeset.to_xml.to_s, :content_type => "text/xml"
53     rescue ActiveRecord::RecordNotFound
54       render :nothing => true, :status => :not_found
55     end
56   end
57   
58   def close 
59     begin
60       if not request.put?
61         render :nothing => true, :status => :method_not_allowed
62         return
63       end
64       changeset = Changeset.find(params[:id])
65       changeset.open = false
66       changeset.save
67       render :nothing => true
68     rescue ActiveRecord::RecordNotFound
69       render :nothing => true, :status => :not_found
70     end
71   end
72
73   def upload
74     if not request.put?
75       render :nothing => true, :status => :method_not_allowed
76       return
77     end
78
79     p = XML::Reader.new request.raw_post
80
81     node_ids, way_ids, rel_ids = {}, {}, {}
82     ids = {"node"=>node_ids, "way"=>way_ids, "relation"=>rel_ids}
83
84     res = XML::Document.new
85     res.encoding = 'UTF-8'
86     root = XML::Node.new 'osm'
87     root['version'] = '0.6'
88     root['creator'] = 'OpenStreetMap.org'
89     res.root = root
90
91     root << XML::Node.new_comment(" Warning: this is a 0.6 result document, " +
92       "not a normal OSM file. ")
93
94     Changeset.transaction do
95       while p.read == 1
96         break if p.node_type == 15 # end element
97         next unless p.node_type == 1 # element
98
99         case p.name
100         when 'create':
101           while p.read == 1
102             break if p.node_type == 15 # end element
103             next unless p.node_type == 1 # element
104
105             case p.name
106             when 'node':
107               elem = XML::Node.new 'node'
108               node = Node.from_xml_node(p.expand, true)
109               elem['old_id'] = p.expand['id']
110               create_prim node_ids, node, p.expand
111               elem['new_id'] = node.id.to_s
112               elem['new_version'] = node.version.to_s
113               root << elem
114             when 'way':
115               elem = XML::Node.new 'way'
116               way = Way.from_xml_node(p.expand, true)
117               elem['old_id'] = p.expand['id']
118               fix_way(way, node_ids)
119               raise OSM::APIPreconditionFailedError.new if !way.preconditions_ok?
120               create_prim way_ids, way, p.expand
121               elem['new_id'] = way.id.to_s
122               elem['new_version'] = way.version.to_s
123               root << elem
124             when 'relation':
125               elem = XML::Node.new 'relation'
126               relation = Relation.from_xml_node(p.expand, true)
127               elem['old_id'] = p.expand['id']
128               fix_rel(relation, ids)
129               raise OSM::APIPreconditionFailedError.new if !relation.preconditions_ok?
130               create_prim rel_ids, relation, p.expand
131               elem['new_id'] = relation.id.to_s
132               elem['new_version'] = relation.version.to_s
133               root << elem
134             end
135           end
136         when 'modify':
137           while p.read == 1
138             break if p.node_type == 15 # end element
139             next unless p.node_type == 1 # element
140
141             case p.name
142             when 'node':
143               elem = XML::Node.new 'node'
144               new_node = Node.from_xml_node(p.expand)
145               node = Node.find(new_node.id)
146               node.update_from new_node, @user
147               elem['old_id'] = elem['new_id'] = node.id.to_s
148               elem['new_version'] = node.version.to_s
149               root << elem
150             when 'way':
151               elem = XML::Node.new 'way'
152               new_way = Way.from_xml_node(p.expand)
153               way = Way.find(new_way.id)
154               way.update_from new_way, @user
155               elem['old_id'] = elem['new_id'] = way.id.to_s
156               elem['new_version'] = way.version.to_s
157               root << elem
158             when 'relation':
159               elem = XML::Node.new 'relation'
160               new_relation = Relation.from_xml_node(p.expand)
161               relation = Relation.find(new_relation.id)
162               relation.update_from new_relation, @user
163               elem['old_id'] = elem['new_id'] = relation.id.to_s
164               elem['new_version'] = relation.version.to_s
165               root << elem
166             end
167           end
168         when 'delete':
169           while p.read == 1
170             break if p.node_type == 15 # end element
171             next unless p.node_type == 1 # element
172
173             case p.name
174             when 'node':
175               elem = XML::Node.new 'node'
176               node = Node.find(p.expand['id'])
177               node.delete_with_history(@user)
178               elem['old_id'] = elem['new_id'] = node.id.to_s
179               elem['new_version'] = node.version.to_s
180               root << elem
181             when 'way':
182               elem = XML::Node.new 'way'
183               way = Way.find(p.expand['id'])
184               way.delete_with_history(@user)
185               elem['old_id'] = elem['new_id'] = way.id.to_s
186               elem['new_version'] = way.version.to_s
187               root << elem
188             when 'relation':
189               elem = XML::Node.new 'relation'
190               relation = Relation.find(p.expand['id'])
191               relation.delete_with_history(@user)
192               elem['old_id'] = elem['new_id'] = relation.id.to_s
193               elem['new_version'] = relation.version.to_s
194               root << elem
195             end
196           end
197         end
198       end
199     end
200
201     render :text => res.to_s, :content_type => "text/xml"
202
203   rescue OSM::APIError => ex
204     render ex.render_opts
205   end
206 end