Small bugfixes

This commit is contained in:
Xavier Bustamante Talavera 2019-01-16 20:40:27 +01:00
parent 5fb1471d9b
commit b1aa79fd0a
10 changed files with 45 additions and 11 deletions

View file

@ -106,7 +106,7 @@ class Client(TealClient):
def login(self, email: str, password: str): def login(self, email: str, password: str):
assert isinstance(email, str) assert isinstance(email, str)
assert isinstance(password, str) assert isinstance(password, str)
return self.post({'email': email, 'password': password}, '/users/login', status=200) return self.post({'email': email, 'password': password}, '/users/login/', status=200)
def get_many(self, def get_many(self,
res: ResourceLike, res: ResourceLike,

View file

@ -16,9 +16,11 @@
<tbody> <tbody>
{% for erasure in erasures %} {% for erasure in erasures %}
<tr> <tr>
{% if erasure.parent.serial_number %}
<td> <td>
{{ erasure.parent.serial_number.upper() }} {{ erasure.parent.serial_number.upper() }}
</td> </td>
{% endif %}
<td> <td>
{{ erasure.parent.tags }} {{ erasure.parent.tags }}
</td> </td>

View file

@ -4,6 +4,7 @@ from uuid import UUID
from flask import current_app as app, request from flask import current_app as app, request
from sqlalchemy.util import OrderedSet from sqlalchemy.util import OrderedSet
from teal.marshmallow import ValidationError
from teal.resource import View from teal.resource import View
from ereuse_devicehub.db import db from ereuse_devicehub.db import db
@ -16,6 +17,8 @@ class EventView(View):
def post(self): def post(self):
"""Posts an event.""" """Posts an event."""
json = request.get_json(validate=False) json = request.get_json(validate=False)
if 'type' not in json:
raise ValidationError('Resource needs a type.')
e = app.resources[json['type']].schema.load(json) e = app.resources[json['type']].schema.load(json)
Model = db.Model._decl_class_registry.data[json['type']]() Model = db.Model._decl_class_registry.data[json['type']]()
event = Model(**e) event = Model(**e)

View file

@ -1,11 +1,13 @@
from contextlib import suppress from contextlib import suppress
from typing import Set from typing import Set
from boltons import urlutils
from sqlalchemy import BigInteger, Column, ForeignKey, Unicode, UniqueConstraint from sqlalchemy import BigInteger, Column, ForeignKey, Unicode, UniqueConstraint
from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import backref, relationship, validates from sqlalchemy.orm import backref, relationship, validates
from teal.db import DB_CASCADE_SET_NULL, Query, URL, check_lower from teal.db import DB_CASCADE_SET_NULL, Query, URL, check_lower
from teal.marshmallow import ValidationError from teal.marshmallow import ValidationError
from teal.resource import url_for_resource
from ereuse_devicehub.resources.agent.models import Organization from ereuse_devicehub.resources.agent.models import Organization
from ereuse_devicehub.resources.device.models import Device from ereuse_devicehub.resources.device.models import Device
@ -93,6 +95,21 @@ class Tag(Thing):
def type(self) -> str: def type(self) -> str:
return self.__class__.__name__ return self.__class__.__name__
@property
def url(self) -> urlutils.URL:
"""The URL where to GET this device."""
# todo this url only works for printable internal tags
return urlutils.URL(url_for_resource(Tag, item_id=self.id))
@property
def printable(self) -> bool:
"""Can the tag be printed by the user?
Only tags that are from the default organization can be
printed by the user.
"""
return Organization.get_default_org_id == self.org_id
def __repr__(self) -> str: def __repr__(self) -> str:
return '<Tag {0.id} org:{0.org_id} device:{0.device_id}>'.format(self) return '<Tag {0.id} org:{0.org_id} device:{0.device_id}>'.format(self)

View file

@ -1,5 +1,6 @@
from uuid import UUID from uuid import UUID
from boltons import urlutils
from boltons.urlutils import URL from boltons.urlutils import URL
from sqlalchemy import Column from sqlalchemy import Column
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
@ -39,3 +40,11 @@ class Tag(Thing):
def like_etag(self) -> bool: def like_etag(self) -> bool:
pass pass
@property
def printable(self) -> bool:
pass
@property
def url(self) -> urlutils.URL:
pass

View file

@ -1,4 +1,5 @@
from sqlalchemy.util import OrderedSet from sqlalchemy.util import OrderedSet
from marshmallow.fields import Boolean
from teal.marshmallow import SanitizedStr, URL from teal.marshmallow import SanitizedStr, URL
from ereuse_devicehub.marshmallow import NestedOn from ereuse_devicehub.marshmallow import NestedOn
@ -23,3 +24,5 @@ class Tag(Thing):
device = NestedOn(Device, dump_only=True) device = NestedOn(Device, dump_only=True)
org = NestedOn(Organization, collection_class=OrderedSet, only_query='id') org = NestedOn(Organization, collection_class=OrderedSet, only_query='id')
secondary = SanitizedStr(lower=True, description=m.Tag.secondary.comment) secondary = SanitizedStr(lower=True, description=m.Tag.secondary.comment)
printable = Boolean(dump_only=True, decsription=m.Tag.printable.__doc__)
url = URL(dump_only=True, description=m.Tag.url.__doc__)

View file

@ -20,7 +20,7 @@ class UserDef(Resource):
cli_commands = ((self.create_user, 'create-user'),) cli_commands = ((self.create_user, 'create-user'),)
super().__init__(app, import_name, static_folder, static_url_path, template_folder, super().__init__(app, import_name, static_folder, static_url_path, template_folder,
url_prefix, subdomain, url_defaults, root_path, cli_commands) url_prefix, subdomain, url_defaults, root_path, cli_commands)
self.add_url_rule('/login', view_func=login, methods={'POST'}) self.add_url_rule('/login/', view_func=login, methods={'POST'})
@argument('email') @argument('email')
@option('-a', '--agent', help='The name of an agent to create with the user.') @option('-a', '--agent', help='The name of an agent to create with the user.')

View file

@ -12,7 +12,7 @@ test_requires = [
setup( setup(
name='ereuse-devicehub', name='ereuse-devicehub',
version='0.2.0b2', version='0.2.0b3',
url='https://github.com/ereuse/devicehub-teal', url='https://github.com/ereuse/devicehub-teal',
project_urls=OrderedDict(( project_urls=OrderedDict((
('Documentation', 'http://devicheub.ereuse.org'), ('Documentation', 'http://devicheub.ereuse.org'),

View file

@ -22,7 +22,7 @@ def test_api_docs(client: Client):
'/devices/', '/devices/',
'/tags/', '/tags/',
'/snapshots/', '/snapshots/',
'/users/login', '/users/login/',
'/events/', '/events/',
'/lots/', '/lots/',
'/manufacturers/', '/manufacturers/',

View file

@ -74,7 +74,7 @@ def test_login_success(client: Client, app: Devicehub):
with app.app_context(): with app.app_context():
create_user() create_user()
user, _ = client.post({'email': 'foo@foo.com', 'password': 'foo'}, user, _ = client.post({'email': 'foo@foo.com', 'password': 'foo'},
uri='/users/login', uri='/users/login/',
status=200) status=200)
assert user['email'] == 'foo@foo.com' assert user['email'] == 'foo@foo.com'
assert UUID(b64decode(user['token'].encode()).decode()[:-1]) assert UUID(b64decode(user['token'].encode()).decode()[:-1])
@ -90,12 +90,12 @@ def test_login_failure(client: Client, app: Devicehub):
with app.app_context(): with app.app_context():
create_user() create_user()
client.post({'email': 'foo@foo.com', 'password': 'wrong pass'}, client.post({'email': 'foo@foo.com', 'password': 'wrong pass'},
uri='/users/login', uri='/users/login/',
status=WrongCredentials) status=WrongCredentials)
# Wrong URI # Wrong URI
client.post({}, uri='/wrong-uri', status=NotFound) client.post({}, uri='/wrong-uri', status=NotFound)
# Malformed data # Malformed data
client.post({}, uri='/users/login', status=ValidationError) client.post({}, uri='/users/login/', status=ValidationError)
client.post({'email': 'this is not an email', 'password': 'nope'}, client.post({'email': 'this is not an email', 'password': 'nope'},
uri='/users/login', uri='/users/login/',
status=ValidationError) status=ValidationError)