Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
aaxelb committed Feb 22, 2024
1 parent cc0e075 commit 8f32af9
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 41 deletions.
13 changes: 13 additions & 0 deletions addon_service/common/opaque.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import base64


def make_opaque(key: str) -> str:
"""make a reversible opaque id for the given key
NOT secure in any way, just meant to discourage applying semantic value to ids
"""
return base64.urlsafe_b64encode(str(key).encode()).decode()


def unmake_opaque(opaque_key: str) -> str:
return base64.urlsafe_b64decode(opaque_key.encode()).decode()
2 changes: 1 addition & 1 deletion addon_service/configured_storage_addon/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def connected_operations(self) -> list[StorageOperationModel]:
return [
StorageOperationModel(_imp)
for _imp in get_operation_implementations(
self.external_storage_service.storage_implementation_cls,
self.base_account.external_storage_service.storage_implementation_cls,
capabilities=self.connected_capabilities_enum,
)
]
Expand Down
9 changes: 8 additions & 1 deletion addon_service/management/commands/fill_garbage.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from addon_service import models as db
from addon_service.capability.enums import IntStorageCapability
from addon_service.common.invocation import IntInvocationStatus
from addon_service.storage_imp.known import IntStorageImp


Expand Down Expand Up @@ -50,4 +51,10 @@ def handle_label(self, label, **options):
authorized_resource=_ir,
connected_capabilities=[IntStorageCapability.ACCESS],
)
return str(_csa)
_soi = db.StorageOperationInvocation.objects.create(
int_invocation_status=IntInvocationStatus.DONE,
operation_identifier="foo",
operation_args={},
thru_addon=_csa,
)
return str(_soi)
49 changes: 49 additions & 0 deletions addon_service/storage_imp/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import dataclasses

from addon_service.common.opaque import (
make_opaque,
unmake_opaque,
)
from addon_service.storage_imp.known import (
ApiStorageImp,
StorageImp,
)


# not a database model; just a convenience wrapper for
# AddonOperationImplementation to give the serializer
@dataclasses.dataclass
class StorageImpModel:
interface_imp: type

@classmethod
def get_by_pk(cls, pk: str) -> "StorageImpModel":
_interface_imp = StorageImp[unmake_opaque(pk)].value
return cls(_interface_imp)

@property
def pk(self) -> str:
return make_opaque(self.name)

@property
def name(self) -> str:
return ApiStorageImp.from_original_enum_value(self.interface_imp).value

@property
def docstring(self):
return self.imp.operation.docstring

@property
def implementation_docstring(self):
return self.imp.implementation_docstring

@property
def capability(self):
return self.imp.operation.capability

