Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions dbt/adapters/dremio/api/rest/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ def _check_error(response, details=""):
except: # NOQA
return response.text
if code == 400:
# Dremio Cloud returns 400 for an existing folder/object, whereas
# Software returns 409. Normalize so callers can catch a single
# DremioAlreadyExistsException.
if "An object already exists with that name" in response.text:
raise DremioAlreadyExistsException(
"Already exists:" + details, error, response
)
raise DremioBadRequestException("Bad request:" + details, error,
response)
if code == 401:
Expand Down
26 changes: 24 additions & 2 deletions tests/unit/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
# limitations under the License.

import pytest
from unittest.mock import patch
from unittest.mock import MagicMock, patch
from dbt_common.exceptions import DbtRuntimeError
from dbt.adapters.dremio.api.rest.error import DremioRequestTimeoutException
from dbt.adapters.dremio.api.rest.error import (
DremioAlreadyExistsException,
DremioRequestTimeoutException,
)
from dbt.adapters.dremio.connections import DremioConnectionManager


Expand Down Expand Up @@ -46,3 +49,22 @@ def test_connection_retry(

# Assert
assert mocked_post_func.call_count == TOTAL_CONNECTION_ATTEMPTS


class TestCreateFolders:
def test_create_folders_swallows_already_exists(self):
# Guards the integration path: _check_error normalizes Cloud's
# 400 "already exists" to DremioAlreadyExistsException, which the
# existing handler in _create_folders is responsible for swallowing.
mgr = DremioConnectionManager.__new__(DremioConnectionManager)
mgr._make_new_folder_json = MagicMock(return_value="{}")
rest_client = MagicMock()
rest_client.create_catalog_api.side_effect = DremioAlreadyExistsException(
msg="Already exists:",
original_exception="400 Client Error: Bad Request",
)

mgr._create_folders("mySource", "staging.subfolder", rest_client)

# Both folders in the path attempted; both swallowed
assert rest_client.create_catalog_api.call_count == 2
61 changes: 61 additions & 0 deletions tests/unit/test_rest_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Copyright (C) 2022 Dremio Corporation
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json
from unittest.mock import MagicMock

import pytest

from dbt.adapters.dremio.api.rest.error import (
DremioAlreadyExistsException,
DremioBadRequestException,
)
from dbt.adapters.dremio.api.rest.utils import _check_error


_REASON_BY_STATUS = {400: "Bad Request", 409: "Conflict"}


def _make_response(status_code, body_text):
response = MagicMock()
response.status_code = status_code
response.reason = _REASON_BY_STATUS.get(status_code, "Bad Request")
response.url = "https://api.dremio.cloud/v0/projects/test/catalog"
response.text = body_text
return response


class TestCheckErrorAlreadyExists:
def test_cloud_400_already_exists_routes_to_already_exists_exception(self):
body = json.dumps(
{
"errorMessage": (
"Unable to create folder staging on source mySource. "
"An object already exists with that name."
),
"moreInfo": "",
}
)
response = _make_response(400, body)
with pytest.raises(DremioAlreadyExistsException):
_check_error(response)

def test_400_other_message_still_raises_bad_request(self):
body = json.dumps({"errorMessage": "Some other 400 error", "moreInfo": ""})
response = _make_response(400, body)
with pytest.raises(DremioBadRequestException):
_check_error(response)

def test_409_still_raises_already_exists(self):
body = json.dumps({"errorMessage": "Already exists", "moreInfo": ""})
response = _make_response(409, body)
with pytest.raises(DremioAlreadyExistsException):
_check_error(response)
Loading