adding TagCode to tag named system
This commit is contained in:
parent
0d7a098ac5
commit
f331128741
|
@ -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;")
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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)
|
|
@ -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'
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in New Issue