Skip to content
Open
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
1 change: 1 addition & 0 deletions newsfragments/3090.change.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ``QuotaExceededError``, ``LicenseCheckFailedError``, and ``AuthorizationFailedError`` for documented VuMark Generation API result codes.
3 changes: 3 additions & 0 deletions spelling_private_dict.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
AuthenticationFailure
AuthorizationFailed
BadImage
ConnectionErrorPossiblyImageTooLarge
DateRangeError
ImageTooLarge
InactiveProject
JSONDecodeError
LicenseCheckFailed
MatchProcessing
MaxNumResultsOutOfRange
MetadataTooLarge
Expand All @@ -13,6 +15,7 @@ OopsAnErrorOccurredPossiblyBadNameError
ProjectHasNoAPIAccess
ProjectInactive
ProjectSuspended
QuotaExceeded
RequestQuotaReached
RequestTimeTooSkewed
TargetNameExist
Expand Down
6 changes: 6 additions & 0 deletions src/vws/async_vumark_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ async def generate_vumark_instance(
Raises:
~vws.exceptions.vws_exceptions.AuthenticationFailureError: The
secret key is not correct.
~vws.exceptions.vws_exceptions.AuthorizationFailedError: There was
a general authentication problem.
~vws.exceptions.vws_exceptions.FailError: There was an error with
the request. For example, the given access key does not match a
known database.
Expand All @@ -94,6 +96,10 @@ async def generate_vumark_instance(
instance ID is invalid. For example, it may be empty.
~vws.exceptions.vws_exceptions.InvalidTargetTypeError: The target
is not a VuMark template target.
~vws.exceptions.vws_exceptions.LicenseCheckFailedError: The
license state and/or type does not allow this request.
~vws.exceptions.vws_exceptions.QuotaExceededError: No more
instances can be created for the associated license.
~vws.exceptions.vws_exceptions.RequestTimeTooSkewedError: There is
an error with the time sent to Vuforia.
~vws.exceptions.vws_exceptions.TargetStatusNotSuccessError: The
Expand Down
27 changes: 27 additions & 0 deletions src/vws/exceptions/vws_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,34 @@ class InvalidTargetTypeError(VWSError):
"""


# This is not simulated by the mock.
@beartype
class QuotaExceededError(VWSError):
"""Exception raised when Vuforia returns a response with a result code
``QuotaExceeded``.
"""


# This is not simulated by the mock.
@beartype
class LicenseCheckFailedError(VWSError):
"""Exception raised when Vuforia returns a response with a result code
``LicenseCheckFailed``.
"""


# This is not simulated by the mock.
@beartype
class AuthorizationFailedError(VWSError):
"""Exception raised when Vuforia returns a response with a result code
``AuthorizationFailed``.
"""


VWSError.register_exceptions_by_result_code(
exceptions_by_result_code={
"AuthenticationFailure": AuthenticationFailureError,
"AuthorizationFailed": AuthorizationFailedError,
"BadImage": BadImageError,
"BadRequest": BadRequestError,
"DateRangeError": DateRangeError,
Expand All @@ -215,10 +240,12 @@ class InvalidTargetTypeError(VWSError):
"InvalidAcceptHeader": InvalidAcceptHeaderError,
"InvalidInstanceId": InvalidInstanceIdError,
"InvalidTargetType": InvalidTargetTypeError,
"LicenseCheckFailed": LicenseCheckFailedError,
"MetadataTooLarge": MetadataTooLargeError,
"ProjectHasNoAPIAccess": ProjectHasNoAPIAccessError,
"ProjectInactive": ProjectInactiveError,
"ProjectSuspended": ProjectSuspendedError,
"QuotaExceeded": QuotaExceededError,
"RequestQuotaReached": RequestQuotaReachedError,
"RequestTimeTooSkewed": RequestTimeTooSkewedError,
"TargetNameExist": TargetNameExistError,
Expand Down
6 changes: 6 additions & 0 deletions src/vws/vumark_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ def generate_vumark_instance(
Raises:
~vws.exceptions.vws_exceptions.AuthenticationFailureError: The
secret key is not correct.
~vws.exceptions.vws_exceptions.AuthorizationFailedError: There was
a general authentication problem.
~vws.exceptions.vws_exceptions.FailError: There was an error with
the request. For example, the given access key does not match a
known database.
Expand All @@ -78,6 +80,10 @@ def generate_vumark_instance(
instance ID is invalid. For example, it may be empty.
~vws.exceptions.vws_exceptions.InvalidTargetTypeError: The target
is not a VuMark template target.
~vws.exceptions.vws_exceptions.LicenseCheckFailedError: The
license state and/or type does not allow this request.
~vws.exceptions.vws_exceptions.QuotaExceededError: No more
instances can be created for the associated license.
~vws.exceptions.vws_exceptions.RequestTimeTooSkewedError: There is
an error with the time sent to Vuforia.
~vws.exceptions.vws_exceptions.TargetStatusNotSuccessError: The
Expand Down
79 changes: 79 additions & 0 deletions tests/test_async_vws_exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Tests for VWS exceptions raised from async clients."""

import io
import json
import uuid
from http import HTTPStatus

Expand All @@ -10,21 +11,26 @@
from mock_vws.states import States

from vws import AsyncVuMarkService, AsyncVWS
from vws.exceptions.base_exceptions import VWSError
from vws.exceptions.custom_exceptions import (
ServerError,
)
from vws.exceptions.vws_exceptions import (
AuthenticationFailureError,
AuthorizationFailedError,
BadImageError,
FailError,
ImageTooLargeError,
InvalidInstanceIdError,
LicenseCheckFailedError,
MetadataTooLargeError,
ProjectInactiveError,
QuotaExceededError,
TargetNameExistError,
TargetStatusProcessingError,
UnknownTargetError,
)
from vws.response import Response
from vws.vumark_accept import VuMarkAccept


Expand Down Expand Up @@ -302,3 +308,76 @@ async def test_invalid_instance_id(
)

assert exc.value.response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY


@pytest.mark.asyncio
@pytest.mark.parametrize(
argnames=("result_code", "exception_type", "status_code"),
argvalues=[
("QuotaExceeded", QuotaExceededError, HTTPStatus.FORBIDDEN),
("LicenseCheckFailed", LicenseCheckFailedError, HTTPStatus.FORBIDDEN),
(
"AuthorizationFailed",
AuthorizationFailedError,
HTTPStatus.UNAUTHORIZED,
),
],
)
async def test_documented_vumark_error_codes(
*,
result_code: str,
exception_type: type[VWSError],
status_code: HTTPStatus,
) -> None:
"""
Documented VuMark Generation API error result codes raise matching
exceptions.
"""

class _AsyncVuMarkErrorTransport:
"""Async transport that returns a documented VuMark error result
code.
"""

async def aclose(self) -> None:
"""Close the transport."""

async def __call__(
self,
*,
method: str,
url: str,
headers: dict[str, str],
data: bytes,
request_timeout: float | tuple[float, float],
) -> Response:
"""Return a VuMark error response."""
del method, headers, request_timeout
body = {
"transaction_id": uuid.uuid4().hex,
"result_code": result_code,
}
text = json.dumps(obj=body)
return Response(
text=text,
url=url,
status_code=status_code,
headers={"Content-Type": "application/json"},
request_body=data,
tell_position=0,
content=text.encode(),
)

async with AsyncVuMarkService(
server_access_key=uuid.uuid4().hex,
server_secret_key=uuid.uuid4().hex,
transport=_AsyncVuMarkErrorTransport(),
) as vumark_service:
with pytest.raises(expected_exception=exception_type) as exc:
await vumark_service.generate_vumark_instance(
target_id="example_target_id",
instance_id="example_instance_id",
accept=VuMarkAccept.PNG,
)

assert exc.value.response.status_code == status_code
80 changes: 80 additions & 0 deletions tests/test_vws_exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Tests for VWS exceptions."""

import io
import json
import uuid
from http import HTTPStatus

Expand All @@ -17,6 +18,7 @@
)
from vws.exceptions.vws_exceptions import (
AuthenticationFailureError,
AuthorizationFailedError,
BadImageError,
BadRequestError,
DateRangeError,
Expand All @@ -25,10 +27,12 @@
InvalidAcceptHeaderError,
InvalidInstanceIdError,
InvalidTargetTypeError,
LicenseCheckFailedError,
MetadataTooLargeError,
ProjectHasNoAPIAccessError,
ProjectInactiveError,
ProjectSuspendedError,
QuotaExceededError,
RequestQuotaReachedError,
RequestTimeTooSkewedError,
TargetNameExistError,
Expand Down Expand Up @@ -354,6 +358,7 @@ def test_vwsexception_inheritance() -> None:
"""VWS-related exceptions should inherit from VWSException."""
subclasses = [
AuthenticationFailureError,
AuthorizationFailedError,
BadImageError,
BadRequestError,
DateRangeError,
Expand All @@ -362,10 +367,12 @@ def test_vwsexception_inheritance() -> None:
InvalidAcceptHeaderError,
InvalidInstanceIdError,
InvalidTargetTypeError,
LicenseCheckFailedError,
MetadataTooLargeError,
ProjectInactiveError,
ProjectHasNoAPIAccessError,
ProjectSuspendedError,
QuotaExceededError,
RequestQuotaReachedError,
RequestTimeTooSkewedError,
TargetNameExistError,
Expand Down Expand Up @@ -434,6 +441,79 @@ def test_invalid_target_type(
assert exc.value.response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY


@pytest.mark.parametrize(
argnames=("result_code", "exception_type", "status_code"),
argvalues=[
("QuotaExceeded", QuotaExceededError, HTTPStatus.FORBIDDEN),
("LicenseCheckFailed", LicenseCheckFailedError, HTTPStatus.FORBIDDEN),
(
"AuthorizationFailed",
AuthorizationFailedError,
HTTPStatus.UNAUTHORIZED,
),
],
)
def test_documented_vumark_error_codes(
*,
result_code: str,
exception_type: type[VWSError],
status_code: HTTPStatus,
) -> None:
"""
Documented VuMark Generation API error result codes raise matching
exceptions.
"""

class _VuMarkErrorTransport:
"""Transport that returns a documented VuMark error result
code.
"""

def close(self) -> None:
"""Close the transport."""

def __call__(
self,
*,
method: str,
url: str,
headers: dict[str, str],
data: bytes,
request_timeout: float | tuple[float, float],
) -> Response:
"""Return a VuMark error response."""
del method, headers, request_timeout
body = {
"transaction_id": uuid.uuid4().hex,
"result_code": result_code,
}
text = json.dumps(obj=body)
return Response(
text=text,
url=url,
status_code=status_code,
headers={"Content-Type": "application/json"},
request_body=data,
tell_position=0,
content=text.encode(),
)

vumark_service = VuMarkService(
server_access_key=uuid.uuid4().hex,
server_secret_key=uuid.uuid4().hex,
transport=_VuMarkErrorTransport(),
)

with pytest.raises(expected_exception=exception_type) as exc:
vumark_service.generate_vumark_instance(
target_id="example_target_id",
instance_id="example_instance_id",
accept=VuMarkAccept.PNG,
)

assert exc.value.response.status_code == status_code


def test_base_exception(
*,
vws_client: VWS,
Expand Down
Loading