Skip to content

Conversation

@Gleekzone
Copy link
Contributor

Se elimina código de mongo incluido en rest-api, esto para hacer compatible con redis.

Aun este código no esta listo para ser revisado, hay mucho código que se agrego para pruebas, pero se va a limpiar. Sobre todo para probar redis. Estará como draft, pero pueden ir agregando comentarios si lo desean.

@Gleekzone Gleekzone self-assigned this Dec 11, 2020
agave/filters.py Outdated
Comment on lines 75 to 90
exclude_fields = {
'created_before',
'created_after',
'active',
'limit',
'page_size',
'key',
}
fields = query.dict(exclude=exclude_fields)
fields = {**fields, **kwargs}
if 'count' in fields:
del fields['count']
if len(filters) == 0:
filters = fields
return filters
return filters
Copy link
Member

Choose a reason for hiding this comment

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

isn't this redundant?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, se agrego en una función

from base64 import urlsafe_b64encode
from typing import Any

from rom import Column, Model, PrimaryKey
Copy link
Member

Choose a reason for hiding this comment

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

You can't have this here. It'll break if running on a server without redis

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

Comment on lines 31 to 38
def sanitize_item(item: Any) -> Any:
if isinstance(item, dt.date):
rv = item.isoformat()
elif hasattr(item, 'dict'):
rv = item.dict()
else:
rv = item
return rv
Copy link
Member

Choose a reason for hiding this comment

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

@Felipaoo I thought we agreed to use the one from cuenca-validations

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sip se va a agregar en cuenca-validations, solo lo puse para probar redis, este código no estará aqui.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, we are going to take the one from cuenca-validations and remove it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Listo el pr en cuenca-validations cuenca-mx/cuenca-validations#74

Comment on lines 11 to 23
class AccountRedis(RedisModel):
id = String(
default=uuid_field('US'),
required=True,
unique=True,
index=True,
keygen=util.IDENTITY,
)
name = String(required=True, index=True, keygen=util.IDENTITY)
user_id = String(required=True, index=True, keygen=util.IDENTITY)
secret = String()
created_at = DateTime(default=dt.datetime.utcnow, index=True)
deactivated_at = DateTime(default=DEFAULT_MISSING_DATE, index=True)
Copy link
Member

Choose a reason for hiding this comment

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

why is this in agave—instead of authed? Where else is it used?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Este es código solo lo puse para probar los tests con redis.

Copy link
Member

Choose a reason for hiding this comment

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

Podemos moverlo a las pruebas entonces?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Este codigo deberia estar en la ruta 'examples/chalicelib/models/accounts_redis.py' donde actualmente esta, ya que forma parte de los examples con Redis

Copy link
Member

@matin matin left a comment

Choose a reason for hiding this comment

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

still some work to do

@codecov
Copy link

codecov bot commented Dec 15, 2020

Codecov Report

Merging #25 (03f0eed) into main (895a81b) will not change coverage.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff            @@
##              main       #25   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            8        14    +6     
  Lines          238       326   +88     
  Branches        38        43    +5     
=========================================
+ Hits           238       326   +88     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
agave/blueprints/rest_api.py 100.00% <100.00%> (ø)
agave/exc.py 100.00% <100.00%> (ø)
agave/models/base.py 100.00% <100.00%> (ø)
agave/models/mongo/__init__.py 100.00% <100.00%> (ø)
agave/models/mongo/filters.py 100.00% <100.00%> (ø)
agave/models/mongo/mongo_model.py 100.00% <100.00%> (ø)
agave/models/redis/__init__.py 100.00% <100.00%> (ø)
agave/models/redis/filters.py 100.00% <100.00%> (ø)
agave/models/redis/redis_model.py 100.00% <100.00%> (ø)
... and 6 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 895a81b...03f0eed. Read the comment docs.

Copy link
Member

@felipao-mx felipao-mx left a comment

Choose a reason for hiding this comment

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

encontré algunos detalles, pero va por buen camino

agave/filters.py Outdated
Comment on lines 23 to 55
def generic_mongo_query(query: QueryParams) -> Q:
filters = Q()
if query.created_before:
filters &= Q(created_at__lt=query.created_before)
if query.created_after:
filters &= Q(created_at__gt=query.created_after)
fields = exclude_fields(query)
return filters & Q(**fields)


