Thanks to visit codestin.com
Credit goes to github.com

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
31 changes: 13 additions & 18 deletions chalicelib/api/listings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,67 +10,62 @@

# TODO: Change /submit to /apply
@listings_api.route("/submit", methods=["POST"], cors=True)
@handle_exceptions
def apply_to_listing():
return listing_service.apply(listings_api.current_request.json_body)


@listings_api.route("/create", methods=["POST"], cors=True)
@auth(listings_api, roles=[Roles.ADMIN, Roles.MEMBER])
@handle_exceptions
def create_listing():
"""Creates a new listing with given information"""
return listing_service.create(listings_api.current_request.json_body)


@listings_api.route("/listings/{id}", methods=["GET"], cors=True)
@handle_exceptions
def get_listing(id):
"""Gets a listing from id"""
return listing_service.get(id)


@listings_api.route("/listings", methods=["GET"], cors=True)
@auth(listings_api, roles=[Roles.ADMIN, Roles.MEMBER])
@handle_exceptions
def get_all_listings():
"""Gets all listings available"""
return listing_service.get_all()


@listings_api.route("/listings/{id}", methods=["DELETE"], cors=True)
@auth(listings_api, roles=[Roles.ADMIN, Roles.MEMBER])
@handle_exceptions
def delete_listing(id):
"""Deletes a listing with the given ID."""
try:
return listing_service.delete(id)
except Exception as e:
listings_api.log.debug(e)
return listing_service.delete(id)


@listings_api.route("/listings/{id}/toggle/visibility", methods=["PATCH"], cors=True)
@auth(listings_api, roles=[Roles.ADMIN, Roles.MEMBER])
@handle_exceptions
def toggle_visibility(id):
"""Toggles visibilility of a given <listing_id>"""
try:
return listing_service.toggle_visibility(id)
except Exception as e:
listings_api.log.debug(e)
return listing_service.toggle_visibility(id)


@listings_api.route("/listings/{id}/toggle/encryption", methods=["PATCH"], cors=True)
@auth(listings_api, roles=[Roles.ADMIN])
@handle_exceptions
def toggle_encryption(id):
"""Encrypts a listing with the given ID."""
try:
return listing_service.toggle_encryption(id)
except Exception as e:
listings_api.log.debug(e)
return listing_service.toggle_encryption(id)


@listings_api.route("/listings/{id}/update-field", methods=["PATCH"], cors=True)
@auth(listings_api, roles=[Roles.ADMIN, Roles.MEMBER])
@handle_exceptions
def update_listing_field_route(id):
try:
return listing_service.update_field_route(
id, listings_api.current_request.json_body
)
except Exception as e:
listings_api.log.debug(e)
return listing_service.update_field_route(
id, listings_api.current_request.json_body
)
23 changes: 18 additions & 5 deletions chalicelib/handlers/error_handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from chalice import NotFoundError, BadRequestError
from chalice import Response
from chalice import NotFoundError, BadRequestError, ChaliceViewError
import logging


def handle_exceptions(func):
Expand All @@ -7,12 +9,23 @@ def wrapper(*args, **kwargs):
return func(*args, **kwargs)

except BadRequestError as e:
return {"status": False, "message": str(e)}, 400
logging.warning(f"[{func.__name__}] BadRequestError: {str(e)}")
raise

except NotFoundError as e:
return {"status": False, "message": str(e)}, 404
logging.warning(f"[{func.__name__}] NotFoundError: {str(e)}")
raise

except ChaliceViewError as e:
logging.error(f"[{func.__name__}] ChaliceViewError: {str(e)}")
raise

except Exception as e:
return {"status": False, "message": "Internal Server Error"}, 500
logging.exception(f"[{func.__name__}] Unexpected error")
return Response(
body={"error": "Internal Server Error", "message": str(e)},
headers={"Content-Type": "application/json"},
status_code=500,
)

return wrapper
return wrapper
1 change: 1 addition & 0 deletions chalicelib/services/EventsMemberService.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ def create_event(self, timeframe_id: str, event_data: dict):
timeframe_doc = self.mongo_module.get_document_by_id(
f"{self.collection_prefix}timeframe", timeframe_id
)

spreadsheet_id = timeframe_doc["spreadsheetId"]

# Add event name to Google Sheets
Expand Down
17 changes: 5 additions & 12 deletions chalicelib/services/ListingService.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,20 +159,13 @@ def toggle_visibility(self, id: str):
return json.dumps({"statusCode": 500, "message": str(e)})

def toggle_encryption(self, id: str):
try:
# Perform encryption toggle in the database
data = db.toggle_encryption(
table_name="zap-listings", key={"listingId": id}
)
# Perform encryption toggle in the database
data = db.toggle_encryption(table_name="zap-listings", key={"listingId": id})

# Check the result and return the appropriate response
if data:
return json.dumps({"statusCode": 200})
else:
return json.dumps({"statusCode": 400, "message": "Invalid listing ID"})
if data is None:
raise NotFoundError(f"Listing not found: {id}")

except Exception as e:
return json.dumps({"statusCode": 500, "message": str(e)})
return json.dumps({"statusCode": 200})

