From 929ad8e2e944a99c2dba64beb91dbd49798ef16e Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 24 Feb 2021 20:18:13 +0100 Subject: [PATCH 01/22] . --- ereuse_devicehub/resources/tag/view.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ereuse_devicehub/resources/tag/view.py b/ereuse_devicehub/resources/tag/view.py index 2376635e..6be6179e 100644 --- a/ereuse_devicehub/resources/tag/view.py +++ b/ereuse_devicehub/resources/tag/view.py @@ -16,8 +16,10 @@ class TagView(View): """Creates a tag.""" num = request.args.get('num', type=int) if num: + # create unnamed tag res = self._create_many_regular_tags(num) else: + # create named tag res = self._post_one() return res @@ -35,6 +37,7 @@ class TagView(View): def _create_many_regular_tags(self, num: int): tags_id, _ = g.tag_provider.post('/', {}, query=[('num', num)]) tags = [Tag(id=tag_id, provider=g.inventory.tag_provider) for tag_id in tags_id] + import pdb; pdb.set_trace() db.session.add_all(tags) db.session().final_flush() response = things_response(self.schema.dump(tags, many=True, nested=1), code=201) @@ -43,6 +46,7 @@ class TagView(View): def _post_one(self): # todo do we use this? + # import pdb; pdb.set_trace() t = request.get_json() tag = Tag(**t) if tag.like_etag(): @@ -58,6 +62,7 @@ class TagDeviceView(View): def one(self, id): """Gets the device from the tag.""" + import pdb; pdb.set_trace() tag = Tag.from_an_id(id).one() # type: Tag if not tag.device: raise TagNotLinked(tag.id) @@ -68,6 +73,7 @@ class TagDeviceView(View): # noinspection PyMethodOverriding def put(self, tag_id: str, device_id: str): """Links an existing tag with a device.""" + import pdb; pdb.set_trace() tag = Tag.from_an_id(tag_id).one() # type: Tag if tag.device_id: if tag.device_id == device_id: From aa3368838294d0f69a5ee8304c32adac34693880 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 24 Feb 2021 23:22:36 +0100 Subject: [PATCH 02/22] fixed 2 bugs --- ereuse_devicehub/resources/tag/view.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/ereuse_devicehub/resources/tag/view.py b/ereuse_devicehub/resources/tag/view.py index 6be6179e..6fd46770 100644 --- a/ereuse_devicehub/resources/tag/view.py +++ b/ereuse_devicehub/resources/tag/view.py @@ -11,6 +11,13 @@ from ereuse_devicehub.resources.tag import Tag class TagView(View): + def one(self, id): + """Gets the device from the named tag, /tags/namedtag.""" + tag = Tag.from_an_id(id).one() # type: Tag + if not tag.device: + raise TagNotLinked(tag.id) + return redirect(location=url_for_resource(Device, tag.device.id)) + @auth.Auth.requires_auth def post(self): """Creates a tag.""" @@ -37,7 +44,6 @@ class TagView(View): def _create_many_regular_tags(self, num: int): tags_id, _ = g.tag_provider.post('/', {}, query=[('num', num)]) tags = [Tag(id=tag_id, provider=g.inventory.tag_provider) for tag_id in tags_id] - import pdb; pdb.set_trace() db.session.add_all(tags) db.session().final_flush() response = things_response(self.schema.dump(tags, many=True, nested=1), code=201) @@ -45,8 +51,6 @@ class TagView(View): return response def _post_one(self): - # todo do we use this? - # import pdb; pdb.set_trace() t = request.get_json() tag = Tag(**t) if tag.like_etag(): @@ -62,7 +66,6 @@ class TagDeviceView(View): def one(self, id): """Gets the device from the tag.""" - import pdb; pdb.set_trace() tag = Tag.from_an_id(id).one() # type: Tag if not tag.device: raise TagNotLinked(tag.id) @@ -71,9 +74,9 @@ class TagDeviceView(View): return app.resources[Device.t].schema.jsonify(tag.device) # noinspection PyMethodOverriding + @auth.Auth.requires_auth def put(self, tag_id: str, device_id: str): """Links an existing tag with a device.""" - import pdb; pdb.set_trace() tag = Tag.from_an_id(tag_id).one() # type: Tag if tag.device_id: if tag.device_id == device_id: @@ -81,7 +84,10 @@ class TagDeviceView(View): else: raise LinkedToAnotherDevice(tag.device_id) else: + # Check if this device exist for this woner + Device.query.filter_by(owner=g.user).filter_by(id=device_id).one() tag.device_id = device_id + db.session().final_flush() db.session.commit() return Response(status=204) From 689d0c48c81995f15e30e2a887cb2dd0885684a9 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 26 Feb 2021 11:28:32 +0100 Subject: [PATCH 03/22] change the model of tags from org to user --- .../6a2a939d5668_drop_unique_org_for_tag.py | 33 +++++++++++++++++++ ereuse_devicehub/resources/tag/model.py | 5 +-- ereuse_devicehub/resources/tag/view.py | 13 +++++++- tests/test_tag.py | 28 ++++++++++++---- 4 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py diff --git a/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py b/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py new file mode 100644 index 00000000..417c27f9 --- /dev/null +++ b/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py @@ -0,0 +1,33 @@ +"""drop unique org for tag + +Revision ID: 6a2a939d5668 +Revises: eca457d8b2a4 +Create Date: 2021-02-25 18:47:47.441195 + +""" +from alembic import op +from alembic import context + + +# revision identifiers, used by Alembic. +revision = '6a2a939d5668' +down_revision = 'eca457d8b2a4' +branch_labels = None +depends_on = None + + +def get_inv(): + INV = context.get_x_argument(as_dictionary=True).get('inventory') + if not INV: + raise ValueError("Inventory value is not specified") + return INV + +def upgrade(): + 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.create_unique_constraint('one tag id per owner', 'tag', ['id', 'owner_id'], schema=f'{get_inv()}') + + +def downgrade(): + op.create_unique_constraint('one tag id per organization', 'tag', ['id', 'org_id'], schema=f'{get_inv()}') + op.create_unique_constraint('one secondary tag per organization', 'tag', ['id', 'secondary'], schema=f'{get_inv()}') diff --git a/ereuse_devicehub/resources/tag/model.py b/ereuse_devicehub/resources/tag/model.py index 8aae5670..b5128830 100644 --- a/ereuse_devicehub/resources/tag/model.py +++ b/ereuse_devicehub/resources/tag/model.py @@ -97,8 +97,9 @@ class Tag(Thing): return url __table_args__ = ( - UniqueConstraint(id, org_id, name='one tag id per organization'), - UniqueConstraint(secondary, org_id, name='one secondary tag per organization') + UniqueConstraint(id, owner_id, name='one tag id per owner'), + # UniqueConstraint(id, org_id, name='one tag id per organization'), + # UniqueConstraint(secondary, org_id, name='one secondary tag per organization') ) @property diff --git a/ereuse_devicehub/resources/tag/view.py b/ereuse_devicehub/resources/tag/view.py index 6fd46770..2cb3009f 100644 --- a/ereuse_devicehub/resources/tag/view.py +++ b/ereuse_devicehub/resources/tag/view.py @@ -77,7 +77,8 @@ class TagDeviceView(View): @auth.Auth.requires_auth def put(self, tag_id: str, device_id: str): """Links an existing tag with a device.""" - tag = Tag.from_an_id(tag_id).one() # type: Tag + # tag = Tag.from_an_id(tag_id).one() # type: Tag + tag = Tag.from_an_id(tag_id).filter_by(owner=g.user).one() # type: Tag if tag.device_id: if tag.device_id == device_id: return Response(status=204) @@ -92,6 +93,16 @@ class TagDeviceView(View): db.session.commit() return Response(status=204) + @auth.Auth.requires_auth + def delete(self, tag_id: str, device_id: str): + tag = Tag.from_an_id(tag_id).filter_by(owner=g.user).one() # type: Tag + device = Device.query.filter_by(owner=g.user).filter_by(id=device_id).one() + if tag.device == device: + tag.device_id = None + db.session().final_flush() + db.session.commit() + return Response(status=204) + def get_device_from_tag(id: str): """Gets the device by passing a tag id. diff --git a/tests/test_tag.py b/tests/test_tag.py index b0336021..dcb8fe24 100644 --- a/tests/test_tag.py +++ b/tests/test_tag.py @@ -51,13 +51,16 @@ def test_create_tag_default_org(user: UserClient): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) -def test_create_tag_no_slash(): - """Checks that no tags can be created that contain a slash.""" - with raises(ValidationError): - Tag('/') - - with raises(ValidationError): - Tag('bar', secondary='/') +def test_create_same_tag_default_org_two_users(user: UserClient, user2: UserClient): + """Creates a tag using the default organization.""" + # import pdb; pdb.set_trace() + tag = Tag(id='foo-1', owner_id=user.user['id']) + tag2 = Tag(id='foo-1', owner_id=user2.user['id']) + db.session.add(tag) + db.session.add(tag2) + db.session.commit() + assert tag.org.name == 'FooOrg' # as defined in the settings + assert tag2.org.name == 'FooOrg' # as defined in the settings @pytest.mark.mvp @@ -78,6 +81,17 @@ def test_create_two_same_tags(user: UserClient): db.session.commit() +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_create_tag_no_slash(): + """Checks that no tags can be created that contain a slash.""" + with raises(ValidationError): + Tag('/') + + with raises(ValidationError): + Tag('bar', secondary='/') + + @pytest.mark.mvp def test_tag_post(app: Devicehub, user: UserClient): """Checks the POST method of creating a tag.""" From 3931d2456fff35fee1b67d028bbed445067ccbef Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 26 Feb 2021 11:29:17 +0100 Subject: [PATCH 04/22] add endpoint for deassociate tags of devices --- ereuse_devicehub/resources/tag/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ereuse_devicehub/resources/tag/__init__.py b/ereuse_devicehub/resources/tag/__init__.py index e6418e54..2423d80a 100644 --- a/ereuse_devicehub/resources/tag/__init__.py +++ b/ereuse_devicehub/resources/tag/__init__.py @@ -48,6 +48,10 @@ class TagDef(Resource): 'device/<{0.ID_CONVERTER.value}:device_id>'.format(DeviceDef), view_func=device_view, methods={'PUT'}) + self.add_url_rule('/<{0.ID_CONVERTER.value}:tag_id>/'.format(self) + + 'device/<{0.ID_CONVERTER.value}:device_id>'.format(DeviceDef), + view_func=device_view, + methods={'DELETE'}) @option('-u', '--owner', help=OWNER_H) @option('-o', '--org', help=ORG_H) From 159eab29c2222c3acf6c6d4c9e182b9f48817a29 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 26 Feb 2021 13:23:50 +0100 Subject: [PATCH 05/22] restrict to named tags --- ereuse_devicehub/resources/tag/view.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ereuse_devicehub/resources/tag/view.py b/ereuse_devicehub/resources/tag/view.py index 2cb3009f..a23c50b1 100644 --- a/ereuse_devicehub/resources/tag/view.py +++ b/ereuse_devicehub/resources/tag/view.py @@ -97,6 +97,10 @@ class TagDeviceView(View): def delete(self, tag_id: str, device_id: str): tag = Tag.from_an_id(tag_id).filter_by(owner=g.user).one() # type: Tag device = Device.query.filter_by(owner=g.user).filter_by(id=device_id).one() + if tag.provider: + # if is an unamed tag not do nothing + return Response(status=204) + if tag.device == device: tag.device_id = None db.session().final_flush() From ac032c7902efafbe0bd08db46718b4daaf42454a Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 26 Feb 2021 13:25:21 +0100 Subject: [PATCH 06/22] fixing primary keys --- .../versions/6a2a939d5668_drop_unique_org_for_tag.py | 7 +++---- ereuse_devicehub/resources/tag/model.py | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py b/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py index 417c27f9..b6bdabff 100644 --- a/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py +++ b/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py @@ -24,10 +24,9 @@ def get_inv(): def upgrade(): 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.create_unique_constraint('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()}'), def downgrade(): - op.create_unique_constraint('one tag id per organization', 'tag', ['id', 'org_id'], schema=f'{get_inv()}') - op.create_unique_constraint('one secondary tag per organization', 'tag', ['id', 'secondary'], schema=f'{get_inv()}') + op.drop_constraint('one tag id per owner', 'tag', schema=f'{get_inv()}') + op.create_primary_key('one tag id per organization', 'tag', ['id', 'org_id'], schema=f'{get_inv()}'), diff --git a/ereuse_devicehub/resources/tag/model.py b/ereuse_devicehub/resources/tag/model.py index b5128830..5adce308 100644 --- a/ereuse_devicehub/resources/tag/model.py +++ b/ereuse_devicehub/resources/tag/model.py @@ -30,12 +30,12 @@ class Tag(Thing): id.comment = """The ID of the tag.""" owner_id = Column(UUID(as_uuid=True), ForeignKey(User.id), + primary_key=True, nullable=False, default=lambda: g.user.id) owner = relationship(User, primaryjoin=owner_id == User.id) org_id = Column(UUID(as_uuid=True), ForeignKey(Organization.id), - primary_key=True, # If we link with the Organization object this instance # will be set as persistent and added to session # which is something we don't want to enforce by default @@ -98,8 +98,7 @@ class Tag(Thing): __table_args__ = ( UniqueConstraint(id, owner_id, name='one tag id per owner'), - # UniqueConstraint(id, org_id, name='one tag id per organization'), - # UniqueConstraint(secondary, org_id, name='one secondary tag per organization') + UniqueConstraint(secondary, org_id, name='one secondary tag per organization') ) @property From 8d6e50ece90c855a53c1bc26ca22699eb17cb4b3 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 26 Feb 2021 18:58:10 +0100 Subject: [PATCH 07/22] fixing unique secondary and owner --- .../versions/6a2a939d5668_drop_unique_org_for_tag.py | 4 ++++ ereuse_devicehub/resources/tag/model.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py b/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py index b6bdabff..435cce5d 100644 --- a/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py +++ b/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py @@ -24,9 +24,13 @@ def get_inv(): def upgrade(): 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.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()}'), def downgrade(): op.drop_constraint('one tag id per owner', 'tag', schema=f'{get_inv()}') + op.drop_constraint('one secondary tag per owner', 'tag', 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()}'), diff --git a/ereuse_devicehub/resources/tag/model.py b/ereuse_devicehub/resources/tag/model.py index 5adce308..7f6acc4f 100644 --- a/ereuse_devicehub/resources/tag/model.py +++ b/ereuse_devicehub/resources/tag/model.py @@ -98,7 +98,7 @@ class Tag(Thing): __table_args__ = ( UniqueConstraint(id, owner_id, name='one tag id per owner'), - UniqueConstraint(secondary, org_id, name='one secondary tag per organization') + UniqueConstraint(secondary, owner_id, name='one secondary tag per organization') ) @property From adcb330b87a6bf7d17e4387a764855ebdbe43820 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 26 Feb 2021 18:59:32 +0100 Subject: [PATCH 08/22] fixing same tag 2 users get with a validate user --- ereuse_devicehub/resources/tag/view.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ereuse_devicehub/resources/tag/view.py b/ereuse_devicehub/resources/tag/view.py index a23c50b1..2b1d2c1a 100644 --- a/ereuse_devicehub/resources/tag/view.py +++ b/ereuse_devicehub/resources/tag/view.py @@ -66,11 +66,19 @@ class TagDeviceView(View): def one(self, id): """Gets the device from the tag.""" + if request.authorization: + return self.one_authorization(id) + tag = Tag.from_an_id(id).one() # type: Tag if not tag.device: raise TagNotLinked(tag.id) - if not request.authorization: - return redirect(location=url_for_resource(Device, tag.device.id)) + return redirect(location=url_for_resource(Device, tag.device.id)) + + @auth.Auth.requires_auth + def one_authorization(self, id): + tag = Tag.from_an_id(id).filter_by(owner=g.user).one() # type: Tag + if not tag.device: + raise TagNotLinked(tag.id) return app.resources[Device.t].schema.jsonify(tag.device) # noinspection PyMethodOverriding From b2859781f05a4745dd7a6e9712b3d442983f296a Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 26 Feb 2021 18:59:57 +0100 Subject: [PATCH 09/22] new tests and fixing others --- tests/test_tag.py | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/tests/test_tag.py b/tests/test_tag.py index dcb8fe24..b3a8fd4d 100644 --- a/tests/test_tag.py +++ b/tests/test_tag.py @@ -8,7 +8,7 @@ from pytest import raises from teal.db import MultipleResourcesFound, ResourceNotFound, UniqueViolation, DBError from teal.marshmallow import ValidationError -from ereuse_devicehub.client import UserClient +from ereuse_devicehub.client import UserClient, Client from ereuse_devicehub.db import db from ereuse_devicehub.devicehub import Devicehub from ereuse_devicehub.resources.action.models import Snapshot @@ -78,7 +78,8 @@ def test_create_two_same_tags(user: UserClient): db.session.add(Tag(id='foo-bar', owner_id=user.user['id'])) org2 = Organization(name='org 2', tax_id='tax id org 2') db.session.add(Tag(id='foo-bar', org=org2, owner_id=user.user['id'])) - db.session.commit() + with raises(DBError): + db.session.commit() @pytest.mark.mvp @@ -145,17 +146,39 @@ def test_tag_get_device_from_tag_endpoint_no_tag(user: UserClient): @pytest.mark.mvp -def test_tag_get_device_from_tag_endpoint_multiple_tags(app: Devicehub, user: UserClient): - """As above, but when there are two tags with the same ID, the +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_tag_get_device_from_tag_endpoint_multiple_tags(app: Devicehub, user: UserClient, user2: UserClient, client: Client): + """As above, but when there are two tags with the secondary ID, the system should not return any of both (to be deterministic) so it should raise an exception. """ - with app.app_context(): - db.session.add(Tag(id='foo-bar', owner_id=user.user['id'])) - org2 = Organization(name='org 2', tax_id='tax id org 2') - db.session.add(Tag(id='foo-bar', org=org2, owner_id=user.user['id'])) + db.session.add(Tag(id='foo', secondary='bar', owner_id=user.user['id'])) + db.session.commit() + + db.session.add(Tag(id='foo', secondary='bar', owner_id=user2.user['id'])) + db.session.commit() + + db.session.add(Tag(id='foo2', secondary='bar', owner_id=user.user['id'])) + with raises(DBError): db.session.commit() - user.get(res=Tag, item='foo-bar/device', status=MultipleResourcesFound) + db.session.rollback() + + tag1 = Tag.from_an_id('foo').filter_by(owner_id=user.user['id']).one() + tag2 = Tag.from_an_id('foo').filter_by(owner_id=user2.user['id']).one() + pc1 = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id']) + pc2 = Desktop(serial_number='sn2', chassis=ComputerChassis.Tower, owner_id=user2.user['id']) + pc1.tags.add(tag1) + pc2.tags.add(tag2) + db.session.add(pc1) + db.session.add(pc2) + db.session.commit() + computer, _ = user.get(res=Tag, item='foo/device') + assert computer['serialNumber'] == 'sn1' + computer, _ = user2.get(res=Tag, item='foo/device') + assert computer['serialNumber'] == 'sn2' + + _, status = client.get(res=Tag, item='foo/device', status=MultipleResourcesFound) + assert status.status_code == 422 @pytest.mark.mvp @@ -230,8 +253,7 @@ def test_tag_secondary_workbench_link_find(user: UserClient): t = Tag('foo', secondary='bar', owner_id=user.user['id']) db.session.add(t) db.session.flush() - assert Tag.from_an_id('bar').one() == t - assert Tag.from_an_id('foo').one() == t + assert Tag.from_an_id('bar').one() == Tag.from_an_id('foo').one() with pytest.raises(ResourceNotFound): Tag.from_an_id('nope').one() From b811e0572c1e06ac85825697fd58e99a5c0b7183 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 1 Mar 2021 17:56:55 +0100 Subject: [PATCH 10/22] delete tags named --- ereuse_devicehub/resources/tag/model.py | 25 ++++++++++++++ ereuse_devicehub/resources/tag/view.py | 8 +++++ tests/test_tag.py | 43 +++++++++++++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/ereuse_devicehub/resources/tag/model.py b/ereuse_devicehub/resources/tag/model.py index 7f6acc4f..9a5231d7 100644 --- a/ereuse_devicehub/resources/tag/model.py +++ b/ereuse_devicehub/resources/tag/model.py @@ -125,6 +125,19 @@ class Tag(Thing): """Return a SQLAlchemy filter expression for printable queries.""" return cls.org_id == Organization.get_default_org_id() + def delete(self): + """Deletes the tag. + + This method removes the tag if is named tag and don't have any linked device. + """ + if self.device: + raise TagLinked(self) + if self.provider: + # if is an unnamed tag not delete + raise TagUnnamed(self.id) + + db.session.delete(self) + def __repr__(self) -> str: return ''.format(self) @@ -133,3 +146,15 @@ class Tag(Thing): def __format__(self, format_spec: str) -> str: return '{0.org.name} {0.id}'.format(self) + + +class TagLinked(ValidationError): + def __init__(self, tag): + message = 'The tag {} is linked to device {}.'.format(tag.id, tag.device.id) + super().__init__(message, field_names=['device']) + + +class TagUnnamed(ValidationError): + def __init__(self, id): + message = 'This tag {} is unnamed tag. It is imposible delete.'.format(id) + super().__init__(message, field_names=['device']) diff --git a/ereuse_devicehub/resources/tag/view.py b/ereuse_devicehub/resources/tag/view.py index 2b1d2c1a..75f575f5 100644 --- a/ereuse_devicehub/resources/tag/view.py +++ b/ereuse_devicehub/resources/tag/view.py @@ -60,6 +60,14 @@ class TagView(View): db.session.commit() return Response(status=201) + @auth.Auth.requires_auth + def delete(self, id): + tag = Tag.from_an_id(id).filter_by(owner=g.user).one() + tag.delete() + db.session().final_flush() + db.session.commit() + return Response(status=204) + class TagDeviceView(View): """Endpoints to work with the device of the tag; /tags/23/device.""" diff --git a/tests/test_tag.py b/tests/test_tag.py index b3a8fd4d..ce6a517e 100644 --- a/tests/test_tag.py +++ b/tests/test_tag.py @@ -35,6 +35,49 @@ def test_create_tag(user: UserClient): assert tag.provider == URL('http://foo.bar') +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_delete_tags(user: UserClient, client: Client): + """Delete a named tag.""" + # Delete Tag Named + # import pdb; pdb.set_trace() + pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id']) + db.session.add(pc) + db.session.commit() + tag = Tag(id='bar', owner_id=user.user['id'], device_id=pc.id) + db.session.add(tag) + db.session.commit() + tag = Tag.query.one() + assert tag.id == 'bar' + # Is not possible delete one tag linked to one device + res, _ = user.delete(res=Tag, item=tag.id, status=422) + msg = 'The tag bar is linked to device' + assert msg in res['message'][0] + + tag.device_id = None + db.session.add(tag) + db.session.commit() + # Is not possible delete one tag from an anonymous user + client.delete(res=Tag, item=tag.id, status=401) + + # Is possible delete one normal tag + user.delete(res=Tag, item=tag.id) + user.get(res=Tag, item=tag.id, status=404) + + # Delete Tag UnNamed + org = Organization(name='bar', tax_id='bartax') + tag = Tag(id='bar-1', org=org, provider=URL('http://foo.bar'), owner_id=user.user['id']) + db.session.add(tag) + db.session.commit() + tag = Tag.query.one() + assert tag.id == 'bar-1' + res, _ = user.delete(res=Tag, item=tag.id, status=422) + msg = 'This tag {} is unnamed tag. It is imposible delete.'.format(tag.id) + assert msg in res['message'] + tag = Tag.query.one() + assert tag.id == 'bar-1' + + @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_create_tag_default_org(user: UserClient): From a73dee899241850550a63d8e9178499f834eb504 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 1 Mar 2021 18:55:44 +0100 Subject: [PATCH 11/22] add test for liink and unlink device to one tag --- tests/test_tag.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/test_tag.py b/tests/test_tag.py index ce6a517e..ce76194c 100644 --- a/tests/test_tag.py +++ b/tests/test_tag.py @@ -33,6 +33,26 @@ def test_create_tag(user: UserClient): tag = Tag.query.one() assert tag.id == 'bar-1' assert tag.provider == URL('http://foo.bar') + res, _ = user.get(res=Tag, item=tag.id, status=422) + assert res['type'] == 'TagNotLinked' + + +@pytest.mark.mvp +@pytest.mark.usefixtures(conftest.app_context.__name__) +def test_create_tag_with_device(user: UserClient): + """Creates a tag specifying linked with one device.""" + pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id']) + db.session.add(pc) + db.session.commit() + tag = Tag(id='bar', owner_id=user.user['id']) + db.session.add(tag) + db.session.commit() + data = '{tag_id}/device/{device_id}'.format(tag_id=tag.id, device_id=pc.id) + user.put({}, res=Tag, item=data, status=204) + user.get(res=Tag, item='{}/device'.format(tag.id)) + user.delete({}, res=Tag, item=data, status=204) + res, _ = user.get(res=Tag, item='{}/device'.format(tag.id), status=422) + assert res['type'] == 'TagNotLinked' @pytest.mark.mvp @@ -40,7 +60,6 @@ def test_create_tag(user: UserClient): def test_delete_tags(user: UserClient, client: Client): """Delete a named tag.""" # Delete Tag Named - # import pdb; pdb.set_trace() pc = Desktop(serial_number='sn1', chassis=ComputerChassis.Tower, owner_id=user.user['id']) db.session.add(pc) db.session.commit() @@ -96,7 +115,6 @@ def test_create_tag_default_org(user: UserClient): @pytest.mark.usefixtures(conftest.app_context.__name__) def test_create_same_tag_default_org_two_users(user: UserClient, user2: UserClient): """Creates a tag using the default organization.""" - # import pdb; pdb.set_trace() tag = Tag(id='foo-1', owner_id=user.user['id']) tag2 = Tag(id='foo-1', owner_id=user2.user['id']) db.session.add(tag) From 0d7a098ac56f5cc047acdbe7d9a92dd910dccf4c Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 5 Mar 2021 11:31:47 +0100 Subject: [PATCH 12/22] adding sequence internal_id --- .../versions/6a2a939d5668_drop_unique_org_for_tag.py | 6 ++++++ ereuse_devicehub/resources/tag/model.py | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py b/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py index 435cce5d..50a803a9 100644 --- a/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py +++ b/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py @@ -6,6 +6,7 @@ Create Date: 2021-02-25 18:47:47.441195 """ from alembic import op +import sqlalchemy as sa from alembic import context @@ -27,6 +28,8 @@ def upgrade(): 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_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, + 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(): @@ -34,3 +37,6 @@ def downgrade(): op.drop_constraint('one secondary tag per owner', 'tag', 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.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;") diff --git a/ereuse_devicehub/resources/tag/model.py b/ereuse_devicehub/resources/tag/model.py index 9a5231d7..757f02ae 100644 --- a/ereuse_devicehub/resources/tag/model.py +++ b/ereuse_devicehub/resources/tag/model.py @@ -3,7 +3,7 @@ from typing import Set from boltons import urlutils from flask import g -from sqlalchemy import BigInteger, Column, ForeignKey, UniqueConstraint +from sqlalchemy import BigInteger, Column, ForeignKey, UniqueConstraint, Sequence from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import backref, relationship, validates from teal.db import DB_CASCADE_SET_NULL, Query, URL @@ -26,6 +26,10 @@ class Tags(Set['Tag']): class Tag(Thing): + internal_id = Column(BigInteger, Sequence('tag_internal_id_seq'), unique=True, nulable=False) + internal_id.comment = """The identifier of the tag for this database. Used only + internally for software; users should not use this. + """ id = Column(db.CIText(), primary_key=True) id.comment = """The ID of the tag.""" owner_id = Column(UUID(as_uuid=True), From f331128741000ae559d9dab31cc27b5463c9853b Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 8 Mar 2021 12:39:17 +0100 Subject: [PATCH 13/22] adding TagCode to tag named system --- .../6a2a939d5668_drop_unique_org_for_tag.py | 26 ++++++++++++++++--- ereuse_devicehub/resources/tag/model.py | 13 +++++++--- ereuse_devicehub/resources/tag/schema.py | 1 + ereuse_devicehub/resources/tag/view.py | 6 +++-- ereuse_devicehub/resources/utils.py | 9 +++++++ tests/test_tag.py | 3 ++- 6 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 ereuse_devicehub/resources/utils.py diff --git a/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py b/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py index 50a803a9..17e0290e 100644 --- a/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py +++ b/ereuse_devicehub/migrations/versions/6a2a939d5668_drop_unique_org_for_tag.py @@ -23,13 +23,34 @@ def get_inv(): raise ValueError("Inventory value is not specified") 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(): 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.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.add_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()}') + 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()}') + + 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(): @@ -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_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.execute(f"DROP SEQUENCE {get_inv()}.tag_internal_id_seq;") diff --git a/ereuse_devicehub/resources/tag/model.py b/ereuse_devicehub/resources/tag/model.py index 757f02ae..c52cdf8a 100644 --- a/ereuse_devicehub/resources/tag/model.py +++ b/ereuse_devicehub/resources/tag/model.py @@ -15,6 +15,7 @@ from ereuse_devicehub.resources.agent.models import Organization from ereuse_devicehub.resources.device.models import Device from ereuse_devicehub.resources.models import Thing from ereuse_devicehub.resources.user.models import User +from ereuse_devicehub.resources.utils import hascode class Tags(Set['Tag']): @@ -25,8 +26,10 @@ class Tags(Set['Tag']): return ', '.join(format(tag, format_spec) for tag in self).strip() + + 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 internally for software; users should not use this. """ @@ -113,7 +116,7 @@ class Tag(Thing): 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)) + return urlutils.URL(url_for_resource(Tag, item_id=self.code)) @property def printable(self) -> bool: @@ -129,6 +132,10 @@ class Tag(Thing): """Return a SQLAlchemy filter expression for printable queries.""" return cls.org_id == Organization.get_default_org_id() + @property + def code(self) -> str: + return hascode.encode(self.internal_id) + def delete(self): """Deletes the tag. @@ -157,7 +164,7 @@ class TagLinked(ValidationError): message = 'The tag {} is linked to device {}.'.format(tag.id, tag.device.id) super().__init__(message, field_names=['device']) - + class TagUnnamed(ValidationError): def __init__(self, id): message = 'This tag {} is unnamed tag. It is imposible delete.'.format(id) diff --git a/ereuse_devicehub/resources/tag/schema.py b/ereuse_devicehub/resources/tag/schema.py index 7db4fe0d..e1c8b608 100644 --- a/ereuse_devicehub/resources/tag/schema.py +++ b/ereuse_devicehub/resources/tag/schema.py @@ -28,3 +28,4 @@ class Tag(Thing): 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__) + code = SanitizedStr(dump_only=True, description=m.Tag.internal_id.comment) diff --git a/ereuse_devicehub/resources/tag/view.py b/ereuse_devicehub/resources/tag/view.py index 75f575f5..5416fd2a 100644 --- a/ereuse_devicehub/resources/tag/view.py +++ b/ereuse_devicehub/resources/tag/view.py @@ -6,14 +6,16 @@ from teal.resource import View, url_for_resource from ereuse_devicehub import auth from ereuse_devicehub.db import db 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.tag import Tag class TagView(View): - def one(self, id): + def one(self, code): """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: raise TagNotLinked(tag.id) return redirect(location=url_for_resource(Device, tag.device.id)) diff --git a/ereuse_devicehub/resources/utils.py b/ereuse_devicehub/resources/utils.py new file mode 100644 index 00000000..1ac37b51 --- /dev/null +++ b/ereuse_devicehub/resources/utils.py @@ -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) diff --git a/tests/test_tag.py b/tests/test_tag.py index ce76194c..040b2751 100644 --- a/tests/test_tag.py +++ b/tests/test_tag.py @@ -26,6 +26,7 @@ from tests.conftest import file @pytest.mark.usefixtures(conftest.app_context.__name__) def test_create_tag(user: UserClient): """Creates a tag specifying a custom organization.""" + # import pdb; pdb.set_trace() org = Organization(name='bar', tax_id='bartax') tag = Tag(id='bar-1', org=org, provider=URL('http://foo.bar'), owner_id=user.user['id']) db.session.add(tag) @@ -33,7 +34,7 @@ def test_create_tag(user: UserClient): tag = Tag.query.one() assert tag.id == 'bar-1' 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' From 2175987c8588282cc4300b31d863eeff535d3888 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 8 Mar 2021 13:09:01 +0100 Subject: [PATCH 14/22] hashcode insted of hascode --- ereuse_devicehub/resources/tag/model.py | 4 ++-- ereuse_devicehub/resources/tag/view.py | 4 ++-- ereuse_devicehub/resources/utils.py | 5 +---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/ereuse_devicehub/resources/tag/model.py b/ereuse_devicehub/resources/tag/model.py index c52cdf8a..92c8d5d4 100644 --- a/ereuse_devicehub/resources/tag/model.py +++ b/ereuse_devicehub/resources/tag/model.py @@ -15,7 +15,7 @@ from ereuse_devicehub.resources.agent.models import Organization from ereuse_devicehub.resources.device.models import Device from ereuse_devicehub.resources.models import Thing from ereuse_devicehub.resources.user.models import User -from ereuse_devicehub.resources.utils import hascode +from ereuse_devicehub.resources.utils import hashcode class Tags(Set['Tag']): @@ -134,7 +134,7 @@ class Tag(Thing): @property def code(self) -> str: - return hascode.encode(self.internal_id) + return hashcode.encode(self.internal_id) def delete(self): """Deletes the tag. diff --git a/ereuse_devicehub/resources/tag/view.py b/ereuse_devicehub/resources/tag/view.py index 5416fd2a..6d61304e 100644 --- a/ereuse_devicehub/resources/tag/view.py +++ b/ereuse_devicehub/resources/tag/view.py @@ -6,7 +6,7 @@ from teal.resource import View, url_for_resource from ereuse_devicehub import auth from ereuse_devicehub.db import db from ereuse_devicehub.query import things_response -from ereuse_devicehub.resources.utils import hascode +from ereuse_devicehub.resources.utils import hashcode from ereuse_devicehub.resources.device.models import Device from ereuse_devicehub.resources.tag import Tag @@ -14,7 +14,7 @@ from ereuse_devicehub.resources.tag import Tag class TagView(View): def one(self, code): """Gets the device from the named tag, /tags/namedtag.""" - internal_id = hascode.decode(code.upper()) or -1 + internal_id = hashcode.decode(code.upper()) or -1 tag = Tag.query.filter_by(internal_id=internal_id).one() # type: Tag if not tag.device: raise TagNotLinked(tag.id) diff --git a/ereuse_devicehub/resources/utils.py b/ereuse_devicehub/resources/utils.py index 1ac37b51..3d02d610 100644 --- a/ereuse_devicehub/resources/utils.py +++ b/ereuse_devicehub/resources/utils.py @@ -3,7 +3,4 @@ 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) +hashcode = Hashids(SECRET, min_length=5, alphabet=ALPHABET) From 4d01e7f05d4736fe7c41480b65eb759aec968c79 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 16 Mar 2021 15:43:42 +0100 Subject: [PATCH 15/22] fixing bug unamed in csv --- ereuse_devicehub/resources/documents/device_row.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ereuse_devicehub/resources/documents/device_row.py b/ereuse_devicehub/resources/documents/device_row.py index 74a322be..cb5e82d8 100644 --- a/ereuse_devicehub/resources/documents/device_row.py +++ b/ereuse_devicehub/resources/documents/device_row.py @@ -50,8 +50,7 @@ class DeviceRow(OrderedDict): self['Tag 2 Type'] = self['Tag 2 ID'] = self['Tag 2 Organization'] = '' self['Tag 3 Type'] = self['Tag 3 ID'] = self['Tag 3 Organization'] = '' for i, tag in zip(range(1, 3), device.tags): - # TODO @cayop we need redefined how save the Tag Type info - self['Tag {} Type'.format(i)] = 'unamed' + self['Tag {} Type'.format(i)] = 'unamed' if tag.provider else 'named' self['Tag {} ID'.format(i)] = tag.id self['Tag {} Organization'.format(i)] = tag.org.name From 10733f88895b6975cc513b56c9e84699cbc02d71 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 16 Mar 2021 20:05:08 +0100 Subject: [PATCH 16/22] fixing test unamed --- tests/files/proposal_extended_csv_report.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/files/proposal_extended_csv_report.csv b/tests/files/proposal_extended_csv_report.csv index 63709330..cf1e88a7 100644 --- a/tests/files/proposal_extended_csv_report.csv +++ b/tests/files/proposal_extended_csv_report.csv @@ -1,3 +1,3 @@ System ID;DocumentID;Public Link;Tag 1 Type;Tag 1 ID;Tag 1 Organization;Tag 2 Type;Tag 2 ID;Tag 2 Organization;Tag 3 Type;Tag 3 ID;Tag 3 Organization;Device Hardware ID;Device Type;Device Chassis;Device Serial Number;Device Model;Device Manufacturer;Registered in;Registered (process);Updated in (software);Updated in (web);Physical state;Trading state;Processor;RAM (MB);Data Storage Size (MB);Processor 1;Processor 1 Manufacturer;Processor 1 Model;Processor 1 Serial Number;Processor 1 Number of cores;Processor 1 Speed (GHz);Benchmark Processor 1 (points);Benchmark ProcessorSysbench Processor 1 (points);Processor 2;Processor 2 Manufacturer;Processor 2 Model;Processor 2 Serial Number;Processor 2 Number of cores;Processor 2 Speed (GHz);Benchmark Processor 2 (points);Benchmark ProcessorSysbench Processor 2 (points);RamModule 1;RamModule 1 Manufacturer;RamModule 1 Model;RamModule 1 Serial Number;RamModule 1 Size (MB);RamModule 1 Speed (MHz);RamModule 2;RamModule 2 Manufacturer;RamModule 2 Model;RamModule 2 Serial Number;RamModule 2 Size (MB);RamModule 2 Speed (MHz);RamModule 3;RamModule 3 Manufacturer;RamModule 3 Model;RamModule 3 Serial Number;RamModule 3 Size (MB);RamModule 3 Speed (MHz);RamModule 4;RamModule 4 Manufacturer;RamModule 4 Model;RamModule 4 Serial Number;RamModule 4 Size (MB);RamModule 4 Speed (MHz);DataStorage 1;DataStorage 1 Manufacturer;DataStorage 1 Model;DataStorage 1 Serial Number;DataStorage 1 Size (MB);Erasure DataStorage 1;Erasure DataStorage 1 Serial Number;Erasure DataStorage 1 Size (MB);Erasure DataStorage 1 Software;Erasure DataStorage 1 Result;Erasure DataStorage 1 Type;Erasure DataStorage 1 Method;Erasure DataStorage 1 Elapsed (hours);Erasure DataStorage 1 Date;Erasure DataStorage 1 Steps;Erasure DataStorage 1 Steps Start Time;Erasure DataStorage 1 Steps End Time;Benchmark DataStorage 1 Read Speed (MB/s);Benchmark DataStorage 1 Writing speed (MB/s);Test DataStorage 1 Software;Test DataStorage 1 Type;Test DataStorage 1 Result;Test DataStorage 1 Power on (hours used);Test DataStorage 1 Lifetime remaining (percentage);DataStorage 2;DataStorage 2 Manufacturer;DataStorage 2 Model;DataStorage 2 Serial Number;DataStorage 2 Size (MB);Erasure DataStorage 2;Erasure DataStorage 2 Serial Number;Erasure DataStorage 2 Size (MB);Erasure DataStorage 2 Software;Erasure DataStorage 2 Result;Erasure DataStorage 2 Type;Erasure DataStorage 2 Method;Erasure DataStorage 2 Elapsed (hours);Erasure DataStorage 2 Date;Erasure DataStorage 2 Steps;Erasure DataStorage 2 Steps Start Time;Erasure DataStorage 2 Steps End Time;Benchmark DataStorage 2 Read Speed (MB/s);Benchmark DataStorage 2 Writing speed (MB/s);Test DataStorage 2 Software;Test DataStorage 2 Type;Test DataStorage 2 Result;Test DataStorage 2 Power on (hours used);Test DataStorage 2 Lifetime remaining (percentage);DataStorage 3;DataStorage 3 Manufacturer;DataStorage 3 Model;DataStorage 3 Serial Number;DataStorage 3 Size (MB);Erasure DataStorage 3;Erasure DataStorage 3 Serial Number;Erasure DataStorage 3 Size (MB);Erasure DataStorage 3 Software;Erasure DataStorage 3 Result;Erasure DataStorage 3 Type;Erasure DataStorage 3 Method;Erasure DataStorage 3 Elapsed (hours);Erasure DataStorage 3 Date;Erasure DataStorage 3 Steps;Erasure DataStorage 3 Steps Start Time;Erasure DataStorage 3 Steps End Time;Benchmark DataStorage 3 Read Speed (MB/s);Benchmark DataStorage 3 Writing speed (MB/s);Test DataStorage 3 Software;Test DataStorage 3 Type;Test DataStorage 3 Result;Test DataStorage 3 Power on (hours used);Test DataStorage 3 Lifetime remaining (percentage);DataStorage 4;DataStorage 4 Manufacturer;DataStorage 4 Model;DataStorage 4 Serial Number;DataStorage 4 Size (MB);Erasure DataStorage 4;Erasure DataStorage 4 Serial Number;Erasure DataStorage 4 Size (MB);Erasure DataStorage 4 Software;Erasure DataStorage 4 Result;Erasure DataStorage 4 Type;Erasure DataStorage 4 Method;Erasure DataStorage 4 Elapsed (hours);Erasure DataStorage 4 Date;Erasure DataStorage 4 Steps;Erasure DataStorage 4 Steps Start Time;Erasure DataStorage 4 Steps End Time;Benchmark DataStorage 4 Read Speed (MB/s);Benchmark DataStorage 4 Writing speed (MB/s);Test DataStorage 4 Software;Test DataStorage 4 Type;Test DataStorage 4 Result;Test DataStorage 4 Power on (hours used);Test DataStorage 4 Lifetime remaining (percentage);Motherboard 1;Motherboard 1 Manufacturer;Motherboard 1 Model;Motherboard 1 Serial Number;Display 1;Display 1 Manufacturer;Display 1 Model;Display 1 Serial Number;GraphicCard 1;GraphicCard 1 Manufacturer;GraphicCard 1 Model;GraphicCard 1 Serial Number;GraphicCard 1 Memory (MB);GraphicCard 2;GraphicCard 2 Manufacturer;GraphicCard 2 Model;GraphicCard 2 Serial Number;GraphicCard 2 Memory (MB);NetworkAdapter 1;NetworkAdapter 1 Manufacturer;NetworkAdapter 1 Model;NetworkAdapter 1 Serial Number;NetworkAdapter 2;NetworkAdapter 2 Manufacturer;NetworkAdapter 2 Model;NetworkAdapter 2 Serial Number;SoundCard 1;SoundCard 1 Manufacturer;SoundCard 1 Model;SoundCard 1 Serial Number;SoundCard 2;SoundCard 2 Manufacturer;SoundCard 2 Model;SoundCard 2 Serial Number;Device Rate;Device Range;Processor Rate;Processor Range;RAM Rate;RAM Range;Data Storage Rate;Data Storage Range;Price;Benchmark RamSysbench (points) -1;;http://localhost/devices/1;unamed;foo;FooOrg;;;;;;;laptop-asustek_computer_inc-1001pxd-b8oaas048285-14:da:e9:42:f6:7b;Laptop;Netbook;b8oaas048285;1001pxd;asustek computer inc.;Thu Nov 12 19:53:01 2020;Workbench 11.0a2;2020-11-12 19:54:03.959185+01:00;;;;intel atom cpu n455 @ 2.66ghz;1024;238475;Processor 4: model intel atom cpu n455 @ 2.66ghz, S/N None;intel corp.;intel atom cpu n455 @ 2.66ghz;;1;2.667;6666.24;164.0803;;;;;;;;;RamModule 8: model None, S/N None;;;;1024;667;;;;;;;;;;;;;;;;;;;HardDrive 9: model hts54322, S/N e2024242cv86mm;hitachi;hts54322;e2024242cv86mm;238475;harddrive-hitachi-hts54322-e2024242cv86mm;e2024242cv86mm;238475;Workbench 11.0a2;Success;EraseBasic;Shred;1:16:49;2020-11-12 19:53:01.899092+01:00;✓ – StepRandom 1:16:49;2018-07-03 11:15:22.257059+02:00;2018-07-03 12:32:11.843190+02:00;66.2;21.8;Workbench 11.0a2;Short;Failure;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Motherboard 10: model 1001pxd, S/N eee0123456720;asustek computer inc.;1001pxd;eee0123456720;;;;;GraphicCard 5: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None;intel corporation;atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller;;256;;;;;;NetworkAdapter 2: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c9;qualcomm atheros;ar9285 wireless network adapter;74:2f:68:8b:fd:c9;NetworkAdapter 3: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7b;qualcomm atheros;ar8152 v2.0 fast ethernet;14:da:e9:42:f6:7b;SoundCard 6: model nm10/ich7 family high definition audio controller, S/N None;intel corporation;nm10/ich7 family high definition audio controller;;;;;;1.75;LOW;1.55;LOW;1.53;LOW;3.76;HIGH;52.50 €;15.7188 +1;;http://localhost/devices/1;named;foo;FooOrg;;;;;;;laptop-asustek_computer_inc-1001pxd-b8oaas048285-14:da:e9:42:f6:7b;Laptop;Netbook;b8oaas048285;1001pxd;asustek computer inc.;Thu Nov 12 19:53:01 2020;Workbench 11.0a2;2020-11-12 19:54:03.959185+01:00;;;;intel atom cpu n455 @ 2.66ghz;1024;238475;Processor 4: model intel atom cpu n455 @ 2.66ghz, S/N None;intel corp.;intel atom cpu n455 @ 2.66ghz;;1;2.667;6666.24;164.0803;;;;;;;;;RamModule 8: model None, S/N None;;;;1024;667;;;;;;;;;;;;;;;;;;;HardDrive 9: model hts54322, S/N e2024242cv86mm;hitachi;hts54322;e2024242cv86mm;238475;harddrive-hitachi-hts54322-e2024242cv86mm;e2024242cv86mm;238475;Workbench 11.0a2;Success;EraseBasic;Shred;1:16:49;2020-11-12 19:53:01.899092+01:00;✓ – StepRandom 1:16:49;2018-07-03 11:15:22.257059+02:00;2018-07-03 12:32:11.843190+02:00;66.2;21.8;Workbench 11.0a2;Short;Failure;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Motherboard 10: model 1001pxd, S/N eee0123456720;asustek computer inc.;1001pxd;eee0123456720;;;;;GraphicCard 5: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None;intel corporation;atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller;;256;;;;;;NetworkAdapter 2: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c9;qualcomm atheros;ar9285 wireless network adapter;74:2f:68:8b:fd:c9;NetworkAdapter 3: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7b;qualcomm atheros;ar8152 v2.0 fast ethernet;14:da:e9:42:f6:7b;SoundCard 6: model nm10/ich7 family high definition audio controller, S/N None;intel corporation;nm10/ich7 family high definition audio controller;;;;;;1.75;LOW;1.55;LOW;1.53;LOW;3.76;HIGH;52.50 €;15.7188 11;;http://localhost/devices/11;;;;;;;;;;laptop-asustek_computer_inc-1001pxd-b8oaas048287-14:da:e9:42:f6:7c;Laptop;Netbook;b8oaas048287;1001pxd;asustek computer inc.;Thu Nov 12 19:53:02 2020;Workbench 11.0b11;2020-11-12 19:53:02.225373+01:00;;;;intel atom cpu n455 @ 1.66ghz;2048;558558;Processor 15: model intel atom cpu n455 @ 1.66ghz, S/N None;intel corp.;intel atom cpu n455 @ 1.66ghz;;1;1.667;6666.24;164.0803;;;;;;;;;RamModule 18: model None, S/N None;;;;1024;667;RamModule 19: model 48594d503131325336344350362d53362020, S/N 4f43487b;hynix semiconductor;48594d503131325336344350362d53362020;4f43487b;1024;667;;;;;;;;;;;;;HardDrive 20: model hts54322, S/N e2024242cv86hj;hitachi;hts54322;e2024242cv86hj;238475;harddrive-hitachi-hts54322-e2024242cv86hj;e2024242cv86hj;238475;Workbench 11.0b11;Success;EraseBasic;Shred;1:16:49;2020-11-12 19:53:02.175189+01:00;✓ – StepRandom 1:16:49;2018-07-03 11:15:22.257059+02:00;2018-07-03 12:32:11.843190+02:00;66.2;21.8;Workbench 11.0b11;Extended;Failure;;;DataStorage 21: model wdc wd1600bevt-2, S/N wd-wx11a80w7430;western digital;wdc wd1600bevt-2;wd-wx11a80w7430;160041;datastorage-western_digital-wdc_wd1600bevt-2-wd-wx11a80w7430;wd-wx11a80w7430;160041;Workbench 11.0b11;Failure;EraseBasic;Shred;0:45:36;2020-11-12 19:53:02.176882+01:00;✓ – StepRandom 0:45:36;2019-10-23 09:49:54.410830+02:00;2019-10-23 10:35:31.400587+02:00;41.6;17.3;Workbench 11.0b11;Short;Success;5293;195 days, 12:00:00;SolidStateDrive 22: model wdc wd1600bevt-2, S/N wd-wx11a80w7430;western digital;wdc wd1600bevt-2;wd-wx11a80w7430;160042;solidstatedrive-western_digital-wdc_wd1600bevt-2-wd-wx11a80w7430;wd-wx11a80w7430;160042;Workbench 11.0b11;Success;EraseSectors;Badblocks;1:46:03;2020-11-12 19:53:02.180043+01:00;✓ – StepRandom 0:46:03,✓ – StepZero 1:00:00;2019-08-19 18:48:19.690458+02:00,2019-08-19 19:34:22.690458+02:00;2019-08-19 19:34:22.930562+02:00,2019-08-19 20:34:22.930562+02:00;41.1;17.1;Workbench 11.0b11;Short;Success;5231;194 days, 17:00:00;;;;;;;;;;;;;;;;;;;;;;;;;Motherboard 23: model 1001pxd, S/N eee0123456789;asustek computer inc.;1001pxd;eee0123456789;;"auo ""auo""";auo lcd monitor;;GraphicCard 16: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None;intel corporation;atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller;;256;;;;;;NetworkAdapter 13: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c8;qualcomm atheros;ar9285 wireless network adapter;74:2f:68:8b:fd:c8;NetworkAdapter 14: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7c;qualcomm atheros;ar8152 v2.0 fast ethernet;14:da:e9:42:f6:7c;SoundCard 7: model usb 2.0 uvc vga webcam, S/N 0x0001;azurewave;usb 2.0 uvc vga webcam;0x0001;SoundCard 17: model nm10/ich7 family high definition audio controller, S/N None;intel corporation;nm10/ich7 family high definition audio controller;;1.72;LOW;1.31;LOW;1.99;LOW;3.97;HIGH;51.60 €;15.7188 From 2ee71def75d36240e5841788bd8434edf681dc4f Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 16 Mar 2021 20:53:54 +0100 Subject: [PATCH 17/22] clean pdbs --- tests/test_documents.py | 1 - tests/test_tag.py | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/test_documents.py b/tests/test_documents.py index 645bf51c..1f8907f8 100644 --- a/tests/test_documents.py +++ b/tests/test_documents.py @@ -584,7 +584,6 @@ def test_verify_stamp_erasure_certificate(user: UserClient, client: Client): """Test verify stamp of one export certificate in PDF.""" s = file('erase-sectors.snapshot') snapshot, response = user.post(s, res=Snapshot) - # import pdb; pdb.set_trace() doc, _ = user.get(res=documents.DocumentDef.t, item='erasures/', diff --git a/tests/test_tag.py b/tests/test_tag.py index 040b2751..f6402bd4 100644 --- a/tests/test_tag.py +++ b/tests/test_tag.py @@ -26,7 +26,6 @@ from tests.conftest import file @pytest.mark.usefixtures(conftest.app_context.__name__) def test_create_tag(user: UserClient): """Creates a tag specifying a custom organization.""" - # import pdb; pdb.set_trace() org = Organization(name='bar', tax_id='bartax') tag = Tag(id='bar-1', org=org, provider=URL('http://foo.bar'), owner_id=user.user['id']) db.session.add(tag) From 4cbe28d5915c62d2921b1ea81d9b90be9fb5c3fe Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 16 Mar 2021 20:55:07 +0100 Subject: [PATCH 18/22] fixing comment --- ereuse_devicehub/resources/tag/view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereuse_devicehub/resources/tag/view.py b/ereuse_devicehub/resources/tag/view.py index 6d61304e..404ec0c1 100644 --- a/ereuse_devicehub/resources/tag/view.py +++ b/ereuse_devicehub/resources/tag/view.py @@ -103,7 +103,7 @@ class TagDeviceView(View): else: raise LinkedToAnotherDevice(tag.device_id) else: - # Check if this device exist for this woner + # Check if this device exist for this owner Device.query.filter_by(owner=g.user).filter_by(id=device_id).one() tag.device_id = device_id From 0706aab3b72aa0aaa00311a42b3c0cc6fac4c2ff Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 17 Mar 2021 10:44:58 +0100 Subject: [PATCH 19/22] test for a simple trade --- tests/test_action.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/test_action.py b/tests/test_action.py index 421a5c80..40962e49 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -747,9 +747,11 @@ def test_deallocate_bad_dates(user: UserClient): (models.Rent, states.Trading.Renting), (models.DisposeProduct, states.Trading.ProductDisposed) ])) -def test_trade(action_model_state: Tuple[Type[models.Action], states.Trading], user: UserClient): +def test_trade2(action_model_state: Tuple[Type[models.Action], states.Trading], user: UserClient): """Tests POSTing all Trade actions.""" # todo missing None states.Trading for after cancelling renting, for example + # import pdb; pdb.set_trace() + # Remove this test action_model, state = action_model_state snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) action = { @@ -767,6 +769,20 @@ def test_trade(action_model_state: Tuple[Type[models.Action], states.Trading], u assert device['trading'] == state.name +@pytest.mark.mvp +def test_trade(user: UserClient, user2: UserClient): + """Tests POST one simple Trade action with both users as system users.""" + # import pdb; pdb.set_trace() + snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) + action = { + 'type': 'Trade', + 'devices': [snapshot['device']['id']], + 'user_to': user2.user['email'] + } + action, _ = user.post(action, res=models.Action) + assert action['devices'][0]['id'] == snapshot['device']['id'] + + @pytest.mark.mvp @pytest.mark.usefixtures(conftest.auth_app_context.__name__) def test_price_custom(): From 45b44ce17ba97c7792669b98a54ecdf956481c87 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 25 Mar 2021 11:26:30 +0100 Subject: [PATCH 20/22] fixing tests --- ereuse_devicehub/resources/action/__init__.py | 5 +++++ tests/test_action.py | 16 +--------------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/ereuse_devicehub/resources/action/__init__.py b/ereuse_devicehub/resources/action/__init__.py index 7fd91eb8..a584f038 100644 --- a/ereuse_devicehub/resources/action/__init__.py +++ b/ereuse_devicehub/resources/action/__init__.py @@ -250,6 +250,11 @@ class MakeAvailable(ActionDef): SCHEMA = schemas.MakeAvailable +class TradeDef(ActionDef): + VIEW = None + SCHEMA = schemas.Trade + + class CancelTradeDef(ActionDef): VIEW = None SCHEMA = schemas.CancelTrade diff --git a/tests/test_action.py b/tests/test_action.py index 40962e49..2f0a1ea4 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -747,7 +747,7 @@ def test_deallocate_bad_dates(user: UserClient): (models.Rent, states.Trading.Renting), (models.DisposeProduct, states.Trading.ProductDisposed) ])) -def test_trade2(action_model_state: Tuple[Type[models.Action], states.Trading], user: UserClient): +def test_trade(action_model_state: Tuple[Type[models.Action], states.Trading], user: UserClient): """Tests POSTing all Trade actions.""" # todo missing None states.Trading for after cancelling renting, for example # import pdb; pdb.set_trace() @@ -769,20 +769,6 @@ def test_trade2(action_model_state: Tuple[Type[models.Action], states.Trading], assert device['trading'] == state.name -@pytest.mark.mvp -def test_trade(user: UserClient, user2: UserClient): - """Tests POST one simple Trade action with both users as system users.""" - # import pdb; pdb.set_trace() - snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot) - action = { - 'type': 'Trade', - 'devices': [snapshot['device']['id']], - 'user_to': user2.user['email'] - } - action, _ = user.post(action, res=models.Action) - assert action['devices'][0]['id'] == snapshot['device']['id'] - - @pytest.mark.mvp @pytest.mark.usefixtures(conftest.auth_app_context.__name__) def test_price_custom(): From 335c53530e4b5fc93190d16cb01557adbcd4a208 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 25 Mar 2021 11:39:52 +0100 Subject: [PATCH 21/22] fixing test --- tests/test_basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_basic.py b/tests/test_basic.py index 11ffbb80..edf58492 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -119,4 +119,4 @@ def test_api_docs(client: Client): 'scheme': 'basic', 'name': 'Authorization' } - assert len(docs['definitions']) == 117 + assert len(docs['definitions']) == 118 From c5ef11e6701faf81b3479d61cfffdf1a458f977b Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 9 Apr 2021 14:43:20 +0200 Subject: [PATCH 22/22] fixing for todo trade pr --- ereuse_devicehub/dummy/dummy.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/ereuse_devicehub/dummy/dummy.py b/ereuse_devicehub/dummy/dummy.py index 376ccd1b..e83a3577 100644 --- a/ereuse_devicehub/dummy/dummy.py +++ b/ereuse_devicehub/dummy/dummy.py @@ -115,13 +115,13 @@ class Dummy: user1.post({'type': model.t, 'devices': [pc]}, res=m.Action) # Perform a Sell to several devices - user1.post( - { - 'type': m.Sell.t, - 'to': user1.user['individuals'][0]['id'], - 'devices': list(itertools.islice(pcs, len(pcs) // 2)) - }, - res=m.Action) + # user1.post( + # { + # 'type': m.Sell.t, + # 'to': user1.user['individuals'][0]['id'], + # 'devices': list(itertools.islice(pcs, len(pcs) // 2)) + # }, + # res=m.Action) lot_user, _ = user1.post({'name': 'LoteStephan'}, res=Lot) @@ -171,13 +171,13 @@ class Dummy: res=m.Action) # todo test reserve - user1.post( # Sell device - { - 'type': m.Sell.t, - 'to': user1.user['individuals'][0]['id'], - 'devices': [sample_pc] - }, - res=m.Action) + # user1.post( # Sell device + # { + # 'type': m.Sell.t, + # 'to': user1.user['individuals'][0]['id'], + # 'devices': [sample_pc] + # }, + # res=m.Action) # todo Receive user1.get(res=Device, item=sample_pc) # Test