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
21 changes: 21 additions & 0 deletions libs/unity-py/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.10.0] - 2025-02-19

### Added
* Unit test to validate the health services schema

### Fixed
* Schema definition of health service as it wasn't properly validating enumerated values

### Changed
* The health services schema has been updated to account for the new fields, componentCategory, componentType, and description
* Improved error handling of health service methods related to fetching health information from API
* Unit tests have been updated to use mock data rather than live API endpoints
* Updated printing of health status report to include new fields mentioned above

### Removed
* Superfluous unit test that creates a health_service instance

### Security

### Deprecated

## [0.9.0] - 2025-02-19

### Added
Expand Down
2 changes: 1 addition & 1 deletion libs/unity-py/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "unity-sds-client"
version = "0.9.0"
version = "0.10.0"

description = "Unity-Py is a Python client to simplify interactions with NASA's Unity Platform."
authors = ["Anil Natha, Mike Gangl"]
Expand Down
68 changes: 68 additions & 0 deletions libs/unity-py/tests/test_files/health_api_mock_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"services": [
{
"componentName": "Airflow API",
"componentCategory": "general",
"componentType": "api",
"description": "",
"ssmKey": "/unity/unity/dev/component/luca/airflow-api",
"healthCheckUrl": "http://k8s-airflow-airflowi-c68f394a2a-1712638265.us-west-2.elb.amazonaws.com:5000/api/v1/health",
"landingPageUrl": "http://k8s-airflow-airflowi-c68f394a2a-1712638265.us-west-2.elb.amazonaws.com:5000/api/v1",
"healthChecks": [
{
"status": "HEALTHY",
"httpResponseCode": "200",
"date": "2024-07-26T07:29:21.316116"
}
]
},
{
"componentName": "Airflow UI",
"componentCategory": "general",
"componentType": "ui",
"description": "",
"ssmKey": "/unity/unity/dev/component/luca/airflow-ui",
"healthCheckUrl": "http://k8s-airflow-airflowi-c68f394a2a-1712638265.us-west-2.elb.amazonaws.com:5000/health",
"landingPageUrl": "http://k8s-airflow-airflowi-c68f394a2a-1712638265.us-west-2.elb.amazonaws.com:5000",
"healthChecks": [
{
"status": "HEALTHY",
"httpResponseCode": "200",
"date": "2024-07-26T07:29:21.363497"
}
]
},
{
"componentName": "OGC API",
"componentCategory": "general",
"componentType": "api",
"description": "",
"ssmKey": "/unity/unity/dev/component/luca/ogc-api",
"healthCheckUrl": "http://k8s-airflow-ogcproce-e84448018d-906922971.us-west-2.elb.amazonaws.com:5001/health",
"landingPageUrl": "http://k8s-airflow-ogcproce-e84448018d-906922971.us-west-2.elb.amazonaws.com:5001",
"healthChecks": [
{
"status": "HEALTHY",
"httpResponseCode": "200",
"date": "2024-07-26T07:29:21.395126"
}
]
},
{
"componentName": "Management Console",
"componentCategory": "general",
"componentType": "ui",
"description": "",
"ssmKey": "/unity/unity/dev/component/management-console",
"healthCheckUrl": "http://unity-dev-httpd-alb-443241596.us-west-2.elb.amazonaws.com:8080/management/api/health_checks",
"landingPageUrl": "http://unity-dev-httpd-alb-443241596.us-west-2.elb.amazonaws.com:8080/management/ui",
"healthChecks": [
{
"status": "UNHEALTHY",
"httpResponseCode": "404",
"date": "2024-07-26T07:29:21.417864"
}
]
}
]
}
49 changes: 27 additions & 22 deletions libs/unity-py/tests/test_unity_health_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
Unity Health Service is functional.
"""

from unittest.mock import patch, Mock

import json
import pytest

Expand All @@ -13,30 +15,20 @@


@pytest.mark.regression
def test_health_service_client_creation():
def test_health_status_schema():
"""
Test that an instance of the health service can be instantiated.
Test that Health API schema is valid
"""
s = Unity()
health_service = s.client(UnityServices.HEALTH_SERVICE)
print("Validate Health API Schema")

@pytest.mark.regression
def test_health_status_retrieval():
"""
Test that health statuses can be retrieved using the health service.
"""
print("Example health status check")
s = Unity(environment=UnityEnvironments.DEV)
s.set_project("unity")
s.set_venue("dev")
health_service = s.client(UnityServices.HEALTH_SERVICE)
health_statuses = health_service.get_health_status()
f = open('../../schemas/health-service/health-services.schema.json', encoding='utf-8')
schema = json.load(f)
mock_data_file_path = 'tests/test_files/health_api_mock_data.json'
schema_file_path = '../../schemas/health-service/health-services.schema.json'

validate(instance=health_statuses, schema=schema)

assert health_statuses is not None
with open(mock_data_file_path, encoding='utf-8') as f_mock_data, \
open(schema_file_path, encoding='utf-8') as f_health_schema:
mock_health_data = json.load(f_mock_data)
schema = json.load(f_health_schema)
validate(instance=mock_health_data, schema=schema)

@pytest.mark.regression
def test_health_status_printing():
Expand All @@ -49,7 +41,14 @@ def test_health_status_printing():
health_service = s.client(UnityServices.HEALTH_SERVICE)

print("Example health status output using health service object:")
health_service.print_health_status()

mock_data_file_path = 'tests/test_files/health_api_mock_data.json'
with open(mock_data_file_path, encoding='utf-8') as f_mock_data:
mock_get_patcher = patch('unity_sds_client.services.health_service.requests.get')
mock_get = mock_get_patcher.start()
mock_get.return_value = Mock(status_code = 200)
mock_get.return_value.json.return_value = json.load(f_mock_data)
health_service.print_health_status()

@pytest.mark.regression
def test_health_service_printing():
Expand All @@ -61,4 +60,10 @@ def test_health_service_printing():
s.set_venue("dev")

print("Example health status output using unity object:")
print(s)
mock_data_file_path = 'tests/test_files/health_api_mock_data.json'
with open(mock_data_file_path, encoding='utf-8') as f_mock_data:
mock_get_patcher = patch('unity_sds_client.services.health_service.requests.get')
mock_get = mock_get_patcher.start()
mock_get.return_value = Mock(status_code = 200)
mock_get.return_value.json.return_value = json.load(f_mock_data)
print(s)
29 changes: 23 additions & 6 deletions libs/unity-py/unity_sds_client/services/health_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ def get_health_status(self):
headers = get_headers(token, {
'Content-type': 'application/json'
})
response = requests.get(url, headers=headers, timeout=60)

try:
response = requests.get(url, headers=headers, timeout=60)
response.raise_for_status()
except requests.HTTPError as exception:
raise exception

return response.json()

Expand All @@ -60,21 +65,33 @@ def generate_health_status_report(self):
Return a generated report of health status information
"""

