"""Aura API functions. Ref: https://neo4j.com/docs/aura/platform/api/specification/ """ import requests from requests.auth import HTTPBasicAuth import config from exceptions import ApiError from exceptions import TokenExpired def get_oauth_token() -> object: """To obtain a Bearer Token for authenticating API requests.""" response = requests.post( f'{config.AURA_API_HOST}oauth/token', headers={'Content-Type': 'application/x-www-form-urlencoded'}, data={'grant_type': 'client_credentials'}, auth=HTTPBasicAuth( config.AURA_API_CLIENT_ID, config.AURA_API_CLIENT_SECRET) ) data = response.json() assert data.get('access_token'), 'Error: No access_token' assert data.get('token_type', '') == 'Bearer' return data def get_all_instances(token: object) -> list: """Returns a list containing a summary of each of your Aura instances. Returns: List of Instance objects. """ response = requests.get( f'{config.AURA_API_BASE_URL}instances', headers={ 'Authorization': f"Bearer {token.get('access_token')}", 'Content-Type': config.JSON_CONTENT_TYPE } ) data = response.json() assert data.get('data'), "Error: No instances data" assert len(data.get('data', [])) > 0 return data.get('data') def get_instance_details(token: object, instance_id: str) -> object: """Returns details about a specific Aura Instance. Returns: Object. """ response = requests.get( f'{config.AURA_API_BASE_URL}instances/{instance_id}', headers={ 'Authorization': f"Bearer {token.get('access_token')}", 'Content-Type': config.JSON_CONTENT_TYPE } ) data = response.json() if data.get('errors'): err_msg = ', '.join([item['message'] for item in data.get('errors')]) raise ApiError(err_msg) elif data.get('error'): if 'token has expired' in str(data.get('error')): raise TokenExpired(str(data.get('error'))) raise ApiError(str(data.get('error'))) assert data.get('data'), f'Error: Instance with id {instance_id} not found.' assert data.get('data', {}).get('status'), \ f'Error: Missing status for Instance {instance_id}.' return data.get('data') def overwrite_instance( token: object, dest_instance_id: str, source_instance_id: str ) -> object: """Starts the process of overwriting the specified instance. A new snapshot of that instance is created and used for overwriting. Overwriting an instance is an asynchronous operation. Returns: Object. Throws: Exception. Eg: The database is currently undergoing an operation: overwriting. Cannot clone database b4820bcb into smaller database 8d6c00eb. The database is currently undergoing an operation: loading """ response = requests.post( f'{config.AURA_API_BASE_URL}instances/{dest_instance_id}/overwrite', headers={ 'Authorization': f"Bearer {token.get('access_token')}", 'Content-Type': config.JSON_CONTENT_TYPE }, json={'source_instance_id': source_instance_id} ) data = response.json() if data.get('errors'): err_msg = ', '.join([item['message'] for item in data.get('errors')]) raise ApiError(err_msg) assert data.get('data'), \ f'Error: Failed to overwrite Instance {dest_instance_id}.' assert data.get('data', {}).get('status'), \ f'Error: Missing status for Instance {dest_instance_id}.' assert data.get('data').get('status') == 'overwriting', \ (f"Error: Failed to start overwriting Instance {dest_instance_id}. " f"Status is still {data.get('data').get('status')}") return data.get('data') def update_instance( token: object, dest_instance_id: str, cdc_mode: str ) -> object: """Update CDC enrichment mode on an Aura instance. Returns: Object. Throws: Exception. Eg: The database is currently undergoing an operation: overwriting. DB not found: 24d18db5 """ response = requests.patch( f'{config.AURA_API_BASE_URL}instances/{dest_instance_id}', headers={ 'Authorization': f"Bearer {token.get('access_token')}", 'Content-Type': config.JSON_CONTENT_TYPE }, json={'cdc_enrichment_mode': cdc_mode} ) data = response.json() if data.get('errors'): err_msg = ', '.join([item['message'] for item in data.get('errors')]) raise ApiError(err_msg) assert data.get('data'), \ f'Error: Failed to update Instance {dest_instance_id}.' assert data.get('data', {}).get('status'), \ f'Error: Missing status after updating the Instance {dest_instance_id}.' return data.get('data')