def test_sync_execute_register_desktop_tag_not_linked(): """Syncs a new d.Desktop with HID and a non-linked tag. It is OK if the tag was not linked, it will be linked in this process. """ tag = Tag(id='foo') db.session.add(tag) db.session.commit() # Create a new transient non-db object pc = d.Desktop(**yaml2json('pc-components.db')['device'], tags=OrderedSet([Tag(id='foo')])) returned_pc = Sync().execute_register(pc) assert returned_pc == pc assert tag.device == pc, 'Tag has to be linked' assert d.Desktop.query.one() == pc, 'd.Desktop had to be set to db'
def test_sync_execute_register_tag_linked_same_device(): """ If the tag is linked to the device, regardless if it has HID, the system should match the device through the tag. (If it has HID it validates both HID and tag point at the same device, this his checked in ). """ orig_pc = Computer(**file('pc-components.db')['device']) db.session.add(Tag(id='foo', device=orig_pc)) db.session.commit() pc = Computer(**file('pc-components.db')['device']) # Create a new transient non-db object pc.tags.add(Tag(id='foo')) db_pc = Sync().execute_register(pc) assert db_pc.id == orig_pc.id assert len(db_pc.tags) == 1 assert next(iter(db_pc.tags)).id == 'foo'
def test_sync_execute_register_tag_linked_other_device_mismatch_between_tags(): """ Checks that sync raises an error if finds that at least two passed-in tags are not linked to the same device. """ pc1 = Computer(**file('pc-components.db')['device']) db.session.add(Tag(id='foo-1', device=pc1)) pc2 = Computer(**file('pc-components.db')['device']) pc2.serial_number = 'pc2-serial' pc2.hid = Naming.hid(pc2.manufacturer, pc2.serial_number, pc2.model) db.session.add(Tag(id='foo-2', device=pc2)) db.session.commit() pc1 = Computer(**file('pc-components.db')['device']) # Create a new transient non-db object pc1.tags.add(Tag(id='foo-1')) pc1.tags.add(Tag(id='foo-2')) with raises(MismatchBetweenTags): Sync().execute_register(pc1)
def create_tag(self, id: str, org: str = None, sec: str = None, provider: str = None): """Create a tag with the given ID.""" db.session.add(Tag(**self.schema.load( dict(id=id, org=org, secondary=sec, provider=provider) ))) db.session.commit()
def test_sync_execute_register_mismatch_between_tags_and_hid(): """ Checks that sync raises an error if it finds that the HID does not point at the same device as the tag does. In this case we set HID -> pc1 but tag -> pc2 """ pc1 = Computer(**file('pc-components.db')['device']) db.session.add(Tag(id='foo-1', device=pc1)) pc2 = Computer(**file('pc-components.db')['device']) pc2.serial_number = 'pc2-serial' pc2.hid = Naming.hid(pc2.manufacturer, pc2.serial_number, pc2.model) db.session.add(Tag(id='foo-2', device=pc2)) db.session.commit() pc1 = Computer(**file('pc-components.db')['device']) # Create a new transient non-db object pc1.tags.add(Tag(id='foo-2')) with raises(MismatchBetweenTagsAndHid): Sync().execute_register(pc1)
def test_sync_execute_register_tag_does_not_exist(): """ Ensures not being able to register if the tag does not exist, even if the device has HID or it existed before. Tags have to be created before trying to link them through a Snapshot. """ pc = Computer(**file('pc-components.db')['device'], tags=OrderedSet([Tag()])) with raises(ResourceNotFound): Sync().execute_register(pc)
def test_sync_execute_register_tag_does_not_exist(): """Ensures not being able to register if the tag does not exist, even if the device has HID or it existed before. Tags have to be created before trying to link them through a Snapshot. """ user = User.query.filter().first() pc = d.Desktop(**yaml2json('pc-components.db')['device'], tags=OrderedSet([Tag('foo')])) pc.owner_id = user.id with raises(ResourceNotFound): Sync().execute_register(pc)
def create_tags_csv(self, path: pathlib.Path, org: str, provider: str): """Creates tags by reading CSV from ereuse-tag. CSV must have the following columns: 1. ID tag 2. Secondary id tag (or empty) """ with path.open() as f: for id, sec in csv.reader(f): db.session.add(Tag(**self.schema.load( dict(id=id, org=org, secondary=sec, provider=provider) ))) db.session.commit()
def create_tags(self, ids: Tuple[str], org: str = None, provider: str = None): """Create TAGS and associates them to a specific PROVIDER.""" tag_schema = TagS(only=('id', 'provider', 'org')) db.session.add_all( Tag(**tag_schema.load({ 'id': tag_id, 'provider': provider, 'org': org })) for tag_id in ids) db.session.commit()
def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str): """ Validates registering a computer without HID and a non-linked tag. In this case it is ok still, as the non-linked tag proves that the computer was not existing before (otherwise the tag would be linked), and thus it creates a new computer. """ tag = Tag(id=tag_id) pc = Computer(**file('pc-components.db')['device'], tags=OrderedSet([tag])) returned_pc = Sync().execute_register(pc) db.session.commit() assert returned_pc == pc db_tag = next(iter(returned_pc.tags)) # they are not the same tags though # tag is a transient obj and db_tag the one from the db # they have the same pk though assert tag != db_tag, 'They are not the same tags though' assert db_tag.id == tag.id assert Computer.query.one() == pc, 'Computer had to be set to db'
def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str): """Validates registering a d.Desktop without HID and a non-linked tag. In this case it is ok still, as the non-linked tag proves that the d.Desktop was not existing before (otherwise the tag would be linked), and thus it creates a new d.Desktop. """ tag = Tag(id=tag_id) pc = d.Desktop(**yaml2json('pc-components.db')['device'], tags=OrderedSet([tag])) db.session.add(g.user) returned_pc = Sync().execute_register(pc) db.session.commit() assert returned_pc == pc db_tag = next(iter(returned_pc.tags)) # they are not the same tags though # tag is a transient obj and db_tag the one from the db # they have the same pk though assert d.Desktop.query.one() == pc, 'd.Desktop had to be set to db' assert tag != db_tag, 'They are not the same tags though' for tag in pc.tags: assert tag.id in ['foo', pc.devicehub_id]
def execute_register(self, device: Device) -> Device: """ Synchronizes one device to the DB. This method tries to get an existing device using the HID or one of the tags, and... - if it already exists it returns a "local synced version" –the same ``device`` you passed-in but with updated values from the database. In this case we do not "touch" any of its values on the DB. - If it did not exist, a new device is created in the db. This method validates that all passed-in tags (``device.tags``), if linked, are linked to the same device, ditto for the hid. Finally it links the tags with the device. If you pass-in a component that is inside a parent, use :meth:`.execute_register_component` as it has more specialized methods to handle them. :param device: The device to synchronize to the DB. :raise NeedsId: The device has not any identifier we can use. To still create the device use ``force_creation``. :raise DatabaseError: Any other error from the DB. :return: The synced device from the db with the tags linked. """ assert inspect( device).transient, 'Device cannot be already synced from DB' assert all(inspect(tag).transient for tag in device.tags), 'Tags cannot be synced from DB' if not device.tags and not device.hid: # We cannot identify this device raise NeedsId() db_device = None if device.hid: with suppress(ResourceNotFound): db_device = Device.query.filter_by(hid=device.hid).one() try: tags = {Tag.from_an_id(tag.id).one() for tag in device.tags} # type: Set[Tag] except ResourceNotFound: raise ResourceNotFound( 'tag you are linking to device {}'.format(device)) linked_tags = {tag for tag in tags if tag.device_id} # type: Set[Tag] if linked_tags: sample_tag = next(iter(linked_tags)) for tag in linked_tags: if tag.device_id != sample_tag.device_id: raise MismatchBetweenTags( tag, sample_tag) # Tags linked to different devices if db_device: # Device from hid if sample_tag.device_id != db_device.id: # Device from hid != device from tags raise MismatchBetweenTagsAndHid(db_device.id, db_device.hid) else: # There was no device from hid if sample_tag.device.physical_properties != device.physical_properties: # Incoming physical props of device != props from tag's device # which means that the devices are not the same raise MismatchBetweenProperties( sample_tag.device.physical_properties, device.physical_properties) db_device = sample_tag.device if db_device: # Device from hid or tags self.merge(device, db_device) else: # Device is new and tags are not linked to a device device.tags.clear( ) # We don't want to add the transient dummy tags db.session.add(device) db_device = device db_device.tags |= tags # Union of tags the device had plus the (potentially) new ones try: db.session.flush() except IntegrityError as e: # Manage 'one tag per organization' unique constraint if 'One tag per organization' in e.args[0]: # todo test for this id = int(e.args[0][135:e.args[0].index(',', 135)]) raise ValidationError('The device is already linked to tag {} ' 'from the same organization.'.format(id), field_names=['device.tags']) else: raise assert db_device is not None return db_device