App Engine Python SDK version 1.8.9
[gae.git] / python / google / appengine / ext / datastore_admin / delete_handler.py
blob97ad029a08eca6b2a88ddb08d1278f4e049af1c2
1 #!/usr/bin/env python
3 # Copyright 2007 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
21 """Used to confirm and act on delete requests from the Admin Console."""
27 import os
28 import re
29 import urllib
31 from google.appengine.api import capabilities
32 from google.appengine.api import datastore
33 from google.appengine.ext import webapp
34 from google.appengine.ext.datastore_admin import config
35 from google.appengine.ext.datastore_admin import utils
36 from google.appengine.ext.mapreduce import model
37 from google.appengine.ext.mapreduce import operation
39 MAPREDUCE_OBJECTS = [model.MapreduceState.kind(),
40 model.ShardState.kind()]
41 XSRF_ACTION = 'delete'
42 KIND_AND_SIZE_RE = re.compile('^(.*)\|(-?[0-9]+)$')
45 def DeleteEntity(key):
46 """Delete function which deletes all processed entities.
48 Args:
49 key: key of the entity to delete.
51 Yields:
52 a delete operation if the entity is not an active mapreduce or
53 DatastoreAdminOperation object.
54 """
55 if key.kind() in MAPREDUCE_OBJECTS:
56 entity = datastore.Get(key)
57 if entity and not entity["active"]:
58 yield operation.db.Delete(key)
59 elif key.kind() == utils.DatastoreAdminOperation.kind():
60 entity = datastore.Get(key)
61 if entity and not entity["active_jobs"]:
62 yield operation.db.Delete(key)
63 else:
64 yield operation.db.Delete(key)
67 class ConfirmDeleteHandler(webapp.RequestHandler):
68 """Handler to deal with requests from the admin console to delete data."""
70 SUFFIX = 'confirm_delete'
72 @classmethod
73 def Render(cls, handler):
74 """Rendering method that can be called by main.py or get.
76 This method executes no action, so the method by which it is accessed is
77 immaterial. Creating a form with get may be a desirable function. That is,
78 if this builtin is turned on, anyone can create a form to delete a kind by
79 simply linking to the ConfirmDeleteHandler like so:
80 <a href="/_ah/datastore_admin/confirm_delete?kind=trash">
81 Delete all Trash Objects</a>
83 Args:
84 handler: the webapp.RequestHandler invoking the method
85 """
86 readonly_warning = not capabilities.CapabilitySet(
87 'datastore_v3', capabilities=['write']).is_enabled()
88 namespace = handler.request.get('namespace')
89 kinds = handler.request.get_all('kind')
90 sizes_known, size_total, remainder = utils.ParseKindsAndSizes(kinds)
92 (namespace_str, kind_str) = utils.GetPrintableStrs(namespace, kinds)
93 template_params = {
94 'readonly_warning': readonly_warning,
95 'form_target': DoDeleteHandler.SUFFIX,
96 'kind_list': kinds,
97 'remainder': remainder,
98 'sizes_known': sizes_known,
99 'size_total': size_total,
100 'app_id': handler.request.get('app_id'),
101 'datastore_admin_home': utils.GenerateHomeUrl(handler.request),
102 'kind_str': kind_str,
103 'namespace_str': namespace_str,
104 'xsrf_token': utils.CreateXsrfToken(XSRF_ACTION),
106 utils.RenderToResponse(handler, 'confirm_delete.html', template_params)
108 def get(self):
109 """Handler for get requests to datastore_admin/confirm_delete."""
110 ConfirmDeleteHandler.Render(self)
113 class DoDeleteHandler(webapp.RequestHandler):
114 """Handler to deal with requests from the admin console to delete data."""
116 SUFFIX = 'delete.do'
117 DELETE_HANDLER = (
118 'google.appengine.ext.datastore_admin.delete_handler.DeleteEntity')
119 INPUT_READER = (
120 'google.appengine.ext.mapreduce.input_readers.DatastoreKeyInputReader')
121 MAPREDUCE_DETAIL = config.MAPREDUCE_PATH + '/detail?mapreduce_id='
123 def get(self):
124 """Handler for get requests to datastore_admin/delete.do.
126 Status of executed jobs is displayed.
128 jobs = self.request.get_all('job')
129 error = self.request.get('error', '')
130 xsrf_error = self.request.get('xsrf_error', '')
131 noconfirm_error = self.request.get('noconfirm_error', '')
133 template_params = {
134 'job_list': jobs,
135 'mapreduce_detail': self.MAPREDUCE_DETAIL,
136 'error': error,
137 'xsrf_error': xsrf_error,
138 'noconfirm_error': noconfirm_error,
139 'datastore_admin_home': config.BASE_PATH,
141 utils.RenderToResponse(self, 'do_delete.html', template_params)
143 def post(self):
144 """Handler for post requests to datastore_admin/delete.do.
146 Jobs are executed and user is redirected to the get handler.
148 namespace = self.request.get('namespace')
149 kinds = self.request.get_all('kind')
150 (namespace_str, kinds_str) = utils.GetPrintableStrs(namespace, kinds)
151 token = self.request.get('xsrf_token')
152 readonly_warning = self.request.get('readonly_warning')
154 jobs = []
156 if (readonly_warning == 'True') and not self.request.get(
157 'confirm_readonly_delete'):
158 parameters = [('noconfirm_error', '1')]
159 else:
160 if utils.ValidateXsrfToken(token, XSRF_ACTION):
161 try:
162 op = utils.StartOperation(
163 'Deleting %s%s' % (kinds_str, namespace_str))
164 name_template = 'Delete all %(kind)s objects%(namespace)s'
165 mapreduce_params = {'force_ops_writes': True}
166 queue = self.request.get('queue')
167 queue = queue or os.environ.get(
168 'HTTP_X_APPENGINE_QUEUENAME', 'default')
169 if queue[0] == '_':
171 queue = 'default'
172 jobs = utils.RunMapForKinds(
173 op.key(),
174 kinds,
175 name_template,
176 self.DELETE_HANDLER,
177 self.INPUT_READER,
178 None,
180 mapreduce_params=mapreduce_params,
181 queue_name=queue,
182 max_shard_count=utils.MAPREDUCE_DEFAULT_SHARDS)
183 error = ''
186 except Exception, e:
187 error = self._HandleException(e)
189 parameters = [('job', job) for job in jobs]
190 if error:
191 parameters.append(('error', error))
192 else:
193 parameters = [('xsrf_error', '1')]
195 query = urllib.urlencode(parameters)
197 self.redirect('%s/%s?%s' % (config.BASE_PATH, self.SUFFIX, query))
199 def _HandleException(self, e):
200 """Make exception handling overrideable by tests.
202 In normal cases, return only the error string; do not fail to render the
203 page for user.
205 return str(e)