Skip to content
This repository has been archived by the owner on Apr 14, 2021. It is now read-only.

Initial Version #2

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
48 changes: 48 additions & 0 deletions tests/unit/tests_models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import json
import os

from mock import patch
from requests import HTTPError, Response

from tests.unit.tests_client import HTTPPrettyTestMixin


class ModelTestMixin(HTTPPrettyTestMixin):
MODEL_KLASS = None

def sample_list(self):
return self._read_sample(action='list')

def sample_detail(self):
return self._read_sample(action='detail')

def _read_sample(self, action):
path = os.path.dirname(__file__)
filename = f'{self.MODEL_KLASS._RESOURCE_NAME}.{action}.json'
with open(os.path.join(path, 'samples', filename), 'r') as f:
return json.loads(f.read())

def patch_get(self, data):
return patch('tsuru.client.TsuruClient.get', return_value=data)

def patch_get_error(self, content=None, status_code=404):
response = Response()
response.status_code = status_code
response._content = content
error = HTTPError(response=response)
return patch('tsuru.client.TsuruClient.get', side_effect=error)

def test_fields(self):
raise NotImplementedError()

def test_invalid_field(self):
raise NotImplementedError()

def test_list(self):
raise NotImplementedError()

def test_detail(self):
raise NotImplementedError()

def test_not_found(self):
raise NotImplementedError()
2 changes: 2 additions & 0 deletions tsuru/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__all__ = (
)
96 changes: 96 additions & 0 deletions tsuru/models/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from datetime import datetime, timezone

from requests import HTTPError

from tsuru import client, exceptions


class BaseModel:
_RESOURCE_NAME = None
_PK_FIELD = 'ID'

def __init__(self, data):
if not isinstance(data, dict):
raise exceptions.UnexpectedDataFormat()
if self._PK_FIELD not in data:
raise exceptions.UnexpectedDataFormat(f"Missing mandatory {self._PK_FIELD}")
self._data = data
self._detailed = False

@property
def pk(self):
return self._get(self._PK_FIELD)

def _get(self, value):
try:
return self._data[value]
except (KeyError, TypeError):
if not self._detailed:
self.refresh()
return self._get(value)
raise exceptions.UnexpectedDataFormat()

def refresh(self):
if not self._detailed:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this? why I cannot refresh the same model if _detailed is True?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should rename this (refresh looks like refresh_from_db on a django model).

self._data = self.get(pk=self.pk)._data
self._detailed = True

@classmethod
def get(cls, pk):
try:
data = client.TsuruClient.get(resource=cls._RESOURCE_NAME, pk=pk)
except HTTPError as e:
if e.response.status_code == 404:
raise exceptions.DoesNotExist()
raise

obj = cls(data=data)
obj._detailed = True
return obj

@classmethod
def list(cls):
data = client.TsuruClient.get(resource=cls._RESOURCE_NAME)
for item in data:
yield cls(data=item)

def _bound_list(self, resource_class, params=None):
data = client.TsuruClient.get(
resource=resource_class._RESOURCE_NAME,
from_pk=self.pk,
from_resource=self._RESOURCE_NAME,
params=params,
)
for item in data:
yield resource_class(data=item)

def _bound_detail(self, resource_class, pk, params=None):
data = client.TsuruClient.get(
resource=resource_class._RESOURCE_NAME,
from_pk=self.pk,
from_resource=self._RESOURCE_NAME,
pk=pk,
params=params,
)
for item in data:
yield resource_class(data=item)

@classmethod
def _parse_date(cls, date_str):
if date_str and not date_str.startswith('0001-01-01'):
fmt = '.%f' if '.' in date_str else ''
return datetime.strptime(date_str, f'%Y-%m-%dT%H:%M:%S{fmt}Z').astimezone(timezone.utc)
return None


class UnsupportedModelMixin:
@classmethod
def get(cls, pk):
raise exceptions.UnsupportedModelException()

@classmethod
def list(cls):
raise exceptions.UnsupportedModelException()

def _detail(self, resource_class):
raise exceptions.UnsupportedModelException()