From a4a0cc5d3be9af7e72aab6b30140d423fe0e8e4b Mon Sep 17 00:00:00 2001 From: Olzhas Arystanov Date: Tue, 31 Dec 2024 17:29:29 +0100 Subject: [PATCH] Migrate to B2 native API v3 --- b2sdk/_internal/raw_api.py | 2 +- b2sdk/_internal/raw_simulator.py | 21 +++++++++++++----- b2sdk/_internal/session.py | 21 +++++++++++++----- b2sdk/v1/session.py | 19 +++++++++++----- changelog.d/+migrate_to_b2_v3_api.changed.md | 1 + test/integration/test_raw_api.py | 15 ++++++++----- test/integration/test_upload.py | 2 +- test/unit/fixtures/raw_api.py | 23 +++++++++++--------- test/unit/v_all/test_api.py | 2 +- 9 files changed, 70 insertions(+), 36 deletions(-) create mode 100644 changelog.d/+migrate_to_b2_v3_api.changed.md diff --git a/b2sdk/_internal/raw_api.py b/b2sdk/_internal/raw_api.py index 12bb36de..4b820740 100644 --- a/b2sdk/_internal/raw_api.py +++ b/b2sdk/_internal/raw_api.py @@ -79,7 +79,7 @@ ] # API version number to use when calling the service -API_VERSION = 'v2' +API_VERSION = 'v3' logger = getLogger(__name__) diff --git a/b2sdk/_internal/raw_simulator.py b/b2sdk/_internal/raw_simulator.py index 75623b36..cb455021 100644 --- a/b2sdk/_internal/raw_simulator.py +++ b/b2sdk/_internal/raw_simulator.py @@ -1407,12 +1407,21 @@ def authorize_account(self, realm_url, application_key_id, application_key): return dict( accountId=key_sim.account_id, authorizationToken=auth_token, - apiUrl=self.API_URL, - downloadUrl=self.DOWNLOAD_URL, - recommendedPartSize=self.MIN_PART_SIZE, - absoluteMinimumPartSize=self.MIN_PART_SIZE, - allowed=allowed, - s3ApiUrl=self.S3_API_URL, + apiInfo=dict( + groupsApi=dict(), + storageApi=dict( + apiUrl=self.API_URL, + downloadUrl=self.DOWNLOAD_URL, + recommendedPartSize=self.MIN_PART_SIZE, + absoluteMinimumPartSize=self.MIN_PART_SIZE, + allowed=allowed, + s3ApiUrl=self.S3_API_URL, + bucketId=allowed['bucketId'], + bucketName=allowed['bucketName'], + capabilities=allowed['capabilities'], + namePrefix=allowed['namePrefix'], + ), + ), ) def cancel_large_file(self, api_url, account_auth_token, file_id): diff --git a/b2sdk/_internal/session.py b/b2sdk/_internal/session.py index 110e0fa1..82074498 100644 --- a/b2sdk/_internal/session.py +++ b/b2sdk/_internal/session.py @@ -116,7 +116,16 @@ def authorize_account(self, realm, application_key_id, application_key): realm_url = REALM_URLS.get(realm, realm) response = self.raw_api.authorize_account(realm_url, application_key_id, application_key) account_id = response['accountId'] - allowed = response['allowed'] + storage_api_info = response['apiInfo']['storageApi'] + + # `allowed` object has been deprecated in the v3 of the API, but we still + # construct it artificially to avoid changes in all the reliant parts. + allowed = { + 'bucketId': storage_api_info['bucketId'], + 'bucketName': storage_api_info['bucketName'], + 'capabilities': storage_api_info['capabilities'], + 'namePrefix': storage_api_info['namePrefix'], + } # Clear the cache if new account has been used if not self.account_info.is_same_account(account_id, realm): @@ -126,13 +135,13 @@ def authorize_account(self, realm, application_key_id, application_key): self.account_info.set_auth_data( account_id=account_id, auth_token=response['authorizationToken'], - api_url=response['apiUrl'], - download_url=response['downloadUrl'], - absolute_minimum_part_size=response['absoluteMinimumPartSize'], - recommended_part_size=response['recommendedPartSize'], + api_url=storage_api_info['apiUrl'], + download_url=storage_api_info['downloadUrl'], + absolute_minimum_part_size=storage_api_info['absoluteMinimumPartSize'], + recommended_part_size=storage_api_info['recommendedPartSize'], application_key=application_key, realm=realm, - s3_api_url=response['s3ApiUrl'], + s3_api_url=storage_api_info['s3ApiUrl'], allowed=allowed, application_key_id=application_key_id, ) diff --git a/b2sdk/v1/session.py b/b2sdk/v1/session.py index 673605c3..075feab8 100644 --- a/b2sdk/v1/session.py +++ b/b2sdk/v1/session.py @@ -49,7 +49,16 @@ def authorize_account(self, realm, application_key_id, application_key): realm_url = self.account_info.REALM_URLS.get(realm, realm) response = self.raw_api.authorize_account(realm_url, application_key_id, application_key) account_id = response['accountId'] - allowed = response['allowed'] + storage_api_info = response['apiInfo']['storageApi'] + + # `allowed` object has been deprecated in the v3 of the API, but we still + # construct it artificially to avoid changes in all the reliant parts. + allowed = { + 'bucketId': storage_api_info['bucketId'], + 'bucketName': storage_api_info['bucketName'], + 'capabilities': storage_api_info['capabilities'], + 'namePrefix': storage_api_info['namePrefix'], + } # Clear the cache if new account has been used if not self.account_info.is_same_account(account_id, realm): @@ -59,12 +68,12 @@ def authorize_account(self, realm, application_key_id, application_key): self.account_info.set_auth_data( account_id=account_id, auth_token=response['authorizationToken'], - api_url=response['apiUrl'], - download_url=response['downloadUrl'], - minimum_part_size=response['recommendedPartSize'], + api_url=storage_api_info['apiUrl'], + download_url=storage_api_info['downloadUrl'], + minimum_part_size=storage_api_info['recommendedPartSize'], application_key=application_key, realm=realm, - s3_api_url=response['s3ApiUrl'], + s3_api_url=storage_api_info['s3ApiUrl'], allowed=allowed, application_key_id=application_key_id, ) diff --git a/changelog.d/+migrate_to_b2_v3_api.changed.md b/changelog.d/+migrate_to_b2_v3_api.changed.md new file mode 100644 index 00000000..e8cf1ae5 --- /dev/null +++ b/changelog.d/+migrate_to_b2_v3_api.changed.md @@ -0,0 +1 @@ +Migrate to B2 Native API v3. diff --git a/test/integration/test_raw_api.py b/test/integration/test_raw_api.py index 2ed20519..8dcdf4e2 100644 --- a/test/integration/test_raw_api.py +++ b/test/integration/test_raw_api.py @@ -119,14 +119,14 @@ def raw_api_test_helper(raw_api, should_cleanup_old_buckets): set(ALL_CAPABILITIES) - {'readBuckets', 'listAllBucketNames'} - preview_feature_caps - - set(auth_dict['allowed']['capabilities']) + - set(auth_dict['apiInfo']['storageApi']['capabilities']) ) assert not missing_capabilities, f'it appears that the raw_api integration test is being run with a non-full key. Missing capabilities: {missing_capabilities}' account_id = auth_dict['accountId'] account_auth_token = auth_dict['authorizationToken'] - api_url = auth_dict['apiUrl'] - download_url = auth_dict['downloadUrl'] + api_url = auth_dict['apiInfo']['storageApi']['apiUrl'] + download_url = auth_dict['apiInfo']['storageApi']['downloadUrl'] # b2_create_key print('b2_create_key') @@ -599,7 +599,7 @@ def raw_api_test_helper(raw_api, should_cleanup_old_buckets): def _subtest_bucket_notification_rules(raw_api, auth_dict, api_url, account_auth_token, bucket_id): - if 'writeBucketNotifications' not in auth_dict['allowed']['capabilities']: + if 'writeBucketNotifications' not in auth_dict['apiInfo']['storageApi']['capabilities']: pytest.skip('Test account does not have writeBucketNotifications capability') notification_rule = { @@ -644,8 +644,11 @@ def _subtest_bucket_notification_rules(raw_api, auth_dict, api_url, account_auth def cleanup_old_buckets(): raw_api = B2RawHTTPApi(B2Http()) auth_dict = authorize_raw_api(raw_api) + bucket_list_dict = raw_api.list_buckets( - auth_dict['apiUrl'], auth_dict['authorizationToken'], auth_dict['accountId'] + auth_dict['apiInfo']['storageApi']['apiUrl'], + auth_dict['authorizationToken'], + auth_dict['accountId'], ) _cleanup_old_buckets(raw_api, auth_dict, bucket_list_dict) @@ -658,7 +661,7 @@ def _cleanup_old_buckets(raw_api, auth_dict, bucket_list_dict): print('cleaning up old bucket: ' + bucket_name) _clean_and_delete_bucket( raw_api, - auth_dict['apiUrl'], + auth_dict['apiInfo']['storageApi']['apiUrl'], auth_dict['authorizationToken'], auth_dict['accountId'], bucket_id, diff --git a/test/integration/test_upload.py b/test/integration/test_upload.py index 1dba4986..1341b129 100644 --- a/test/integration/test_upload.py +++ b/test/integration/test_upload.py @@ -57,7 +57,7 @@ def test_ssec_key_id(self): auth_dict = authorize_raw_api(raw_api) account_auth_token = auth_dict['authorizationToken'] - api_url = auth_dict['apiUrl'] + api_url = auth_dict['apiInfo']['storageApi']['apiUrl'] bucket = self.create_bucket() large_info = raw_api.start_large_file( diff --git a/test/unit/fixtures/raw_api.py b/test/unit/fixtures/raw_api.py index 1aa434aa..1495fca1 100644 --- a/test/unit/fixtures/raw_api.py +++ b/test/unit/fixtures/raw_api.py @@ -19,19 +19,22 @@ def fake_b2_raw_api_responses(): return { 'authorize_account': { - 'absoluteMinimumPartSize': 5000000, 'accountId': '6012deadbeef', - 'allowed': { - 'bucketId': None, - 'bucketName': None, - 'capabilities': copy(ALL_CAPABILITIES), - 'namePrefix': None, + 'apiInfo': { + 'groupsApi': {}, + 'storageApi': { + 'bucketId': None, + 'bucketName': None, + 'capabilities': copy(ALL_CAPABILITIES), + 'namePrefix': None, + 'downloadUrl': 'https://f000.backblazeb2.xyz:8180', + 'absoluteMinimumPartSize': 5000000, + 'recommendedPartSize': 100000000, + 'apiUrl': 'https://api000.backblazeb2.xyz:8180', + 's3ApiUrl': 'https://s3.us-west-000.backblazeb2.xyz:8180', + }, }, - 'apiUrl': 'https://api000.backblazeb2.xyz:8180', 'authorizationToken': '4_1111111111111111111111111_11111111_111111_1111_1111111111111_1111_11111111=', - 'downloadUrl': 'https://f000.backblazeb2.xyz:8180', - 'recommendedPartSize': 100000000, - 's3ApiUrl': 'https://s3.us-west-000.backblazeb2.xyz:8180', } } diff --git a/test/unit/v_all/test_api.py b/test/unit/v_all/test_api.py index e113dc20..409fb785 100644 --- a/test/unit/v_all/test_api.py +++ b/test/unit/v_all/test_api.py @@ -160,5 +160,5 @@ def test_get_download_url_for_fileid(self): assert ( download_url - == 'http://download.example.com/b2api/v2/b2_download_file_by_id?fileId=file-id' + == 'http://download.example.com/b2api/v3/b2_download_file_by_id?fileId=file-id' )