Skip to content

Commit 711f20e

Browse files
Johnetordofffelliott
authored andcommitted
update onedrive to use new revisions endpoint
* Update OneDrive provider's revisions method to use a new endpoint that works with all account types and change how OneNote versioning behaves to accommodate that. OD revisions were using the viewdelta endpoint which identifies revisions by etag. Since initial development, Microsoft has added actual file versioning to OD. Revisions are not supported for OneNote files, so create a dummy 'current' version and return that. * Move fixtures and adjust tests.
1 parent 5deb281 commit 711f20e

File tree

6 files changed

+115
-84
lines changed

6 files changed

+115
-84
lines changed

tests/providers/onedrive/fixtures/download.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
{
22
"root_id": "F4D50E400DFE7D4E!103",
33
"file_id": "F4D50E400DFE7D4E!291",
4-
"file_revision": "aRjRENTBFNDAwREZFN0Q0RSEyOTEuMg",
54
"onenote_id": "F4D50E400DFE7D4E!154",
65
"onenote_revision": "aRjRENTBFNDAwREZFN0Q0RSExNTQuMg",
6+
"onenote_revision_non_exportable": "F4D50E400DFE7D4E!154",
77
"file_download_url" : "https://public.bn1303.livefilestore.com/y4mB8JhDUWbofzVglNap3rO5i6R7jOQyJAz995dPlkrOiQeOV2jgK-EOf916z8YHi9A42WCTMVfNmHjJliYLccUFzJgsEK3j3cviT2YLlZBMRVN-sC0mfvZz_ZeDgiLzfSChMmNXkRoq6Ymh_F8r8jRAvZTzJOgyX3F7jdw4qcY27tz95Rutrl68W0Z8ntuh3bVoPIDHC5kckF8sSWoyv5j4BfRQCckjyrmaV8F1BM5Cb1x10WNdE7CP_X1bBFqY7ZTJzYcsQcDR07BdalvRTDp-A",
8-
"file_revision_download_url": "https://kdqyaa.bn1303.livefilestore.com/y4pIg3zgkmcBaQ_b2CpkxiYLihuF-GaqI-zBWermrthafHBogxMCjK6Q2qA_DoELVL0-oogK2WBYfx_CKyjynFBkRe61e6OsMDAfn0NEo4fSLSXamfrMRZ0-Pyf8ZgUujCNHpaihbkj2hwIlvnNJez0ZDerAEdfA7jos7JQnVfEAU2GNXGnsyx9Yrn9VC72xLmeMdDh676UTL9gpG-2xj4BX1AI2Ro7phbbB1n2kwnwNRZOaK1tusL8cyUfjs7joD4X",
98
"file_content": "ten of them",
109
"file_revisions": {
1110
"value" : [
Lines changed: 70 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,71 @@
11
{
2-
"root_id": "F4D50E400DFE7D4E!103",
3-
"file_id": "F4D50E400DFE7D4E!291",
4-
"file_revisions": {
5-
"value" : [
6-
{
7-
"@content.downloadUrl" : "https://kdqyaa.bn1303.livefilestore.com/y4pIg3zgkmcBaQ_b2CpkxiYLihuF-GaqI-zBWermrthafHBogxMCjK6Q2qA_DoELVL0-oogK2WBYfx_CKyjynFBkRe61e6OsMDAfn0NEo4fSLSXamfrMRZ0-Pyf8ZgUujCNHpaihbkj2hwIlvnNJez0ZDerAEdfA7jos7JQnVfEAU2GNXGnsyx9Yrn9VC72xLmeMdDh676UTL9gpG-2xj4BX1AI2Ro7phbbB1n2kwnwNRZOaK1tusL8cyUfjs7joD4X",
8-
"createdDateTime" : "2017-08-17T17:49:39.613Z",
9-
"fileSystemInfo" : {
10-
"lastModifiedDateTime" : "2017-08-17T17:49:50.363Z",
11-
"createdDateTime" : "2017-08-17T17:49:39.613Z"
12-
},
13-
"name" : "toes.txt",
14-
"size" : 11,
15-
"eTag" : "aRjRENTBFNDAwREZFN0Q0RSEyOTEuMg",
16-
"cTag" : "aYzpGNEQ1MEU0MDBERkU3RDRFITI5MS4yNTg",
17-
"createdBy" : {
18-
"user" : {
19-
"displayName" : "Fitz Elliott",
20-
"id" : "f4d50e400dfe7d4e"
21-
},
22-
"application" : {
23-
"id" : "481710a4"
24-
}
25-
},
26-
"lastModifiedDateTime" : "2017-08-17T17:49:50.38Z",
27-
"file" : {
28-
"mimeType" : "text/plain",
29-
"hashes" : {
30-
"sha1Hash" : "D6FAC576DCF80198874C9C9476F021AF3F12688C"
31-
}
32-
},
33-
"webUrl" : "https://1drv.ms/t/s!AE59_g1ADtX0giM",
34-
"id" : "F4D50E400DFE7D4E!291",
35-
"parentReference" : {
36-
"id" : "F4D50E400DFE7D4E!103",
37-
"name" : "root:",
38-
"path" : "/drive/root:",
39-
"driveId" : "f4d50e400dfe7d4e"
40-
},
41-
"lastModifiedBy" : {
42-
"user" : {
43-
"id" : "f4d50e400dfe7d4e",
44-
"displayName" : "Fitz Elliott"
45-
},
46-
"application" : {
47-
"id" : "481710a4"
48-
}
49-
}
50-
}
51-
],
52-
"@odata.context" : "https://api.onedrive.com/v1.0/$metadata#drives('me')/items",
53-
"@odata.deltaLink" : "https://api.onedrive.com/v1.0/drives('me')/items('F4D50E400DFE7D4E!291')/view.delta?$top=250&token=aTE09NjM2Mzg1OTM3MTAzMzc7SUQ9RjRENTBFNDAwREZFN0Q0RSEyOTE7TFI9NjM2Mzg5MTk0MDUyMTA7RVA9MTY7U0k9ODA7U0c9MTtTTz0yO1BJPTM",
54-
"@delta.token" : "aTE09NjM2Mzg1OTM3MTAzMzc7SUQ9RjRENTBFNDAwREZFN0Q0RSEyOTE7TFI9NjM2Mzg5MTk0MDUyMTA7RVA9MTY7U0k9ODA7U0c9MTtTTz0yO1BJPTM"
55-
}
56-
}
2+
"root_id":"F4D50E400DFE7D4E!103",
3+
"file_id":"F4D50E400DFE7D4E!291",
4+
"revision_id":"107!70",
5+
"file_revision_download_url": "https://public.ch.files.1drv.com/y4mMH_4HkyaoHu8_0kFg_Q4bTRlfZiB9OyWlZU6ZFe3mLCFm-r2F5YWV9nOmvvF9I04mT2cnK8S5YZP5cgPtM3jAIcoPugtolMMCwFWf3_ZC_cXcsDWN4gZAqhf07TFoIqvebghUGwEYBwPAnrJ0kM0sa8mrf4eQqK8lEjwXOHnk-FvvPqyLJca1u6_x_cRNtV_wLsM-P97TRWJGqUFKY4uwRofjycO-HfdUm5cOtSHWaHBrymv8dc485fEVq1RVSf2-0Rs1_tl0oqDEaH2An0DgQ",
6+
"file_revisions":{
7+
"@odata.context":"https://api.onedrive.com/v1.0/$metadata#drives('me'/items('81ACFE813AEB6C20%213910')/versions",
8+
"value":[
9+
{
10+
"@content.downloadUrl":"https://public.ch.files.1drv.com/y4mdCMJd_GU7BsM49MAk6eaWJLcAahSXbD3ACWHlLw6xBLRVhZxH8d-4HKIsq3Qr5eVqIadN0fUaltRxSUsREoQ9NY9aw-FWGQAJDpytApMZIkHQYE02m_pRHfSLVTJAlL-2t8N3MVz_8GL1Zfhs1rcRWEmQUX_159Xaj1FYJLv13BSJSQJOv677x_jZQiZSSgvr-WnjyvMtoH2ZzMUAJxevHFHvFDduP266QhSWl8kX_feLRyxnhG96-gtbNeJbwJ_",
11+
"id":"current",
12+
"lastModifiedBy":{
13+
"user":{
14+
"displayName":"John Tordoff",
15+
"id":"81acfe813aeb6c20"
16+
}
17+
},
18+
"lastModifiedDateTime":"2017-11-30T15:42:33.447Z",
19+
"size":12
20+
},
21+
{
22+
"@content.downloadUrl":"https://public.ch.files.1drv.com/y4mMH_4HkyaoHu8_0kFg_Q4bTRlfZiB9OyWlZU6ZFe3mLCFm-r2F5YWV9nOmvvF9I04mT2cnK8S5YZP5cgPtM3jAIcoPugtolMMCwFWf3_ZC_cXcsDWN4gZAqhf07TFoIqvebghUGwEYBwPAnrJ0kM0sa8mrf4eQqK8lEjwXOHnk-FvvPqyLJca1u6_x_cRNtV_wLsM-P97TRWJGqUFKY4uwRofjycO-HfdUm5cOtSHWaHBrymv8dc485fEVq1RVSf2-0Rs1_tl0oqDEaH2An0DgQ",
23+
"id":"107!70",
24+
"lastModifiedBy":{
25+
"user":{
26+
"displayName":"John Tordoff",
27+
"id":"81acfe813aeb6c20"
28+
}
29+
},
30+
"lastModifiedDateTime":"2017-11-30T15:15:04.453Z",
31+
"size":6
32+
},
33+
{
34+
"@content.downloadUrl":"https://public.ch.files.1drv.com/y4mrCC1HUStA2PX-L6-pKFuekc9sezEnTqwymnJUtP7kkVXrckReyMUo7I-fz-lR-dTvnZP0gFsE5h6KOV1jwrXqlklo0fw7PITKJ3h_xnox6M97jysra8eIml1uTJrnAKWCDkuC2PL0cwoVZ0Tzwbg6E7Wc3MeIPhdwVLHFEy36QJiOn9ISq12KfATPG-kc6utS31RQlWgGd5BThVyFRsJ2RHW5vT78BfGUYijxNPZEjE-MEQRkjasqcHMnE4s2p_KrcYT6fecXpdPsRrm6ysOMA",
35+
"id":"104!70",
36+
"lastModifiedBy":{
37+
"user":{
38+
"displayName":"John Tordoff",
39+
"id":"81acfe813aeb6c20"
40+
}
41+
},
42+
"lastModifiedDateTime":"2017-11-30T15:13:18.643Z",
43+
"size":5
44+
},
45+
{
46+
"@content.downloadUrl":"https://public.ch.files.1drv.com/y4m26zI4Ycikb_c683UBuLdD7WU85eH3lk_ikfWZXrG5q-z16tFB8Y83ERSc_LcsboC2fMIbVZ19xdESP151xmi7kYZpfy0yzCPzvMwS9wF__m6Uu0Lycq3Ocq3Yb1MSphHD7cZn1foExV9cdYyyVoizLXSx-XHu3nTRUuDjZZ13jx7OlTsvEoCkZhJPO_G1yWBTuPpewHQCh5ayG-4g_srhx1dYV1dL5r4e2x-1Oxz4rRdQNiFAHLX-tcvX07_bBD95jNuiEcn6pNAFAQCvR_iXw",
47+
"id":"102!70",
48+
"lastModifiedBy":{
49+
"user":{
50+
"displayName":"John Tordoff",
51+
"id":"81acfe813aeb6c20"
52+
}
53+
},
54+
"lastModifiedDateTime":"2017-11-30T15:08:32.893Z",
55+
"size":4
56+
},
57+
{
58+
"@content.downloadUrl":"https://public.ch.files.1drv.com/y4mE4L0ez-GhBbcx-GwiM25Th2xGinltbyKDbZu1f5t6eEDHNOwlLS0RnUJW2u5QDL5IOga1sEt57y7V53hheTdxcPGJTZIaVoSgaRWW2sBprhdqc-o36QAgY104aEMurXqbPGtxYAA-gWu3Ol7qjHKc7ykC2v5kF_JzIuIRqoprrRZvwy1of_d-czQfejZZ3k50Q734Alc_jO-X5ZpmwwUndM3twvzfCwgzynCkujSqtzffdSOrDYuarhFtqOVas_i95-MgJegeIbAiAalPDHfog",
59+
"id":"101!70",
60+
"lastModifiedBy":{
61+
"user":{
62+
"displayName":"John Tordoff",
63+
"id":"81acfe813aeb6c20"
64+
}
65+
},
66+
"lastModifiedDateTime":"2017-11-30T15:08:22.987Z",
67+
"size":3
68+
}
69+
]
70+
}
71+
}

tests/providers/onedrive/test_metadata.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ def test_build_revision_metadata(self, revision_fixtures):
7070

7171
assert metadata.serialized() == {
7272
'extra': {},
73-
'version': 'aRjRENTBFNDAwREZFN0Q0RSEyOTEuMg',
74-
'modified': '2017-08-17T17:49:50.38Z',
75-
'modified_utc': '2017-08-17T17:49:50+00:00',
73+
'version': 'current',
74+
'modified': '2017-11-30T15:42:33.447Z',
75+
'modified_utc': '2017-11-30T15:42:33+00:00',
7676
'versionIdentifier': 'revision',
7777
}

tests/providers/onedrive/test_provider.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -409,13 +409,12 @@ async def test_get_revisions(self, provider, revision_fixtures):
409409
path = OneDrivePath('/bicuspids.txt', _ids=[revision_fixtures['root_id'], file_id])
410410

411411
revision_response = revision_fixtures['file_revisions']
412-
revisions_url = provider._build_drive_url('items', file_id, 'view.delta',
413-
top=provider.MAX_REVISIONS)
412+
revisions_url = provider._build_drive_url(*path.api_identifier, 'versions')
414413
aiohttpretty.register_json_uri('GET', revisions_url, body=revision_response)
415414

416415
result = await provider.revisions(path)
417416

418-
assert len(result) == 1
417+
assert len(result) == 5
419418

420419

421420
class TestDownload:
@@ -440,20 +439,19 @@ async def test_download_standard_file(self, provider, download_fixtures):
440439

441440
@pytest.mark.asyncio
442441
@pytest.mark.aiohttpretty
443-
async def test_download_by_revision(self, provider, download_fixtures):
442+
async def test_download_by_revision(self, provider, download_fixtures, revision_fixtures):
444443
file_id = download_fixtures['file_id']
445444
path = OneDrivePath('/toes.txt', _ids=[download_fixtures['root_id'], file_id])
446445

447-
revision_response = download_fixtures['file_revisions']
448-
revisions_url = provider._build_drive_url('items', file_id, 'view.delta',
449-
top=provider.MAX_REVISIONS)
446+
revision_response = revision_fixtures['file_revisions']
447+
revisions_url = provider._build_drive_url(*path.api_identifier, 'versions')
450448
aiohttpretty.register_json_uri('GET', revisions_url, body=revision_response)
451449

452-
aiohttpretty.register_uri('GET', download_fixtures['file_revision_download_url'],
450+
aiohttpretty.register_uri('GET', revision_fixtures['file_revision_download_url'],
453451
body=download_fixtures['file_content'],
454452
headers={'Content-Length': '11'})
455453

456-
response = await provider.download(path, revision=download_fixtures['file_revision'])
454+
response = await provider.download(path, revision=revision_fixtures['revision_id'])
457455
content = await response.read()
458456
assert content == b'ten of them'
459457

@@ -470,8 +468,7 @@ async def test_download_by_bad_revision(self, provider, download_fixtures):
470468
path = OneDrivePath('/toes.txt', _ids=[download_fixtures['root_id'], file_id])
471469

472470
revision_response = download_fixtures['file_revisions']
473-
revisions_url = provider._build_drive_url('items', file_id, 'view.delta',
474-
top=provider.MAX_REVISIONS)
471+
revisions_url = provider._build_drive_url(*path.api_identifier, 'versions')
475472
aiohttpretty.register_json_uri('GET', revisions_url, body=revision_response)
476473

477474
with pytest.raises(exceptions.NotFoundError) as exc:
@@ -497,12 +494,12 @@ async def test_download_unexportable_by_revision(self, provider, download_fixtur
497494
path = OneDrivePath('/onenote', _ids=[download_fixtures['root_id'], onenote_id])
498495

499496
revision_response = download_fixtures['onenote_revisions']
500-
revisions_url = provider._build_drive_url('items', onenote_id, 'view.delta',
501-
top=provider.MAX_REVISIONS)
497+
revisions_url = provider._build_drive_url('items', onenote_id, 'versions')
502498
aiohttpretty.register_json_uri('GET', revisions_url, body=revision_response)
503499

504500
with pytest.raises(exceptions.UnexportableFileTypeError) as exc:
505-
await provider.download(path, revision=download_fixtures['onenote_revision'])
501+
await provider.download(path,
502+
revision=download_fixtures['onenote_revision_non_exportable'])
506503

507504

508505
class TestReadOnlyProvider:

waterbutler/providers/onedrive/metadata.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def version_identifier(self):
114114

115115
@property
116116
def version(self):
117-
return self.raw['eTag']
117+
return self.raw['id']
118118

119119
@property
120120
def modified(self):

waterbutler/providers/onedrive/provider.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,6 @@ async def revisions(self, # type: ignore
288288
return [
289289
OneDriveRevisionMetadata(item)
290290
for item in data['value']
291-
if not item.get('deleted')
292291
]
293292

294293
async def download(self, # type: ignore
@@ -318,7 +317,7 @@ async def download(self, # type: ignore
318317
if revision:
319318
items = await self._revisions_json(path)
320319
for item in items['value']:
321-
if item['eTag'] == revision:
320+
if item['id'] == revision:
322321
try:
323322
download_url = item['@content.downloadUrl']
324323
except KeyError:
@@ -413,19 +412,40 @@ def _construct_metadata(self, data: dict):
413412
async def _revisions_json(self, path: OneDrivePath, **kwargs) -> dict:
414413
"""Fetch a list of revisions for the file at ``path``.
415414
416-
API docs: https://dev.onedrive.com/items/view_delta.htm
415+
API docs: https://docs.microsoft.com/en-us/onedrive/developer/rest-api/resources/driveitemversion
417416
418417
:param OneDrivePath path: the path of the file to get revisions for
419418
:rtype: dict
420419
:return: list of revision metadata under a ``value`` key
421420
"""
421+
try:
422+
resp = await self.make_request(
423+
'GET',
424+
self._build_drive_url(*path.api_identifier, 'versions'),
425+
expects=(200, ),
426+
throws=exceptions.RevisionsError
427+
)
428+
except exceptions.RevisionsError as exc:
429+
if (
430+
exc.code == 405 and
431+
exc.data['error']['message'] == 'Item does not match expected type'
432+
):
433+
# OneNote versioning not supported, instead return a lone revision called 'current'
434+
url = self._build_drive_url(*path.api_identifier)
435+
resp = await self.make_request(
436+
'GET',
437+
url,
438+
expects=(200,),
439+
throws=exceptions.MetadataError
440+
)
441+
logger.debug("metadata resp::{}".format(repr(resp)))
442+
data = await resp.json()
443+
return {'value': [
444+
{'id': 'current', 'lastModifiedDateTime': data['lastModifiedDateTime']},
445+
]} # fudge a fake revision response
446+
else:
447+
raise exc
422448

423-
resp = await self.make_request(
424-
'GET',
425-
self._build_drive_url(*path.api_identifier, 'view.delta', top=self.MAX_REVISIONS),
426-
expects=(200, ),
427-
throws=exceptions.RevisionsError
428-
)
429449
logger.debug('_revisions_json: resp::{}'.format(repr(resp)))
430450
data = await resp.json()
431451
logger.debug('_revisions_json: data::{}'.format(json.dumps(data)))

0 commit comments

Comments
 (0)