Skip to content

Commit 056e079

Browse files
authored
Merge pull request #536 from leancloud/file-scan-test
More fixes on files.
2 parents a05b6e4 + 4c30efd commit 056e079

File tree

5 files changed

+104
-20
lines changed

5 files changed

+104
-20
lines changed

leancloud/file_.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ def __init__(self, name="", data=None, mime_type=None):
3535
self._name = name
3636
self.key = None
3737
self.id = None
38+
self.created_at = None
39+
self.updated_at = None
3840
self._url = None
3941
self._successful_url = None
4042
self._acl = None
@@ -226,9 +228,18 @@ def _save_external(self):
226228
}
227229
response = client.post("/files".format(self._name), data)
228230
content = response.json()
231+
229232
self.id = content["objectId"]
233+
230234
self._successful_url = self._url
231235

236+
_created_at = utils.decode_date_string(content.get("createdAt"))
237+
_updated_at = utils.decode_updated_at(content.get("updatedAt"), _created_at)
238+
if _created_at is not None:
239+
self.created_at = _created_at
240+
if _updated_at is not None:
241+
self.updated_at = _updated_at
242+
232243
def _save_to_qcloud(self, token, upload_url):
233244
headers = {
234245
"Authorization": token,
@@ -289,10 +300,19 @@ def _update_data(self, server_data):
289300
if "url" in server_data:
290301
self._url = server_data.get("url")
291302
self._successful_url = self._url
303+
if "key" in server_data:
304+
self.key = server_data.get("key")
292305
if "mime_type" in server_data:
293306
self._mime_type = server_data["mime_type"]
294307
if "metaData" in server_data:
295308
self._metadata = server_data.get("metaData")
309+
310+
_created_at = utils.decode_date_string(server_data.get("createdAt"))
311+
_updated_at = utils.decode_updated_at(server_data.get("updatedAt"), _created_at)
312+
if _created_at is not None:
313+
self.created_at = _created_at
314+
if _updated_at is not None:
315+
self.updated_at = _updated_at
296316

297317
def _get_file_token(self):
298318
data = {

leancloud/object_.py

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -334,25 +334,18 @@ def _to_pointer(self):
334334
}
335335

336336
def _merge_metadata(self, server_data):
337-
for key in ("objectId", "createdAt", "updatedAt"):
338-
if server_data.get(key) is None:
339-
continue
340-
if key == "objectId":
341-
self.id = server_data[key]
342-
else:
343-
if isinstance(server_data[key], six.string_types):
344-
dt = utils.decode(key, {"__type": "Date", "iso": server_data[key]})
345-
elif server_data[key]["__type"] == "Date":
346-
dt = utils.decode(key, server_data[key])
347-
else:
348-
raise TypeError("Invalid date type")
349-
server_data[key] = dt
350-
if key == "createdAt":
351-
self.created_at = dt
352-
elif key == "updatedAt":
353-
self.updated_at = dt
354-
else:
355-
raise TypeError
337+
object_id = server_data.get("objectId")
338+
_created_at = utils.decode_date_string(server_data.get("createdAt"))
339+
_updated_at = utils.decode_updated_at(server_data.get("updatedAt"), _created_at)
340+
341+
if object_id is not None:
342+
self.id = object_id
343+
if _created_at is not None:
344+
self.created_at = _created_at
345+
if _updated_at is not None:
346+
self.updated_at = _updated_at
347+
348+
356349

357350
def validate(self, attrs):
358351
if "ACL" in attrs and not isinstance(attrs["ACL"], leancloud.ACL):
@@ -370,6 +363,23 @@ def get(self, attr, default=None, deafult=None):
370363
# for backward compatibility
371364
if (deafult is not None) and (default is None):
372365
default = deafult
366+
367+
# createdAt is stored as string in the cloud but used as datetime object on the client side.
368+
# We need to make sure that `.created_at` and `.get("createdAt")` return the same value.
369+
# Otherwise users will get confused.
370+
if attr == "createdAt":
371+
if self.created_at is None:
372+
return None
373+
else:
374+
return self.created_at
375+
376+
# Similar to createdAt.
377+
if attr == "updatedAt":
378+
if self.updated_at is None:
379+
return None
380+
else:
381+
return self.updated_at
382+
373383
return self._attributes.get(attr, default)
374384

375385
def relation(self, attr):

leancloud/utils.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,45 @@ def decode(key, value):
137137
if _type == "File":
138138
f = leancloud.File(value["name"])
139139
meta_data = value.get("metaData")
140-
if meta_data:
140+
file_key = value.get("key")
141+
if file_key is not None:
142+
f.key = file_key
143+
if meta_data is not None:
141144
f._metadata = meta_data
142145
f._url = value["url"]
143146
f._successful_url = value["url"]
144147
f.id = value["objectId"]
148+
f.created_at = decode_date_string(value.get("createdAt"))
149+
f.updated_at = decode_date_string(value.get("updatedAt"))
145150
return f
146151

147152

153+
def decode_date_string(date_or_string):
154+
if date_or_string is None:
155+
return None
156+
elif isinstance(date_or_string, six.string_types):
157+
return decode_date_string({"__type": "Date", "iso": date_or_string})
158+
elif date_or_string["__type"] == "Date":
159+
return arrow.get(iso8601.parse_date(date_or_string["iso"])).to("local").datetime
160+
else:
161+
raise TypeError("Invalid date type")
162+
163+
164+
def decode_updated_at(updated_at_date_string, created_at_datetime):
165+
updated_at = decode_date_string(updated_at_date_string)
166+
if updated_at is None:
167+
if created_at_datetime is None:
168+
return None
169+
else:
170+
# When a new object is created, updatedAt will be set as the same value as createdAt on the cloud.
171+
# However, the cloud will only return objectId and createdAt in REST API response, without updatedAt.
172+
# Thus we need to set updatedAt as the same value as createdAt, consistent with the value on the cloud.
173+
# This behaviour is consistent with other SDKs such as JavaScript and Go.
174+
return created_at_datetime
175+
else:
176+
return updated_at
177+
178+
148179
def traverse_object(obj, callback, seen=None):
149180
seen = seen or set()
150181

tests/test_file.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ def test_save(): # type: () -> None
108108
assert f.name == "Blah.txt"
109109
assert f.mime_type == "text/plain"
110110
assert not f.url.endswith(".")
111+
assert f.created_at == f.updated_at
111112

112113

113114
@with_setup(setup_func)
@@ -118,6 +119,7 @@ def test_save_with_specified_key(): # type: () -> None
118119
f.save()
119120

120121
assert f.id
122+
assert f.created_at == f.updated_at
121123
assert f.name == "Blah.txt"
122124
assert f.mime_type == "text/plain"
123125
path = urlparse(f.url).path
@@ -146,12 +148,31 @@ def test_query(): # type: () -> None
146148
files = leancloud.Query("File").find()
147149
for f in files:
148150
assert isinstance(f, File)
151+
assert f.id
149152
assert f.url
150153
assert f.name
151154
assert f.metadata
155+
assert f.created_at == f.updated_at
156+
if f.metadata.get("__source") == 'external':
157+
assert f.url
158+
else:
159+
assert f.key
152160

153161
assert isinstance(leancloud.File.query.first(), File)
154162

163+
@with_setup(setup_func)
164+
def test_scan(): # type: () -> None
165+
files = leancloud.Query("File").scan()
166+
for f in files:
167+
assert isinstance(f, File)
168+
assert f.created_at == f.updated_at
169+
assert f.name
170+
assert f.metadata
171+
if f.metadata.get("__source") == 'external':
172+
assert f.url
173+
else:
174+
assert f.key
175+
155176

156177
@with_setup(setup_func)
157178
def test_save_external(): # type: () -> None
@@ -160,6 +181,7 @@ def test_save_external(): # type: () -> None
160181
f = File.create_with_url(file_name, file_url)
161182
f.save()
162183
assert f.id
184+
assert f.created_at == f.updated_at
163185
file_on_cloud = File.create_without_data(f.id)
164186
file_on_cloud.fetch()
165187
assert file_on_cloud.name == file_name

tests/test_object.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ def test_fetch_when_save(): # type: () -> None
314314
foo = Foo()
315315
foo.set("counter", 1)
316316
foo.save()
317+
assert foo.created_at == foo.updated_at
317318
assert foo.get("counter") == 1
318319

319320
foo_from_other_thread = leancloud.Query(Foo).get(foo.id)

0 commit comments

Comments
 (0)