Skip to content

Commit

Permalink
ResilienceHub: list_app_assessments() can now return pre-configured r…
Browse files Browse the repository at this point in the history
…esults (#7693)
  • Loading branch information
bblommers committed May 14, 2024
1 parent b90a540 commit 421ab40
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 3 deletions.
36 changes: 36 additions & 0 deletions docs/docs/services/resiliencehub.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,42 @@ resiliencehub
- [ ] list_app_assessment_compliance_drifts
- [ ] list_app_assessment_resource_drifts
- [X] list_app_assessments

Moto will not actually execute any assessments, so this operation will return an empty list by default.
You can use a dedicated API to override this, by configuring a queue of expected results.

A request to `list_app_assessments` will take the first result from that queue, with subsequent calls with the same parameters returning the same result.

Calling `list_app_assessments` with a different set of parameters will return the second result from that queue - and so on, or an empty list of the queue is empty.

Configure this queue by making an HTTP request to `/moto-api/static/resilience-hub-assessments/response`. An example invocation looks like this:

.. sourcecode:: python

summary1 = {"appArn": "app_arn1", "appVersion": "some version", ...}
summary2 = {"appArn": "app_arn2", ...}
results = {"results": [[summary1, summary2], [summary2]], "region": "us-east-1"}
resp = requests.post(
"http://motoapi.amazonaws.com/moto-api/static/resilience-hub-assessments/response",
json=results,
)

assert resp.status_code == 201

client = boto3.client("lambda", region_name="us-east-1")
# First result
resp = client.list_app_assessments() # [summary1, summary2]
# Second result
resp = client.list_app_assessments(assessmentStatus="Pending") # [summary2]

If you're using MotoServer, make sure to make this request to where MotoServer is running:

.. sourcecode:: python

http://localhost:5000/moto-api/static/resilience-hub-assessments/response



- [ ] list_app_component_compliances
- [ ] list_app_component_recommendations
- [ ] list_app_input_sources
Expand Down
8 changes: 8 additions & 0 deletions moto/moto_api/_internal/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ def set_lambda_simple_result(
backend = lambda_simple_backends[account_id][region]
backend.lambda_simple_results_queue.append(result)

def set_resilience_result(
self, result: List[Dict[str, Any]], account_id: str, region: str
) -> None:
from moto.resiliencehub.models import resiliencehub_backends

backend = resiliencehub_backends[account_id][region]
backend.app_assessments_queue.append(result)

def set_sagemaker_result(
self,
body: str,
Expand Down
18 changes: 18 additions & 0 deletions moto/moto_api/_internal/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,24 @@ def set_lambda_simple_result(
)
return 201, {}, ""

def set_resilience_result(
self,
request: Any,
full_url: str, # pylint: disable=unused-argument
headers: Any,
) -> TYPE_RESPONSE:
from .models import moto_api_backend

body = self._get_body(headers, request)
account_id = body.get("account_id", DEFAULT_ACCOUNT_ID)
region = body.get("region", "us-east-1")

for result in body.get("results", []):
moto_api_backend.set_resilience_result(
result=result, account_id=account_id, region=region
)
return 201, {}, ""

def set_sagemaker_result(
self,
request: Any,
Expand Down
1 change: 1 addition & 0 deletions moto/moto_api/_internal/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"{0}/moto-api/static/ce/cost-and-usage-results": response_instance.set_ce_cost_usage_result,
"{0}/moto-api/static/inspector2/findings-results": response_instance.set_inspector2_findings_result,
"{0}/moto-api/static/lambda-simple/response": response_instance.set_lambda_simple_result,
"{0}/moto-api/static/resilience-hub-assessments/response": response_instance.set_resilience_result,
"{0}/moto-api/static/sagemaker/endpoint-results": response_instance.set_sagemaker_result,
"{0}/moto-api/static/rds-data/statement-results": response_instance.set_rds_data_result,
"{0}/moto-api/state-manager/get-transition": response_instance.get_transition,
Expand Down
47 changes: 46 additions & 1 deletion moto/resiliencehub/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ def __init__(self, region_name: str, account_id: str):
self.policies: Dict[str, Policy] = dict()
self.tagger = TaggingService()

self.app_assessments_queue: List[List[Dict[str, Any]]] = []
self.app_assessments_results: Dict[str, List[Dict[str, Any]]] = {}

def create_app(
self,
assessment_schedule: str,
Expand Down Expand Up @@ -251,7 +254,49 @@ def list_apps(self, app_arn: str, name: str, reverse_order: bool) -> List[App]:
app_summaries.reverse()
return app_summaries

def list_app_assessments(self) -> List[str]:
def list_app_assessments(self, request_identifier: str) -> List[Dict[str, Any]]:
"""
Moto will not actually execute any assessments, so this operation will return an empty list by default.
You can use a dedicated API to override this, by configuring a queue of expected results.
A request to `list_app_assessments` will take the first result from that queue, with subsequent calls with the same parameters returning the same result.
Calling `list_app_assessments` with a different set of parameters will return the second result from that queue - and so on, or an empty list of the queue is empty.
Configure this queue by making an HTTP request to `/moto-api/static/resilience-hub-assessments/response`. An example invocation looks like this:
.. sourcecode:: python
summary1 = {"appArn": "app_arn1", "appVersion": "some version", ...}
summary2 = {"appArn": "app_arn2", ...}
results = {"results": [[summary1, summary2], [summary2]], "region": "us-east-1"}
resp = requests.post(
"http://motoapi.amazonaws.com/moto-api/static/resilience-hub-assessments/response",
json=results,
)
assert resp.status_code == 201
client = boto3.client("lambda", region_name="us-east-1")
# First result
resp = client.list_app_assessments() # [summary1, summary2]
# Second result
resp = client.list_app_assessments(assessmentStatus="Pending") # [summary2]
If you're using MotoServer, make sure to make this request to where MotoServer is running:
.. sourcecode:: python
http://localhost:5000/moto-api/static/resilience-hub-assessments/response
"""
if request_identifier in self.app_assessments_results:
return self.app_assessments_results[request_identifier]
if self.app_assessments_queue:
self.app_assessments_results[request_identifier] = (
self.app_assessments_queue.pop(0)
)
return self.app_assessments_results[request_identifier]
return []

def describe_app(self, app_arn: str) -> App:
Expand Down
18 changes: 17 additions & 1 deletion moto/resiliencehub/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,23 @@ def list_apps(self) -> str:
)

def list_app_assessments(self) -> str:
summaries = self.resiliencehub_backend.list_app_assessments()
supported_params = [
"appArn",
"assessmentName",
"assessmentStatus",
"complianceStatus",
"invoker",
"maxResults",
"nextToken",
"reverseOrder",
]
provided_params = [p for p in self.querystring.keys() if p in supported_params]
request_identifier = json.dumps(
{key: self.querystring[key] for key in sorted(provided_params)}
)
summaries = self.resiliencehub_backend.list_app_assessments(
request_identifier=request_identifier,
)
return json.dumps(dict(assessmentSummaries=summaries))

def describe_app(self) -> str:
Expand Down
41 changes: 40 additions & 1 deletion tests/test_resiliencehub/test_resiliencehub.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import boto3
import pytest
import requests
from botocore.exceptions import ClientError

from moto import mock_aws
from moto import mock_aws, settings

# See our Development Tips on writing tests for hints on how to write good tests:
# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html
Expand Down Expand Up @@ -209,6 +210,44 @@ def test_list_apps():
def test_list_app_assessments():
client = boto3.client("resiliencehub", region_name="ap-southeast-1")
assert client.list_app_assessments()["assessmentSummaries"] == []
assert client.list_app_assessments(appArn="asdf")["assessmentSummaries"] == []

# Setup
base_url = (
settings.test_server_mode_endpoint()
if settings.TEST_SERVER_MODE
else "http://motoapi.amazonaws.com"
)
# Configure results for the first two requests
summary1 = {"appArn": "app_arn1", "appVersion": "some version"}
summary2 = {"appArn": "app_arn2", "assessmentName": "assessment 2"}
results = {
"results": [[summary1, summary2], [summary2]],
"region": "ap-southeast-1",
}
resp = requests.post(
f"{base_url}/moto-api/static/resilience-hub-assessments/response",
json=results,
)
assert resp.status_code == 201

# First response
assert client.list_app_assessments()["assessmentSummaries"] == [summary1, summary2]

# Stays the same on subsequent invocations
assert client.list_app_assessments()["assessmentSummaries"] == [summary1, summary2]

# Second response with different args
assert client.list_app_assessments(appArn="asdf")["assessmentSummaries"] == [
summary2
]

# Third response returns an empty response, because nothing else is configured
assert client.list_app_assessments(appArn="diff")["assessmentSummaries"] == []

# First response in different region also returns nothing, because we only configured this in ap-southeast
us_client = boto3.client("resiliencehub", region_name="us-east-1")
assert us_client.list_app_assessments()["assessmentSummaries"] == []


@mock_aws
Expand Down

0 comments on commit 421ab40

Please sign in to comment.