health_statuses = self.get_health_status()

health_status_title = "HEALTH STATUS REPORT"
report = f"\n\n{health_status_title}\n"
report = report + len(health_status_title) * "-" + "\n\n"

try:
health_statuses = self.get_health_status()
except requests.HTTPError as error:
report = report + f"Error encountered with Health API Endpoint ({error.response.status_code})\n"
return report

for service in health_statuses["services"]:
service_name = service["componentName"]
service_category = service["componentCategory"]
service_type = service["componentType"]
service_description = service["description"]
landing_page_url = service["landingPageUrl"]
report = report + f"{service_name} ({landing_page_url})\n"
report = report + f"{service_name}\n"
report = report + f"{service_description}\n"
report = report + f"URL: {landing_page_url}\n"
report = report + f"Category: {service_category}\n"
report = report + f"Type: {service_type}\n"
for status in service["healthChecks"]:
service_status = status["status"]
service_status_date = status["date"]
report = report + f"{service_status_date}: {service_status}\n"
report = report + f"Health Status as of {service_status_date}: {service_status}\n"
report = report + "\n"

return report

def print_health_status(self):
Expand Down
113 changes: 62 additions & 51 deletions schemas/health-service/health-services.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,69 @@
"properties": {
"services": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"componentName": {
"type": "string"
},
"ssmKey": {
"type": "string"
},
"healthCheckUrl": {
"type": "string"
},
"landingPageUrl": {
"type": "string"
},
"healthChecks": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["HEALTHY", "UNHEALTHY"]
},
"httpResponseCode": {
"type": "string"
},
"date": {
"type": "string",
"format": "date-time"
}
},
"required": [
"status",
"httpResponseCode",
"date"
]
}
]
}
"items": {
"type": "object",
"properties": {
"componentName": {
"type": "string"
},
"componentCategory": {
"type": "string",
"enum": ["administration", "catalogs", "development", "infrastructure", "processing", "general"]
},
"componentType": {
"type": "string",
"enum": ["api", "ui", "unknown"]
},
"description": {
"type": "string"
},
"ssmKey": {
"type": "string"
},
"required": [
"componentName",
"ssmKey",
"healthCheckUrl",
"landingPageUrl",
"healthChecks"
]
}
]
"healthCheckUrl": {
"type": "string"
},
"landingPageUrl": {
"type": "string"
},
"healthChecks": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["HEALTHY", "UNHEALTHY"]
},
"httpResponseCode": {
"type": "string"
},
"date": {
"type": "string",
"format": "date-time"
}
},
"required": [
"status",
"httpResponseCode",
"date"
]
}
]
}
},
"required": [
"componentName",
"componentCategory",
"componentType",
"ssmKey",
"healthCheckUrl",
"landingPageUrl",
"healthChecks"
]
}
}
},
"required": [
Expand Down
Loading