@property
def storage_implementation(self) -> str:
# TODO: related storage-implementation resource
return ApiStorageImp.from_original_enum_value(
StorageImp(self.imp.implementation_cls)
).value
42 changes: 29 additions & 13 deletions addon_service/storage_imp/serializers.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
from addon_service.common.enums.serializers import (
DualEnumsChoiceField,
DualEnumsMultipleChoiceField,
)
from addon_service.storage_imp.known import (
ApiStorageImp,
IntStorageImp,
from rest_framework_json_api import serializers
from rest_framework_json_api.relations import (
ResourceRelatedField,
SkipDataMixin,
)

from .models import StorageImpModel


# note: most other serializers get resource_type from the db model
# (for rest_framework_json_api to behave correctly) but operations
# are declared as dataclasses, not stored in a database
RESOURCE_TYPE = "storage-imps"
SELF_LINK_VIEW_NAME = f"{RESOURCE_TYPE}-detail"


class StorageImpSerializer(serializers.Serializer):
url = serializers.HyperlinkedIdentityField(view_name=SELF_LINK_VIEW_NAME)
name = serializers.CharField(read_only=True)
docstring = serializers.CharField(read_only=True)
# TODO operation_parameters = ListField(

class StorageImpChoiceField(DualEnumsChoiceField):
external_enum = ApiStorageImp
internal_enum = IntStorageImp
class JSONAPIMeta:
resource_name = RESOURCE_TYPE


class StorageImpMultipleChoiceField(DualEnumsMultipleChoiceField):
external_enum = ApiStorageImp
internal_enum = IntStorageImp
class StorageImpRelationField(SkipDataMixin, ResourceRelatedField):
def __init__(self, **kwargs):
return super().__init__(
many=True,
read_only=True,
model=StorageImpModel,
**kwargs,
)
53 changes: 39 additions & 14 deletions addon_service/storage_operation/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import dataclasses

from addon_service.common.opaque import (
make_opaque,
unmake_opaque,
)
from addon_service.storage_imp.known import (
ApiStorageImp,
StorageImp,
Expand All @@ -7,28 +13,47 @@

# not a database model; just a convenience wrapper for
# AddonOperationImplementation to give the serializer
@dataclasses.dataclass
class StorageOperationModel:
def __init__(self, imp: AddonOperationImplementation):
self.imp = imp
self.name = imp.operation.name
self.docstring = imp.operation.docstring
self.implementation_docstring = imp.implementation_docstring
self.capability = imp.operation.capability

@property
def pk(self):
_imp_api_name = ApiStorageImp.from_original_enum_value(
self.imp.implementation_cls
).value
return "-".join((_imp_api_name, self.name))
imp: AddonOperationImplementation

@classmethod
def get_by_pk(cls, pk: str) -> "StorageOperationModel":
_imp_name, _operation_name = pk.split("-") # TODO: reconsider
_imp_name, _operation_name = unmake_opaque(pk).split("-") # TODO: reconsider
_interface_imp = StorageImp[_imp_name].value # TODO: try, 404
return cls(
AddonOperationImplementation.by_operation_name(
_interface_imp,
_operation_name,
)
)

@property
def pk(self) -> str:
_imp_api_name = ApiStorageImp.from_original_enum_value(
self.imp.implementation_cls
).value
return make_opaque("-".join((_imp_api_name, self.name)))

@property
def name(self):
return self.imp.operation.name

@property
def docstring(self):
return self.imp.operation.docstring

@property
def implementation_docstring(self):
return self.imp.implementation_docstring

@property
def capability(self):
return self.imp.operation.capability

@property
def storage_implementation(self) -> str:
# TODO: related storage-implementation resource
return ApiStorageImp.from_original_enum_value(
StorageImp(self.imp.implementation_cls)
).value
11 changes: 6 additions & 5 deletions addon_service/storage_operation/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@

class StorageOperationSerializer(serializers.Serializer):
url = serializers.HyperlinkedIdentityField(view_name=SELF_LINK_VIEW_NAME)
name = serializers.CharField()
docstring = serializers.CharField()
implementation_docstring = serializers.CharField()
capability = StorageCapabilityChoiceField()
name = serializers.CharField(read_only=True)
docstring = serializers.CharField(read_only=True)
implementation_docstring = serializers.CharField(read_only=True)
capability = StorageCapabilityChoiceField(read_only=True)
storage_implementation = serializers.CharField(read_only=True)
# TODO operation_parameters = ListField(

class JSONAPIMeta:
Expand All @@ -31,8 +32,8 @@ class JSONAPIMeta:
class StorageOperationRelationField(SkipDataMixin, ResourceRelatedField):
def __init__(self, **kwargs):
return super().__init__(
**kwargs,
many=True,
read_only=True,
model=StorageOperationModel,
**kwargs,
)
15 changes: 10 additions & 5 deletions addon_service/storage_operation_invocation/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from django.db import models

from addon_service.common.base_model import AddonsServiceBaseModel
from addon_service.common.invocation import IntInvocationStatus
from addon_service.common.invocation import (
IntInvocationStatus,
InvocationStatus,
)


class StorageOperationInvocation(AddonsServiceBaseModel):
Expand All @@ -10,7 +13,7 @@ class StorageOperationInvocation(AddonsServiceBaseModel):
default=IntInvocationStatus.STARTING,
)
operation_identifier = models.TextField() # TODO
operation_args = models.JSONField()
operation_args = models.JSONField(default=dict, blank=True)
thru_addon = models.ForeignKey("ConfiguredStorageAddon", on_delete=models.CASCADE)

class Meta:
Expand All @@ -19,8 +22,10 @@ class Meta:
]

class JSONAPIMeta:
resource_name = "addon-operation-invocations"
resource_name = "storage-operation-invocations"

@property
def invocation_status(self):
return IntInvocationStatus(self.int_invocation_status).to_original_enum()
def invocation_status(self) -> InvocationStatus:
return InvocationStatus(
IntInvocationStatus(self.int_invocation_status).to_original_enum()
)
5 changes: 3 additions & 2 deletions addon_service/storage_operation_invocation/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ class StorageOperationInvocationSerializer(serializers.HyperlinkedModelSerialize
)

included_serializers = {
"thru_addon": ("addon_service.serializers.ConfiguredStorageAddon"),
"thru_addon": "addon_service.serializers.ConfiguredStorageAddonSerializer",
}

class Meta:
model = StorageOperationInvocation
fields = [
"url",
"thru_addon",
"authorized_storage_accounts",
"operation_identifier",
"operation_args",
]
2 changes: 2 additions & 0 deletions addon_toolkit/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ class AddonOperationDeclaration:

@property
def name(self):
# TODO: language tag
return self.operation_fn.__name__

@property
def docstring(self) -> str:
# TODO: language tag
# TODO: consider docstring param on operation decorators, allow overriding __doc__
return self.operation_fn.__doc__ or ""

Expand Down

0 comments on commit 8f32af9

Please sign in to comment.