Skip to content

Commit 135c47b

Browse files
committed
🔨 refactor disable hook, and leancloud.Object.destroy_all
1 parent 13a19f7 commit 135c47b

3 files changed

Lines changed: 63 additions & 29 deletions

File tree

leancloud/client.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
APP_ID = None
2525
APP_KEY = None
2626
MASTER_KEY = None
27+
HOOK_KEY = None
2728
USE_PRODUCTION = '1'
2829
USE_HTTPS = True
2930
# 兼容老版本,如果 USE_MASTER_KEY 为 None ,并且 MASTER_KEY 不为 None,则使用 MASTER_KEY
@@ -40,7 +41,7 @@
4041
TIMEOUT_SECONDS = 15
4142

4243

43-
def init(app_id, app_key=None, master_key=None):
44+
def init(app_id, app_key=None, master_key=None, hook_key=None):
4445
"""初始化 LeanCloud 的 AppId / AppKey / MasterKey
4546
4647
:type app_id: string_types
@@ -49,13 +50,19 @@ def init(app_id, app_key=None, master_key=None):
4950
:param app_key: 应用的 Application Key
5051
:type master_key: None or string_types
5152
:param master_key: 应用的 Master Key
53+
:param hook_key: application's hook key
54+
:type hook_key: None or string_type
5255
"""
5356
if (not app_key) and (not master_key):
5457
raise RuntimeError('app_key or master_key must be specified')
55-
global APP_ID, APP_KEY, MASTER_KEY
58+
global APP_ID, APP_KEY, MASTER_KEY, HOOK_KEY
5659
APP_ID = app_id
5760
APP_KEY = app_key
5861
MASTER_KEY = master_key
62+
if hook_key:
63+
HOOK_KEY = hook_key
64+
else:
65+
HOOK_KEY = os.environ.get('LEANCLOUD_APP_HOOK_KEY')
5966

6067

6168
def need_init(func):
@@ -176,6 +183,7 @@ def get_app_info():
176183
'app_id': APP_ID,
177184
'app_key': APP_KEY,
178185
'master_key': MASTER_KEY,
186+
'hook_key': HOOK_KEY,
179187
}
180188

181189

leancloud/object_.py

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@
44
from __future__ import division
55
from __future__ import print_function
66

7-
import time
87
import copy
98
import json
109

11-
import iso8601
12-
from werkzeug import LocalProxy
10+
from werkzeug.local import LocalProxy
1311

1412
import leancloud
1513
from leancloud import utils
@@ -80,6 +78,7 @@ def __init__(self, **attrs):
8078
self._class_name = self._class_name # for IDE
8179
self._changes = {}
8280
self._attributes = {}
81+
self._flags = {}
8382
self.created_at = None
8483
self.updated_at = None
8584

@@ -152,13 +151,32 @@ def destroy_all(cls, objs):
152151
"""
153152
if not objs:
154153
return
155-
if not all(x._class_name == objs[0]._class_name for x in objs):
156-
raise ValueError("destroy_all requires the argument object list's _class_names must be the same")
157154
if any(x.is_new() for x in objs):
158155
raise ValueError("Could not destroy unsaved object")
159-
ids = {x.id for x in objs}
160-
ids = ','.join(ids)
161-
client.delete('/classes/{0}/{1}'.format(objs[0]._class_name, ids))
156+
157+
dumped_objs = []
158+
for obj in objs:
159+
dumped_obj = {
160+
'method': 'DELETE',
161+
'path': '/{0}/classes/{1}/{2}'.format(client.SERVER_VERSION, obj._class_name, obj.id),
162+
'body': obj._flags,
163+
}
164+
dumped_objs.append(dumped_obj)
165+
166+
response = client.post('/batch', params={'requests': dumped_objs}).json()
167+
168+
errors = []
169+
for idx, obj in enumerate(objs):
170+
content = response[idx]
171+
error = content.get('error')
172+
if error:
173+
errors.append(leancloud.LeanCloudError(error.get('code'), error.get('error')))
174+
175+
if errors:
176+
# TODO: how to raise list of errors?
177+
# raise MultipleValidationErrors(errors)
178+
# add test
179+
raise errors[0]
162180

163181
def dump(self):
164182
obj = self._dump()
@@ -186,7 +204,7 @@ def destroy(self):
186204
"""
187205
if not self.id:
188206
return
189-
client.delete('/classes/{0}/{1}'.format(self._class_name, self.id))
207+
client.delete('/classes/{0}/{1}'.format(self._class_name, self.id), self._flags)
190208

191209
def save(self, where=None, fetch_when_save=None):
192210
"""
@@ -464,7 +482,9 @@ def clear(self):
464482
self.set(self._attributes, unset=True)
465483

466484
def _dump_save(self):
467-
return {k:v.dump() for k,v in iteritems(self._changes)}
485+
data = {k: v.dump() for k, v in iteritems(self._changes)}
486+
data.update(self._flags)
487+
return data
468488

469489
def fetch(self, select=None, include=None):
470490
"""
@@ -515,18 +535,29 @@ def set_acl(self, acl):
515535
return self.set('ACL', acl)
516536