def update_field_route(self, id, data):
try:
Expand Down
57 changes: 52 additions & 5 deletions tests/api/test_listings.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,11 @@ def test_update_listing_field_route_not_found():
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)

assert response.json_body is None
assert response.json_body == {
"Message": "Not found",
"Code": "NotFoundError",
}
assert response.status_code == 404


def test_update_listing_field_route_bad_request():
Expand All @@ -180,9 +184,11 @@ def test_update_listing_field_route_bad_request():
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)

# body, status_code = response.json_body

assert response.json_body is None
assert response.json_body == {
"Code": "BadRequestError",
"Message": "Bad request",
}
assert response.status_code == 400


def test_update_listing_field_route_exception():
Expand All @@ -200,4 +206,45 @@ def test_update_listing_field_route_exception():
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)

assert response.json_body is None
assert response.json_body == {
"error": "Internal Server Error",
"message": "Error",
}


def test_toggle_encryption_succeeds():
with Client(app) as client:
with patch("chalicelib.decorators.jwt.decode") as mock_decode:
# Assuming the decoded token has the required role
mock_decode.return_value = {"roles": ["admin"]}
with patch(
"chalicelib.services.ListingService.listing_service.toggle_encryption"
) as mock_toggle_encryption:
mock_toggle_encryption.return_value = {"status": True}
response = client.http.patch(
f"/listings/{TEST_LISTINGS[0]['listingId']}/toggle/encryption",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)

assert response.status_code == 200


def test_toggle_encryption_invalid_id_raises_not_found():
with Client(app) as client:
with patch("chalicelib.decorators.jwt.decode") as mock_decode:
# Assuming the decoded token has the required role
mock_decode.return_value = {"roles": ["admin"]}
with patch(
"chalicelib.services.ListingService.listing_service.toggle_encryption"
) as mock_toggle_encryption:
mock_toggle_encryption.side_effect = NotFoundError("empty id")
response = client.http.patch(
f"/listings/{TEST_LISTINGS[0]['listingId']}/toggle/encryption",
headers={"Authorization": "Bearer SAMPLE_TOKEN_STRING"},
)

assert response.json_body == {
"Message": "empty id",
"Code": "NotFoundError",
}
assert response.status_code == 404
30 changes: 30 additions & 0 deletions tests/services/test_listing_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,33 @@ def test_update_field_exception(service):
)

assert str(exc_info.value) == "Error"


def test_toggle_encryption_succeeds(service):
listing_service, mock_db = service

mock_db.toggle_encryption.return_value = True
mock_listing_id = SAMPLE_LISTINGS[0]["listingId"]

result = json.loads(listing_service.toggle_encryption(mock_listing_id))

mock_db.toggle_encryption.assert_called_once_with(
table_name="zap-listings", key={"listingId": mock_listing_id}
)
assert result["statusCode"] == 200


def test_toggle_encryption_invalid_listing_id_raises_not_found(service):
listing_service, mock_db = service

mock_db.toggle_encryption.return_value = None
mock_listing_id = "3"

with pytest.raises(NotFoundError) as exc_info:
listing_service.toggle_encryption(mock_listing_id)

mock_db.toggle_encryption.assert_called_once_with(
table_name="zap-listings", key={"listingId": mock_listing_id}
)

assert str(exc_info.value) == "Listing not found: 3"
59 changes: 57 additions & 2 deletions tests/test_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@
import pytest

from chalicelib.decorators import add_env_suffix, auth
from chalicelib.handlers.error_handler import handle_exceptions
from chalicelib.models.roles import Roles
from chalice import UnauthorizedError
from chalice import (
UnauthorizedError,
Response,
BadRequestError,
NotFoundError,
ChaliceViewError,
)


def test_add_env_suffix_dev():
Expand All @@ -20,7 +27,6 @@ def mocked_function(self, table_name: str, *args, **kwargs):

# Call the decorated function with 'env=False'
result = decorated_function(instance_mock, "test-table", env=False)

# Check if the suffix is added correctly
assert result == "test-table-dev"

Expand All @@ -42,6 +48,55 @@ def mocked_function(self, table_name: str, *args, **kwargs):
assert result == "test-table-prod"


def test_handle_exceptions_no_error():
@handle_exceptions
def sample_function():
return "Success"

assert sample_function() == "Success"


def test_handle_exceptions_bad_request_error():
@handle_exceptions
def sample_function():
raise BadRequestError("Bad request")

with pytest.raises(BadRequestError):
sample_function()


def test_handle_exceptions_not_found_error():
@handle_exceptions
def sample_function():
raise NotFoundError("Not found")

with pytest.raises(NotFoundError):
sample_function()


def test_handle_exceptions_chalice_view_error():
@handle_exceptions
def sample_function():
raise ChaliceViewError("Chalice view error")

with pytest.raises(ChaliceViewError):
sample_function()


def test_handle_exceptions_general_exception():
@handle_exceptions
def sample_function():
raise Exception("General error")

response = sample_function()
assert isinstance(response, Response)
assert response.status_code == 500
assert response.body == {
"error": "Internal Server Error",
"message": "General error",
}


@pytest.fixture
def mock_blueprint():
return Mock()
Expand Down