# pylint: disable=W0718
"""Smarter API Account Manifest handler"""
import logging
import traceback
from typing import TYPE_CHECKING, Optional, Type
from django.core import serializers
from rest_framework.serializers import ModelSerializer
from smarter.apps.account.manifest.models.account.const import MANIFEST_KIND
from smarter.apps.account.manifest.models.account.metadata import SAMAccountMetadata
from smarter.apps.account.manifest.models.account.model import SAMAccount
from smarter.apps.account.manifest.models.account.spec import (
SAMAccountSpec,
SAMAccountSpecConfig,
)
from smarter.apps.account.manifest.models.account.status import SAMAccountStatus
from smarter.apps.account.models import Account, UserProfile
from smarter.apps.account.signals import broker_ready
from smarter.apps.account.utils import (
smarter_cached_objects,
)
from smarter.lib import json
from smarter.lib.django import waffle
from smarter.lib.django.waffle import SmarterWaffleSwitches
from smarter.lib.journal.enum import SmarterJournalCliCommands
from smarter.lib.journal.http import SmarterJournaledJsonResponse
from smarter.lib.logging import WaffleSwitchedLoggerWrapper
from smarter.lib.manifest.broker import (
AbstractBroker,
SAMBrokerError,
SAMBrokerErrorNotFound,
SAMBrokerErrorNotImplemented,
SAMBrokerErrorNotReady,
)
from smarter.lib.manifest.enum import (
SAMKeys,
SAMMetadataKeys,
SCLIResponseGet,
SCLIResponseGetData,
)
if TYPE_CHECKING:
from django.http import HttpRequest
# pylint: disable=W0613
[docs]
def should_log(level):
"""Check if logging should be done based on the waffle switch."""
return waffle.switch_is_active(SmarterWaffleSwitches.ACCOUNT_LOGGING) and waffle.switch_is_active(
SmarterWaffleSwitches.MANIFEST_LOGGING
)
base_logger = logging.getLogger(__name__)
logger = WaffleSwitchedLoggerWrapper(base_logger, should_log)
MAX_RESULTS = 1000
[docs]
class AccountSerializer(ModelSerializer):
"""Account serializer for smarter api."""
# pylint: disable=missing-class-docstring
class Meta:
model = Account
fields = ["account_number", "company_name", "created_at", "updated_at"]
[docs]
class SAMAccountBrokerError(SAMBrokerError):
"""Base exception for Smarter API Account Broker handling."""
@property
def get_formatted_err_message(self):
return "Smarter API Account Manifest Broker Error"
[docs]
class SAMAccountBroker(AbstractBroker):
"""
Handles Smarter API Account Manifest operations, including loading, validating, and parsing YAML manifests, and mapping them to Django ORM and Pydantic models.
This broker transforms between Django ORM and Pydantic models, ensuring data consistency for serialization and API responses.
This broker is responsible for:
- Loading and validating Smarter API Account manifests.
- Initializing the corresponding Pydantic model from manifest data.
- Creating, updating, deleting, and querying Django ORM models representing account manifests.
- Transforming Django ORM models into Pydantic models for serialization and deserialization.
:param _manifest: The current manifest instance (`SAMAccount`), if loaded.
:type _manifest: Optional[SAMAccount]
:param _pydantic_model: The Pydantic model class used for manifests.
:type _pydantic_model: Type[SAMAccount]
:param _account: The Django ORM `Account` instance associated with the manifest.
:type _account: Optional[Account]
.. note::
The manifest must be explicitly initialized with manifest data, typically using ``**data`` from the manifest loader.
.. warning::
If the manifest loader or manifest metadata is missing, or if the account is not set, the manifest will not be initialized and may return ``None`` or raise an exception.
**Example usage**::
broker = SAMAccountBroker()
manifest = broker.manifest
if manifest:
print(manifest.apiVersion, manifest.kind)
.. seealso::
- :class:`SAMAccount`
- :class:`Account`
- :class:`SAMAccountMetadata`
- :class:`SAMAccountSpec`
.. versionadded:: 1.0.0
Initial implementation of the Smarter API Account Manifest Broker.
"""
# override the base abstract manifest model with the Account model
_manifest: Optional[SAMAccount] = None
_pydantic_model: Type[SAMAccount] = SAMAccount
_brokered_account: Optional[Account] = None
_orm_instance: Optional[Account] = None
[docs]
def __init__(self, *args, **kwargs):
"""
Initialize the SAMAccountBroker instance.
This constructor initializes the broker by calling the parent class's
constructor, which will attempt to bootstrap the class instance
with any combination of raw manifest data (in JSON or YAML format),
a manifest loader, or existing Django ORM models. If a manifest
loader is provided and its kind matches the expected kind for this broker,
the manifest is initialized using the loader's data.
This class can bootstrap itself in any of the following ways:
- request.body (yaml or json string)
- name + account (determined via authentication of the request object)
- SAMLoader instance
- manifest instance
- filepath to a manifest file
If raw manifest data is provided, whether as a string or a dictionary,
or a SAMLoader instance, the base class constructor will only goes as
far as initializing the loader. The actual manifest model initialization
is deferred to this constructor, which checks the loader's kind.
:param args: Positional arguments passed to the parent constructor.
:param kwargs: Keyword arguments passed to the parent constructor.
**Example:**
.. code-block:: python
broker = SAMAccountBroker(loader=loader, plugin_meta=plugin_meta)
"""
super().__init__(*args, **kwargs)
msg = f"{self.formatted_class_name}.__init__() broker for {self.kind} {self.name} is {self.ready_state}."
if self.ready:
logger.info(msg)
else:
logger.warning(msg)
###########################################################################
# Smarter abstract property implementations
###########################################################################
@property
def ready(self) -> bool:
"""
Check if the broker is ready for operations.
This property determines whether the broker has been properly initialized
and is ready to perform its functions. A broker is considered ready if
it has a valid manifest loaded, either from raw data, a loader, or
existing Django ORM models.
:returns: ``True`` if the broker is ready, ``False`` otherwise.
:rtype: bool
"""
retval = super().ready
if not retval:
logger.warning("%s.ready() AbstractBroker is not ready for %s", self.formatted_class_name, self.kind)
return False
retval = self._manifest is not None or self.brokered_account is not None
logger.debug(
"%s.ready() manifest presence indicates ready=%s for %s",
self.formatted_class_name,
retval,
self.kind,
)
if retval:
broker_ready.send(sender=self.__class__, broker=self)
return retval
@property
def brokered_account(self) -> Optional[Account]:
"""
In order to disambiguate between the AccountMixin.account
(the authenticated account making the request) and the Account
resource being brokered, we use the term "brokered_account".
Get the Django ORM `Account` instance associated with this broker.
:returns: The `Account` instance if set, otherwise None.
:rtype: Optional[Account]
"""
if self._brokered_account:
return self._brokered_account
if not self.name:
logger.debug("%s.brokered_account() no name provided, cannot retrieve Account.", self.formatted_class_name)
return None
try:
self._brokered_account = Account.get_cached_object(name=self.name)
logger.debug(
"%s.brokered_account() initialized existing Account: %s",
self.formatted_class_name,
self._brokered_account,
)
except Account.DoesNotExist:
logger.debug(
"%s.brokered_account() no existing Account found with name: %s", self.formatted_class_name, self.name
)
self._brokered_account = None
return self._brokered_account
@brokered_account.setter
def brokered_account(self, value: Account) -> None:
"""
Set the Django ORM `Account` instance associated with this broker.
:param value: The `Account` instance to set.
:type value: Account
"""
self._brokered_account = value
logger.debug("%s.brokered_account() set to Account: %s", self.formatted_class_name, self._brokered_account)
@property
def formatted_class_name(self) -> str:
"""
Returns a formatted class name string for use in logging, providing a more readable identifier for this broker class.
:returns: The formatted class name, including the parent class and `SAMAccountBroker()`.
:rtype: str
**Example usage**::
logger.info(broker.formatted_class_name)
"""
parent_class = super().formatted_class_name
return f"{parent_class}.{SAMAccountBroker.__name__}[{id(self)}]"
@property
def kind(self) -> str:
"""
Get the manifest kind for the Smarter API Account.
:returns: The manifest kind string for the Smarter API Account.
:rtype: str
"""
return MANIFEST_KIND
@property
def name(self) -> Optional[str]:
"""
Get the name of the Smarter API Account.
:returns: The name of the Smarter API Account, or None if not set.
:rtype: Optional[str]
"""
retval = super().name
if retval:
return retval
if self._brokered_account:
return str(self._brokered_account.name)
@property
def manifest(self) -> Optional[SAMAccount]:
"""
Get the manifest for the Smarter API Account as a Pydantic model.
:returns: A `SAMAccount` Pydantic model instance representing the Smarter API Account manifest, or None if not initialized.
.. note::
The top-level manifest model (`SAMAccount`) must be explicitly initialized with manifest data, typically using ``**data`` from the manifest loader.
.. tip::
Child models within the manifest are automatically cascade-initialized by Pydantic, passing ``**data`` to each child's constructor.
.. warning::
If the manifest loader or manifest metadata is missing, or if the account is not set, the manifest will not be initialized and None may be returned or an exception raised.
**Example usage**::
# Access the manifest property
manifest = broker.manifest
if manifest:
print(manifest.apiVersion, manifest.kind)
"""
if self._manifest:
if not isinstance(self._manifest, SAMAccount):
raise SAMAccountBrokerError(
message=f"Invalid manifest type for {self.kind} broker: {type(self._manifest)}",
thing=self.kind,
command=SmarterJournalCliCommands.APPLY,
)
return self._manifest
# 1.) prioritize manifest loader data if available. if it was provided
# in the request body then this is the authoritative source.
if self.loader and self.loader.manifest_kind == self.kind:
self._manifest = SAMAccount(
apiVersion=self.loader.manifest_api_version,
kind=self.loader.manifest_kind,
metadata=SAMAccountMetadata(**self.loader.manifest_metadata),
spec=SAMAccountSpec(**self.loader.manifest_spec),
status=None,
)
logger.debug(
"%s.manifest() initialized %s from loader: %s",
self.formatted_class_name,
type(self._manifest).__name__,
json.dumps(self._manifest.model_dump(), indent=4),
)
return self._manifest
# 2.) next, (and only if a loader is not available) try to initialize
# from existing Account model if available
elif self.brokered_account:
account_number = str(self.brokered_account.account_number)
status = SAMAccountStatus(
adminAccount=account_number,
recordLocator=self.brokered_account.record_locator,
created=self.brokered_account.created_at,
modified=self.brokered_account.updated_at,
)
metadata = SAMAccountMetadata(
name=str(self.brokered_account.name) or self.brokered_account.account_number.replace(" ", "_"),
description=self.brokered_account.company_name,
version=self.brokered_account.version,
tags=self.brokered_account.tags_list,
accountNumber=self.brokered_account.account_number,
annotations=self.brokered_account.annotations,
)
config = SAMAccountSpecConfig(
companyName=self.brokered_account.company_name or "missing company name",
phoneNumber=self.brokered_account.phone_number or "missing phone number",
address1=self.brokered_account.address1 or "missing address1",
address2=self.brokered_account.address2 or "missing address2",
city=self.brokered_account.city or "missing city",
state=self.brokered_account.state or "missing state",
postalCode=self.brokered_account.postal_code or "missing postal code",
country=self.brokered_account.country or "US",
language=self.brokered_account.language or "en-US",
timezone=self.brokered_account.timezone or "America/New_York",
currency=self.brokered_account.currency or "USD",
)
self._manifest = SAMAccount(
apiVersion=self.api_version,
kind=self.kind,
metadata=metadata,
spec=SAMAccountSpec(config=config),
status=status,
)
logger.debug(
"%s.manifest() initialized %s from Account ORM model %s: %s",
self.formatted_class_name,
type(self._manifest).__name__,
self.brokered_account,
serializers.serialize("json", [self.brokered_account]),
)
return self._manifest
else:
logger.warning("%s.manifest could not be initialized", self.formatted_class_name)
return self._manifest
@property
def SerializerClass(self) -> Type[AccountSerializer]:
"""
Get the Django REST Framework serializer class for the Smarter API Account.
:returns: The `AccountSerializer` class.
:rtype: Type[ModelSerializer]
"""
return AccountSerializer
###########################################################################
# Transformation methods
###########################################################################
[docs]
def manifest_to_django_orm(self) -> dict:
"""
Transform the Smarter API Account manifest into a Django ORM model.
"""
metadata = super().manifest_to_django_orm()
config_dump = self.manifest.spec.config.model_dump()
config_dump = self.camel_to_snake(config_dump)
if not isinstance(config_dump, dict):
raise SAMAccountBrokerError(
message=f"Invalid config dump for {self.kind} manifest: {config_dump}",
thing=self.kind,
command=SmarterJournalCliCommands.APPLY,
)
if self.brokered_account is None:
raise SAMBrokerErrorNotReady(
f"Account not set for {self.kind} broker. Cannot apply.",
thing=self.thing,
command=SmarterJournalCliCommands.APPLY,
)
if self.manifest is None:
raise SAMBrokerErrorNotReady(
f"Manifest not set for {self.kind} broker. Cannot apply.",
thing=self.thing,
command=SmarterJournalCliCommands.APPLY,
)
# Convert tags (list[str]) to set for TaggableManager compatibility
return {
**metadata,
"account": self.brokered_account,
**config_dump,
}
[docs]
def django_orm_to_manifest_dict(self) -> dict:
"""
Converts a Django ORM `Account` model instance into a Pydantic-compatible Smarter API Account manifest dictionary.
:returns: Dictionary formatted for Pydantic model consumption, suitable for serialization and API responses.
:rtype: dict
:raises SAMBrokerErrorNotReady: If the broker's account is not set.
:raises SAMAccountBrokerError: If the account data is invalid or cannot be converted.
.. note::
The output uses camelCase keys for compatibility with Pydantic models and API consumers.
**Example usage**::
manifest_dict = broker.django_orm_to_manifest_dict()
print(manifest_dict["apiVersion"], manifest_dict["kind"])
.. seealso::
- :meth:`manifest_to_django_orm`
- :class:`SAMAccount`
- :class:`Account`
.. versionchanged:: 1.0.0
Method now ensures camelCase conversion and excludes the primary key field.
"""
if self.brokered_account is None:
raise SAMBrokerErrorNotFound(
f"Account not set for {self.kind} broker. Cannot describe.",
thing=self.thing,
command=SmarterJournalCliCommands.DESCRIBE,
)
if self.manifest is None:
raise SAMBrokerErrorNotFound(
f"Manifest not set for {self.kind} broker. Cannot describe.",
thing=self.thing,
command=SmarterJournalCliCommands.DESCRIBE,
)
return self.manifest.model_dump()
###########################################################################
# Smarter manifest abstract method implementations
###########################################################################
@property
def ORMMetaModelClass(self) -> Type[Account]:
"""
Return the Django ORM meta model class for the broker.
:return: The Django ORM meta model class definition for the broker.
:rtype: Type[Account]
"""
return Account
@property
def ORMModelClass(self) -> Type[Account]:
"""
Get the Django ORM model class for the Smarter API Account.
:returns: The Django ORM `Account` model class.
:rtype: Type[Account]
"""
return Account
@property
def orm_instance(self) -> Optional[Account]:
"""
Return the Django ORM model instance for the broker.
:return: The Django ORM model instance for the broker.
:rtype: Optional[TimestampedModel]
"""
if self._orm_instance:
return self._orm_instance
if self.orm_meta_instance:
self._orm_instance = self.orm_meta_instance
return self._orm_instance
try:
logger.debug(
"%s.orm_instance() - attempting to retrieve %s instance for user=%s, name=%s",
self.formatted_class_name,
Account.__name__,
self.user,
self.name,
)
self._orm_instance = Account.get_cached_object(name=self.name)
if self._orm_instance:
logger.debug(
"%s.orm_instance() - retrieved %s instance: %s",
self.formatted_class_name,
Account.__name__,
serializers.serialize("json", [self._orm_instance]), # type: ignore[list-item]
)
else:
logger.debug(
"%s.orm_instance() - no %s instance found for name: %s",
self.formatted_class_name,
Account.__name__,
self.name,
)
return self._orm_instance
except Account.DoesNotExist:
logger.warning(
"%s.orm_instance() - %s instance does not exist for account=%s, name=%s",
self.formatted_class_name,
Account.__name__,
self.account,
self.name,
)
return None
@property
def SAMModelClass(self) -> Type[SAMAccount]:
"""
Return the Pydantic model class for the broker.
:return: The Pydantic model class definition for the broker.
:rtype: Type[SAMAccount]
"""
return SAMAccount
[docs]
def cache_invalidations(self) -> None:
"""
Handle broker specific cache invalidation logic. Invalidates
the cache for the `Account` and `UserProfile` models.
"""
logger.debug("%s.cache_invalidations() called.", self.formatted_class_name_cache_invalidations)
Account.get_cached_object(invalidate=True, pk=self.brokered_account.id) # type: ignore
UserProfile.get_cached_object(invalidate=True, account=self.brokered_account)
return super().cache_invalidations()
[docs]
def example_manifest(self, request: "HttpRequest", *args, **kwargs) -> SmarterJournaledJsonResponse:
"""
Return an example manifest for the Smarter API Account.
:returns: A JSON response containing an example Smarter API Account manifest.
:rtype: SmarterJournaledJsonResponse
See Also:
- :class:`SAMAccount`
- :class:`SAMAccountMetadata`
- :class:`SAMAccountSpec`
- :class:`SAMKeys`
- :class:`SAMMetadataKeys`
- :class:`SAMAccountSpecKeys`
"""
command = self.example_manifest.__name__
command = SmarterJournalCliCommands(command)
logger.debug("%s.example_manifest() called", self.formatted_class_name)
self.user = None
self.brokered_account = smarter_cached_objects.smarter_account
if not self.brokered_account:
raise SAMBrokerErrorNotReady(
f"Account not set for {self.kind} broker. Cannot get example manifest.",
thing=self.thing,
command=command,
)
return self.json_response_ok(command=command, data=self.manifest.model_dump())
[docs]
def get(self, request: "HttpRequest", *args, **kwargs) -> SmarterJournaledJsonResponse:
"""
get the manifest(s) for the Smarter API Account.
:param request: The HTTP request object.
:type request: "HttpRequest"
:param args: Additional positional arguments.
:param kwargs: Additional keyword arguments.
:returns: A JSON response containing the Smarter API Account manifest(s).
:rtype: SmarterJournaledJsonResponse
:raises SAMBrokerErrorNotReady: If the broker's account is not set.
:raises SAMAccountBrokerError: If there is an error retrieving or serializing the account data.
"""
# name: str = None, all_objects: bool = False, tags: str = None
command = self.get.__name__
command = SmarterJournalCliCommands(command)
logger.debug("%s.get() called", self.formatted_class_name)
data = []
if not self.user.is_superuser:
raise SAMAccountBrokerError(
message="Only superusers can view accounts.",
thing=self.kind,
command=command,
)
if self.brokered_account is None:
raise SAMBrokerErrorNotReady(
f"Account not set for {self.kind} broker. Cannot get.",
thing=self.thing,
command=command,
)
# returns Optional[list[dict[str, str]]]:
# [
# {"name": "accountNumber", "type": "CharField"},
# {"name": "companyName", "type": "CharField"},
# {"name": "createdAt", "type": "DateTimeField"},
# {"name": "updatedAt", "type": "DateTimeField"},
# ]
model_titles = self.get_model_titles(serializer=AccountSerializer())
# generate a QuerySet of PluginMeta objects that match our search criteria
account = Account.get_cached_object(pk=self.brokered_account.id)
accounts = [account] if account else []
# iterate over the QuerySet and use the manifest controller to create a Pydantic model dump for each Plugin
for account in accounts:
try:
logger.debug("%s.get() processing Account: %s", self.formatted_class_name, account)
self.brokered_account = account
model_dump = AccountSerializer(account).data
camel_cased_model_dump = self.snake_to_camel(model_dump)
data.append(camel_cased_model_dump)
except Exception as e:
logger.error("Error in %s: %s", command, e)
return self.json_response_err(command=command, e=e)
data = {
SAMKeys.APIVERSION.value: self.api_version,
SAMKeys.KIND.value: self.kind,
SAMMetadataKeys.NAME.value: self.brokered_account.account_number,
SAMKeys.METADATA.value: {"count": len(data)},
SCLIResponseGet.KWARGS.value: kwargs,
SCLIResponseGet.DATA.value: {
SCLIResponseGetData.TITLES.value: model_titles,
SCLIResponseGetData.ITEMS.value: data,
},
}
return self.json_response_ok(command=command, data=data)
[docs]
def apply(self, request: "HttpRequest", *args, **kwargs) -> SmarterJournaledJsonResponse:
"""
Applies the manifest by copying its data to the Django ORM `Account` model and saving the model to the database.
.. note::
tags are handled separately because they are of type TaggableManager and
require a different method to set them.
:param request: The HTTP request object.
:type request: "HttpRequest"
:param args: Additional positional arguments.
:param kwargs: Additional keyword arguments.
:returns: A JSON response indicating the result of the apply operation.
:rtype: SmarterJournaledJsonResponse
:raises SAMBrokerErrorNotReady: If the broker's account is not set.
:raises SAMBrokerError: If an error occurs during the apply process.
.. important::
Calls ``super().apply()`` to ensure the manifest is loaded and validated before applying changes.
.. caution::
Fields that are not editable (such as ``id``, ``created_at``, ``updated_at``, and ``account_number``) are removed from the data before saving.
**Example usage**::
response = broker.apply(request)
print(response.status_code, response.data)
.. seealso::
- :meth:`manifest_to_django_orm`
- :meth:`django_orm_to_manifest_dict`
"""
logger.debug("%s.apply() called", self.formatted_class_name)
super().apply(request, kwargs)
command = self.apply.__name__
command = SmarterJournalCliCommands(command)
if not self.user.is_superuser:
raise SAMAccountBrokerError(
message="Only superusers can apply account manifests.",
thing=self.kind,
command=command,
)
readonly_fields = ["id", "created_at", "updated_at", "account_number", "tags"]
if not self.manifest:
raise SAMBrokerErrorNotReady(
f"Manifest not set for {self.kind} broker. Cannot apply.",
thing=self.thing,
command=command,
)
if self.brokered_account is None:
self.brokered_account = Account()
try:
data = self.manifest_to_django_orm()
tags = data.get("tags", [])
for field in readonly_fields:
logger.debug(
"%s.apply() Removing readonly field %s from data for %s",
self.formatted_class_name,
field,
self.kind,
)
data.pop(field, None)
for key, value in data.items():
setattr(self.brokered_account, key, value)
logger.debug("%s.apply() Setting %s to %s", self.formatted_class_name, key, value)
logger.debug(
"%s.apply() Saving %s: %s",
self.formatted_class_name,
self.brokered_account,
serializers.serialize("json", [self.brokered_account]),
)
self.brokered_account.save()
self.brokered_account.tags.set(tags)
tags = set(self.manifest.metadata.tags) if self.manifest.metadata.tags else set()
self.brokered_account.tags.set(tags)
self.brokered_account.refresh_from_db()
logger.debug(
"%s.apply() Saved %s with ID %s: %s",
self.formatted_class_name,
self.brokered_account,
self.brokered_account.id,
serializers.serialize("json", [self.brokered_account]),
)
except Exception as e:
tb = traceback.format_exc()
raise SAMBrokerError(message=f"Error in {command}: {e}\n{tb}", thing=self.kind, command=command) from e
self.cache_invalidations()
return self.json_response_ok(command=command, data=self.to_json())
[docs]
def chat(self, request: "HttpRequest", *args, **kwargs) -> SmarterJournaledJsonResponse:
"""
Chat functionality is not implemented for the Smarter API Account.
:param request: The HTTP request object.
:type request: "HttpRequest"
:param args: Additional positional arguments.
:param kwargs: Additional keyword arguments.
:raises SAMBrokerErrorNotImplemented: Always raised to indicate that chat is not implemented.
:returns: A JSON response indicating that chat is not implemented.
:rtype: SmarterJournaledJsonResponse
"""
logger.debug("%s.chat() called", self.formatted_class_name)
command = self.chat.__name__
command = SmarterJournalCliCommands(command)
raise SAMBrokerErrorNotImplemented(message="Chat not implemented", thing=self.kind, command=command)
[docs]
def describe(self, request: "HttpRequest", *args, **kwargs) -> SmarterJournaledJsonResponse:
"""
Describe the manifest for the Smarter API Account.
:param request: The HTTP request object.
:type request: "HttpRequest"
:param args: Additional positional arguments.
:param kwargs: Additional keyword arguments.
:raises SAMBrokerErrorNotReady: Raised when no account is found.
:returns: A JSON response with the manifest description.
:rtype: SmarterJournaledJsonResponse
"""
command = command = self.describe.__name__
command = SmarterJournalCliCommands(command)
logger.debug("%s.describe() called for %s", self.formatted_class_name, self.name)
if not self.user.is_superuser:
raise SAMAccountBrokerError(
message="Only superusers can view accounts.",
thing=self.kind,
command=command,
)
if not self.brokered_account:
raise SAMBrokerErrorNotFound(message="No account found", thing=self.kind, command=command)
try:
data = self.django_orm_to_manifest_dict()
logger.debug(
"%s.describe() returning manifest for %s: %s",
self.formatted_class_name,
self.name,
json.dumps(data, indent=4),
)
return self.json_response_ok(command=command, data=data)
except Exception as e:
raise SAMBrokerError(message=f"Error in {command}: {str(e)}", thing=self.kind, command=command) from e
[docs]
def delete(self, request: "HttpRequest", *args, **kwargs) -> SmarterJournaledJsonResponse:
"""
.. attention::
Delete functionality is not implemented for the Smarter API Account.
:param request: The HTTP request object.
:type request: "HttpRequest"
:param args: Additional positional arguments.
:param kwargs: Additional keyword arguments.
:raises SAMBrokerErrorNotImplemented: Always raised to indicate that delete is not implemented.
:returns: A JSON response indicating that delete is not implemented.
:rtype: SmarterJournaledJsonResponse
"""
logger.debug("%s.delete() called", self.formatted_class_name)
command = self.delete.__name__
command = SmarterJournalCliCommands(command)
raise SAMBrokerErrorNotImplemented(message="Delete not implemented", thing=self.kind, command=command)
[docs]
def deploy(self, request: "HttpRequest", *args, **kwargs) -> SmarterJournaledJsonResponse:
"""
.. attention::
Deploy functionality is not implemented for the Smarter API Account.
:param request: The HTTP request object.
:type request: "HttpRequest"
:param args: Additional positional arguments.
:param kwargs: Additional keyword arguments.
:raises SAMBrokerErrorNotImplemented: Always raised to indicate that deploy is not implemented.
:returns: A JSON response indicating that deploy is not implemented.
:rtype: SmarterJournaledJsonResponse
"""
logger.debug("%s.deploy() called", self.formatted_class_name)
command = self.deploy.__name__
command = SmarterJournalCliCommands(command)
raise SAMBrokerErrorNotImplemented(message="Deploy not implemented", thing=self.kind, command=command)
[docs]
def undeploy(self, request: "HttpRequest", *args, **kwargs) -> SmarterJournaledJsonResponse:
"""
.. attention::
Undeploy functionality is not implemented for the Smarter API Account.
:param request: The HTTP request object.
:type request: "HttpRequest"
:param args: Additional positional arguments.
:param kwargs: Additional keyword arguments.
:raises SAMBrokerErrorNotImplemented: Always raised to indicate that undeploy is not implemented.
:returns: A JSON response indicating that undeploy is not implemented.
:rtype: SmarterJournaledJsonResponse
"""
logger.debug("%s.undeploy() called", self.formatted_class_name)
command = self.undeploy.__name__
command = SmarterJournalCliCommands(command)
raise SAMBrokerErrorNotImplemented(message="Undeploy not implemented", thing=self.kind, command=command)
[docs]
def logs(self, request: "HttpRequest", *args, **kwargs) -> SmarterJournaledJsonResponse:
"""
.. attention::
Logs functionality is not implemented for the Smarter API Account.
:param request: The HTTP request object.
:type request: "HttpRequest"
:param args: Additional positional arguments.
:param kwargs: Additional keyword arguments.
:returns: A JSON response indicating that logs is not implemented.
:rtype: SmarterJournaledJsonResponse
"""
logger.debug("%s.logs() called", self.formatted_class_name)
command = self.logs.__name__
command = SmarterJournalCliCommands(command)
data = {}
return self.json_response_ok(command=command, data=data)