517537
def disable_before_hook(self):
518-
master_key = client.get_app_info().get('master_key')
519-
if not master_key:
520-
raise ValueError('disable_before_hook need LeanCloud master key')
521-
timestamp = int(time.time() * 1000)
522-
return self.set('__before', utils.sign_hook('__before_for_' + self._class_name, master_key, timestamp))
538+
hook_key = client.get_app_info().get('hook_key')
539+
if not hook_key:
540+
raise ValueError('disable_before_hook need LeanCloud hook key')
541+
self.ignore_hook('beforeSave')
542+
self.ignore_hook('beforeUpdate')
543+
self.ignore_hook('beforeDelete')
544+
return self
523545

524546
def disable_after_hook(self):
525-
master_key = client.get_app_info().get('master_key')
526-
if not master_key:
527-
raise ValueError('disable_before_hook need LeanCloud master key')
528-
timestamp = int(time.time() * 1000)
529-
return self.set('__after', utils.sign_hook('__after_for_' + self._class_name, master_key, timestamp))
547+
hook_key = client.get_app_info().get('hook_key')
548+
if not hook_key:
549+
raise ValueError('`disable_before_hook` need LeanCloud hook key')
550+
self.ignore_hook('afterSave')
551+
self.ignore_hook('afterUpdate')
552+
self.ignore_hook('afterDelete')
553+
return self
554+
555+
def ignore_hook(self, hook_name):
556+
if hook_name not in {'beforeSave', 'afterSave', 'beforeUpdate', 'afterUpdate', 'beforeDelete', 'afterDelete'}:
557+
raise ValueError('invalid hook name: ' + hook_name)
558+
if '__ignore_hooks' not in self._flags:
559+
self._flags['__ignore_hooks'] = []
560+
self._flags['__ignore_hooks'].append(hook_name)
530561

531562
def _update_data(self, server_data):
532563
self._merge_metadata(server_data)

tests/test_engine.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def setup():
5757
leancloud.client.APP_ID = None
5858
leancloud.client.APP_KEY = None
5959
leancloud.client.MASTER_KEY = None
60-
leancloud.init(TEST_APP_ID, TEST_APP_KEY, TEST_MASTER_KEY)
60+
leancloud.init(TEST_APP_ID, TEST_APP_KEY, TEST_MASTER_KEY, TEST_HOOK_KEY)
6161
authorization._ENABLE_TEST = True
6262
authorization.APP_ID = TEST_APP_ID
6363
authorization.APP_KEY = TEST_APP_KEY
@@ -357,7 +357,6 @@ def rpc_list(**params):
357357
def test_before_save_hook(): # type: () -> None
358358
@engine.before_save('HookObject')
359359
def before_hook_object_save(obj):
360-
assert obj.has('__before')
361360
obj.set('beforeSaveHookInserted', True)
362361

363362
response = requests.post(url + '/__engine/1/functions/HookObject/beforeSave', json={
@@ -370,13 +369,12 @@ def before_hook_object_save(obj):
370369
assert response.ok
371370
assert response.json()['beforeSaveHookInserted'] == True
372371
assert response.json()['clientValue'] == 'blah'
373-
assert '__before' in response.json()
374372

375373

376374
def test_after_save_hook(): # type: () -> None
377375
@engine.after_save('HookObject')
378376
def after_hook_object_save(obj):
379-
assert obj.has('__after')
377+
pass
380378

381379
response = requests.post(url + '/__engine/1/functions/HookObject/afterSave', json={
382380
'object': {'clientValue': 'blah'}
@@ -458,14 +456,12 @@ def on_insight_end(ok, data):
458456

459457

460458
def test_client(): # type: () -> None
461-
leancloud.init(os.environ['APP_ID'], os.environ['APP_KEY'])
462459
assert cloudfunc.run('add', a=1, b=2) == 3
463460

464461

465462
def test_request_sms_code(): # type: () -> None
466463
if leancloud.client.REGION == 'US':
467464
return
468-
leancloud.init(os.environ['APP_ID'], master_key=os.environ['MASTER_KEY'])
469465
try:
470466
cloudfunc.request_sms_code('13111111111')
471467
except LeanCloudError as e:
@@ -497,7 +493,6 @@ def test_captcha(): # type: () -> None
497493

498494

499495
def test_current_user(): # type: () -> None
500-
leancloud.init(os.environ['APP_ID'], master_key=os.environ['MASTER_KEY'])
501496
saved_user = leancloud.User()
502497
saved_user.set('username', 'user{0}'.format(int(time.time())))
503498
saved_user.set('password', 'password')

0 commit comments

Comments
 (0)