def generic_redis_query(query: QueryParams, **kwargs) -> Dict[str, Any]:
filters: Dict[str, Any] = dict()
if query.created_before or query.created_after:
# Restamos o sumamos un microsegundo porque la comparación
# aquí es inclusiva
created_at_lt = (
query.created_before.replace(tzinfo=None)
+ dt.timedelta(microseconds=-1)
if query.created_before
else None
)
created_at_gt = (
query.created_after.replace(tzinfo=None)
+ dt.timedelta(microseconds=1)
if query.created_after
else None
)
filters['created_at'] = (created_at_gt, created_at_lt)
fields = exclude_fields(query)
fields = {**fields, **kwargs}
if len(filters) == 0:
filters = fields
return filters
Copy link
Member

Choose a reason for hiding this comment

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

en este bloque de código se tendrán problemas al importar estas funciones cuando un proyecto no tenga instalado mongoengine o rom. Debes aplicar una estructura similar a lo que se hizo con los modelos

cls.model.objects.order_by("-created_at")
.filter(filters)
.limit(limit)
items, items_limit = cls.model.filter_limit(
Copy link
Member

Choose a reason for hiding this comment

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

items_limit -> has_more

Comment on lines 141 to 145
data = cls.model.retrieve(cls, id=id)
if self.user_id_filter_required():
id_query = id_query & Q(user_id=self.current_user_id)
data = cls.model.objects.get(id_query)
except DoesNotExist:
data = cls.model.retrieve(
cls, id=id, user_id=self.current_user_id
)
Copy link
Member

Choose a reason for hiding this comment

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

puedes simplificar un poco este bloquee de código

Copy link
Contributor Author

@Gleekzone Gleekzone Dec 17, 2020

Choose a reason for hiding this comment

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

Que exactamente quieres que se simplifique? @Felipaoo

@matin matin changed the title Compatible agave add redis Dec 22, 2020
except ValidationError as e:
return Response(e.json(), status_code=400)
except DoesNotExist:
except ObjectDoesNotExist:
Copy link
Member

Choose a reason for hiding this comment

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

exc.DoesNotExist

return self._dict(mongo_to_dict)

@classmethod
def retrieve(cls, id: str, user_id: Optional[str] = None):
Copy link
Member

Choose a reason for hiding this comment

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

refer to https://www.python.org/dev/peps/pep-0570/

should be

def retrieve(cls, id: str, *, user_id: Optional[str] = None):

This means that it, if user_id is passed in, is _must_ be passed in as a kwarg. retrieve('222', '3333')will be rejected and onlyretrieve('222', user_id='3333')` will be accepted. This forces greater clarity in the code and allows more flexibility if new kwargs are added in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

Comment on lines 20 to 22
query = Q(id=id)
if user_id:
query = query & Q(user_id=user_id)
Copy link
Member

Choose a reason for hiding this comment

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

this doesn't need to be in the try block. It's impossible for these lines to raise DoesNotExist

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

return id_obj

@classmethod
def count(cls, filters: Any) -> int:
Copy link
Member

Choose a reason for hiding this comment

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

shouldn't use Any. It's the equivalent of not having type hinting

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!


@classmethod
def has_more(cls, items: Any, limit: int):
has_more = items.limit(limit + 1).count() > limit
Copy link
Member

Choose a reason for hiding this comment

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

don't use the name of the method as a variable name

return count

@classmethod
def filter_limit(cls, filters: Any, limit: int):
Copy link
Member

Choose a reason for hiding this comment

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

should be def all(cls, filters, *, limit=DEFAULT_LIMIT)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

items = (
cls.objects.order_by("-created_at").filter(filters).limit(limit)
)
return items
Copy link
Member

Choose a reason for hiding this comment

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

change to: return items, has_more

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!

filters['created_at'] = (created_at_gt, created_at_lt)
fields = exclude_fields(query)
fields = {**fields, **kwargs}
if len(filters) == 0:
Copy link
Member

Choose a reason for hiding this comment

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

could this be if not filters?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!

Comment on lines 31 to 32
return filters
return filters
Copy link
Member

Choose a reason for hiding this comment

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

why are there two return statements?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed up!



def redis_to_dit(obj, exclude_fields: list = None) -> dict:
excluded = ['o_id']
Copy link
Member

Choose a reason for hiding this comment

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

should be a set

Copy link
Member

Choose a reason for hiding this comment

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

if it's always the same, it should be a module-level constant

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!

return value.decode('utf-8')


def redis_to_dit(obj, exclude_fields: list = None) -> dict:
Copy link
Member

Choose a reason for hiding this comment

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

typo

Copy link
Member

Choose a reason for hiding this comment

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

should use list for type-hinting. It should be List[key_type, value_type]

Copy link
Contributor Author

@Gleekzone Gleekzone Dec 23, 2020

Choose a reason for hiding this comment

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

@matin en lugar de devolver un dict debería devolver una lista?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!!

return self._dict(redis_to_dit)

@classmethod
def retrieve(cls, id: str, user_id: Optional[str] = None):
Copy link
Member

Choose a reason for hiding this comment

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

def retrieve(cls, id: str, *, user_id: Optional[str] = None):

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!

Comment on lines 5 to 9
from agave.models.mongo.filters import generic_mongo_query
from ..models.mongo_models import Account as AccountModel
from ..validators import AccountQuery, AccountRequest, AccountUpdateRequest
from .base import app
from agave.exc import ObjectDoesNotExist
Copy link
Member

Choose a reason for hiding this comment

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

agave in this case is a third-party package, which means it should be grouped with other third-party packages vs being grouped with local, relational packages

@matin
Copy link
Member

matin commented Jan 13, 2021

@Felipaoo ¿qué es el estatus de este PR?

@felipao-mx
Copy link
Member

quedó pendiente el review de @kerycdiaz

@felipao-mx felipao-mx requested a review from keryc January 13, 2021 01:25
@keryc
Copy link
Contributor

keryc commented Jan 13, 2021

Ouuu no recordaba que tenía que revisar este PR @Felipaoo mañana temprano lo hago vaa

@felipao-mx felipao-mx assigned felipao-mx and unassigned Gleekzone Jan 13, 2021
Copy link
Contributor

@keryc keryc left a comment

Choose a reason for hiding this comment

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

Algunas sugerencias y preguntas.


def _count(filters: Q):
count = cls.model.objects.filter(filters).count()
def _count(filters):
Copy link
Contributor

Choose a reason for hiding this comment

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

type hint

return dict(count=count)

def _all(query: QueryParams, filters: Q):
def _all(query: QueryParams, filters):
Copy link
Contributor

Choose a reason for hiding this comment

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

type hint a filters

Comment on lines +38 to +43
class RedisModel(BaseModel, Model):
meta = {'allow_inheritance': True}
o_id = PrimaryKey() # Para que podamos usar `id` en los modelos

def dict(self) -> DictStrAny:
return self._dict(redis_to_dict)
Copy link
Contributor

Choose a reason for hiding this comment

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

Si este modeo es muy similar a MongoModel, podemos tener una clase base no?

Copy link
Member

Choose a reason for hiding this comment

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

podrías ampliar un poco más tu idea?
Esta clase al igual que MongoModel heredan de BaseModel que es donde se definió los elementos en común. Ves algo más que podamos unificar?

Copy link
Contributor

Choose a reason for hiding this comment

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

Si claro

  • Ambos tienen el meta = {'allow_inheritance': True}
  • El retrieve o unico distinto en ambos es el query en si.
  • Incluso el count puede estar en el base si el cls.query y el cls.object tuvieran el mismo nombre.

Comment on lines +45 to +46
if not account:
raise NotFoundError('Not valid id')
Copy link
Contributor

Choose a reason for hiding this comment

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

Esto es innecesario, si no existe account debe entrar en el except mas arriba.

Comment on lines +15 to +22
@pytest.fixture
def user_id() -> str:
return 'US123456789'


@pytest.fixture
def another_user_id() -> str:
return 'US987654321'
Copy link
Contributor

Choose a reason for hiding this comment

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

No veo necesario usar un fixture para solo una variablle, puedes colocarla global en e archivo de tests no?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants