adding TagCode to tag named system

This commit is contained in:
Cayo Puigdefabregas 2021-03-08 12:39:17 +01:00
parent 0d7a098ac5
commit f331128741
6 changed files with 49 additions and 9 deletions

View File

@ -23,13 +23,34 @@ def get_inv():
raise ValueError("Inventory value is not specified") raise ValueError("Inventory value is not specified")
return INV return INV
def upgrade_data():
con = op.get_bind()
tags = con.execute(f"select id from {get_inv()}.tag")
i = 1
for c in tags:
id_tag = c.id
internal_id = i
i += 1
sql = f"update {get_inv()}.tag set internal_id='{internal_id}' where id='{id_tag}';"
con.execute(sql)
sql = f"CREATE SEQUENCE {get_inv()}.tag_internal_id_seq START {i};"
con.execute(sql)
def upgrade(): def upgrade():
op.drop_constraint('one tag id per organization', 'tag', schema=f'{get_inv()}') op.drop_constraint('one tag id per organization', 'tag', schema=f'{get_inv()}')
op.drop_constraint('one secondary tag per organization', 'tag', schema=f'{get_inv()}') op.drop_constraint('one secondary tag per organization', 'tag', schema=f'{get_inv()}')
op.create_primary_key('one tag id per owner', 'tag', ['id', 'owner_id'], schema=f'{get_inv()}'), op.create_primary_key('one tag id per owner', 'tag', ['id', 'owner_id'], schema=f'{get_inv()}'),
op.create_unique_constraint('one secondary tag per owner', 'tag', ['secondary', 'owner_id'], schema=f'{get_inv()}'), op.create_unique_constraint('one secondary tag per owner', 'tag', ['secondary', 'owner_id'], schema=f'{get_inv()}'),
op.add_column('tag', sa.Column('internal_id', sa.BigInteger(), nullable=False, op.add_column('tag', sa.Column('internal_id', sa.BigInteger(), nullable=True,
comment='The identifier of the tag for this database. Used only\n internally for software; users should not use this.\n'), schema=f'{get_inv()}') comment='The identifier of the tag for this database. Used only\n internally for software; users should not use this.\n'), schema=f'{get_inv()}')
upgrade_data()
op.alter_column('tag', sa.Column('internal_id', sa.BigInteger(), nullable=False,
comment='The identifier of the tag for this database. Used only\n internally for software; users should not use this.\n'), schema=f'{get_inv()}')
def downgrade(): def downgrade():
@ -38,5 +59,4 @@ def downgrade():
op.create_primary_key('one tag id per organization', 'tag', ['id', 'org_id'], schema=f'{get_inv()}'), op.create_primary_key('one tag id per organization', 'tag', ['id', 'org_id'], schema=f'{get_inv()}'),
op.create_unique_constraint('one secondary tag per organization', 'tag', ['secondary', 'org_id'], schema=f'{get_inv()}'), op.create_unique_constraint('one secondary tag per organization', 'tag', ['secondary', 'org_id'], schema=f'{get_inv()}'),
op.drop_column('tag', 'internal_id', schema=f'{get_inv()}') op.drop_column('tag', 'internal_id', schema=f'{get_inv()}')
op.drop_column('tag', 'internal_id', schema=f'{get_inv()}')
op.execute(f"DROP SEQUENCE {get_inv()}.tag_internal_id_seq;") op.execute(f"DROP SEQUENCE {get_inv()}.tag_internal_id_seq;")

View File

@ -15,6 +15,7 @@ from ereuse_devicehub.resources.agent.models import Organization
from ereuse_devicehub.resources.device.models import Device from ereuse_devicehub.resources.device.models import Device
from ereuse_devicehub.resources.models import Thing from ereuse_devicehub.resources.models import Thing
from ereuse_devicehub.resources.user.models import User from ereuse_devicehub.resources.user.models import User
from ereuse_devicehub.resources.utils import hascode
class Tags(Set['Tag']): class Tags(Set['Tag']):
@ -25,8 +26,10 @@ class Tags(Set['Tag']):
return ', '.join(format(tag, format_spec) for tag in self).strip() return ', '.join(format(tag, format_spec) for tag in self).strip()
class Tag(Thing): class Tag(Thing):
internal_id = Column(BigInteger, Sequence('tag_internal_id_seq'), unique=True, nulable=False) internal_id = Column(BigInteger, Sequence('tag_internal_id_seq'), unique=True, nullable=False)
internal_id.comment = """The identifier of the tag for this database. Used only internal_id.comment = """The identifier of the tag for this database. Used only
internally for software; users should not use this. internally for software; users should not use this.
""" """
@ -113,7 +116,7 @@ class Tag(Thing):
def url(self) -> urlutils.URL: def url(self) -> urlutils.URL:
"""The URL where to GET this device.""" """The URL where to GET this device."""
# todo this url only works for printable internal tags # todo this url only works for printable internal tags
return urlutils.URL(url_for_resource(Tag, item_id=self.id)) return urlutils.URL(url_for_resource(Tag, item_id=self.code))
@property @property
def printable(self) -> bool: def printable(self) -> bool:
@ -129,6 +132,10 @@ class Tag(Thing):
"""Return a SQLAlchemy filter expression for printable queries.""" """Return a SQLAlchemy filter expression for printable queries."""
return cls.org_id == Organization.get_default_org_id() return cls.org_id == Organization.get_default_org_id()
@property
def code(self) -> str:
return hascode.encode(self.internal_id)
def delete(self): def delete(self):
"""Deletes the tag. """Deletes the tag.
@ -157,7 +164,7 @@ class TagLinked(ValidationError):
message = 'The tag {} is linked to device {}.'.format(tag.id, tag.device.id) message = 'The tag {} is linked to device {}.'.format(tag.id, tag.device.id)
super().__init__(message, field_names=['device']) super().__init__(message, field_names=['device'])
class TagUnnamed(ValidationError): class TagUnnamed(ValidationError):
def __init__(self, id): def __init__(self, id):
message = 'This tag {} is unnamed tag. It is imposible delete.'.format(id) message = 'This tag {} is unnamed tag. It is imposible delete.'.format(id)

View File

@ -28,3 +28,4 @@ class Tag(Thing):
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__) printable = Boolean(dump_only=True, decsription=m.Tag.printable.__doc__)
url = URL(dump_only=True, description=m.Tag.url.__doc__) url = URL(dump_only=True, description=m.Tag.url.__doc__)
code = SanitizedStr(dump_only=True, description=m.Tag.internal_id.comment)

View File

@ -6,14 +6,16 @@ from teal.resource import View, url_for_resource
from ereuse_devicehub import auth from ereuse_devicehub import auth
from ereuse_devicehub.db import db from ereuse_devicehub.db import db
from ereuse_devicehub.query import things_response from ereuse_devicehub.query import things_response
from ereuse_devicehub.resources.utils import hascode
from ereuse_devicehub.resources.device.models import Device from ereuse_devicehub.resources.device.models import Device
from ereuse_devicehub.resources.tag import Tag from ereuse_devicehub.resources.tag import Tag
class TagView(View): class TagView(View):
def one(self, id): def one(self, code):
"""Gets the device from the named tag, /tags/namedtag.""" """Gets the device from the named tag, /tags/namedtag."""
tag = Tag.from_an_id(id).one() # type: Tag internal_id = hascode.decode(code.upper()) or -1
tag = Tag.query.filter_by(internal_id=internal_id).one() # type: Tag
if not tag.device: if not tag.device:
raise TagNotLinked(tag.id) raise TagNotLinked(tag.id)
return redirect(location=url_for_resource(Device, tag.device.id)) return redirect(location=url_for_resource(Device, tag.device.id))

View File

@ -0,0 +1,9 @@
from hashids import Hashids
from decouple import config
ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
SECRET = config('TAG_HASH', '')
hascode = Hashids(SECRET, min_length=5, alphabet=ALPHABET)
def hashids(id):
return hascode.encode(id)

View File

@ -26,6 +26,7 @@ from tests.conftest import file
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_create_tag(user: UserClient): def test_create_tag(user: UserClient):
"""Creates a tag specifying a custom organization.""" """Creates a tag specifying a custom organization."""
# import pdb; pdb.set_trace()
org = Organization(name='bar', tax_id='bartax') org = Organization(name='bar', tax_id='bartax')
tag = Tag(id='bar-1', org=org, provider=URL('http://foo.bar'), owner_id=user.user['id']) tag = Tag(id='bar-1', org=org, provider=URL('http://foo.bar'), owner_id=user.user['id'])
db.session.add(tag) db.session.add(tag)
@ -33,7 +34,7 @@ def test_create_tag(user: UserClient):
tag = Tag.query.one() tag = Tag.query.one()
assert tag.id == 'bar-1' assert tag.id == 'bar-1'
assert tag.provider == URL('http://foo.bar') assert tag.provider == URL('http://foo.bar')
res, _ = user.get(res=Tag, item=tag.id, status=422) res, _ = user.get(res=Tag, item=tag.code, status=422)
assert res['type'] == 'TagNotLinked' assert res['type'] == 'TagNotLinked'