def set_inventory_config(cls, name: str = None, org_name: str = None, org_id: str = None, tag_url: boltons.urlutils.URL = None, tag_token: uuid.UUID = None): try: inventory = Inventory.current except ResourceNotFound: # No inventory defined in db yet inventory = Inventory(id=current_app.id, name=name, tag_provider=tag_url, tag_token=tag_token) db.session.add(inventory) if org_name or org_id: from ereuse_devicehub.resources.agent.models import Organization try: org = Organization.query.filter_by(tax_id=org_id, name=org_name).one() except ResourceNotFound: org = Organization(tax_id=org_id, name=org_name) org.default_of = inventory if tag_url: inventory.tag_provider = tag_url if tag_token: inventory.tag_token = tag_token
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 self.org_id == Organization.get_default_org_id()
def test_membership(): """Tests assigning an Individual to an Organization.""" person = Person(name='Timmy') org = Organization(name='ACME') person.member_of.add(Membership(org, person, id='acme-1')) db.session.add(person) db.session.flush()
def test_get_tags_endpoint(user: UserClient, app: Devicehub, requests_mock: requests_mock.mocker.Mocker): """Performs GET /tags after creating 3 tags, 2 printable and one not. Only the printable ones are returned. """ # Prepare test with app.app_context(): 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() assert not tag.printable requests_mock.post('https://example.com/', # request request_headers={ 'Authorization': 'Basic {}'.format(DevicehubClient.encode_token( '52dacef0-6bcb-4919-bfed-f10d2c96ecee')) }, # response json=['tag1id', 'tag2id'], status_code=201) user.post({}, res=Tag, query=[('num', 2)]) # Test itself data, _ = user.get(res=Tag) assert len(data['items']) == 2, 'Only 2 tags are printable, thus retreived' # Order is created descending assert data['items'][0]['id'] == 'tag2id' assert data['items'][0]['printable'] assert data['items'][1]['id'] == 'tag1id' assert data['items'][1]['printable'], 'Tags made this way are printable'
def test_membership_repeated(): person = Person(name='Timmy') org = Organization(name='ACME') person.member_of.add(Membership(org, person, id='acme-1')) db.session.add(person) person.member_of.add(Membership(org, person)) with pytest.raises(DBError): db.session.flush()
def test_tag_create_tags_cli(app: Devicehub, user: UserClient): """Checks creating tags with the CLI endpoint.""" owner_id = user.user['id'] runner = app.test_cli_runner() runner.invoke('tag', 'add', 'id1', '-u', owner_id) with app.app_context(): tag = Tag.query.one() # type: Tag assert tag.id == 'id1' assert tag.org.id == Organization.get_default_org_id()
def test_create_tag(user: UserClient): """Creates a tag specifying a custom organization.""" 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' assert tag.provider == URL('http://foo.bar') res, _ = user.get(res=Tag, item=tag.code, status=422) assert res['type'] == 'TagNotLinked'
def test_membership_repeating_id(): person = Person(name='Timmy') org = Organization(name='ACME') person.member_of.add(Membership(org, person, id='acme-1')) db.session.add(person) db.session.flush() person2 = Person(name='Tommy') person2.member_of.add(Membership(org, person2, id='acme-1')) db.session.add(person2) with pytest.raises(DBError) as e: db.session.flush() assert 'One member id per organization' in str(e)
def test_organization(): """Tests creating an organization.""" org = Organization(name='ACME', tax_id='xyz', country=Country.ES, email='*****@*****.**') db.session.add(org) db.session.commit() o = schemas.Organization().dump(org) assert o['name'] == org.name == 'ACME' assert o['taxId'] == org.tax_id == 'xyz' assert org.country.name == o['country'] == 'ES'
def test_create_two_same_tags(user: UserClient): """Ensures there cannot be two tags with the same ID and organization.""" db.session.add(Tag(id='foo-bar', owner_id=user.user['id'])) db.session.add(Tag(id='foo-bar', owner_id=user.user['id'])) with raises(DBError): db.session.commit() db.session.rollback() # And it works if tags are in different organizations 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'])) with raises(DBError): db.session.commit()
def test_delete_tags(user: UserClient, client: Client): """Delete a named tag.""" # Delete Tag Named g.user = User.query.one() 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.all()[-1] 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.all()[-1] 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.all()[-1] assert tag.id == 'bar-1'
class Tag(Thing): id = Column(db.CIText(), primary_key=True) id.comment = """The ID of the tag.""" 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 default=lambda: Organization.get_default_org_id()) org = relationship(Organization, backref=backref('tags', lazy=True), primaryjoin=Organization.id == org_id, collection_class=set) """The organization that issued the tag.""" provider = Column(URL()) provider.comment = """ The tag provider URL. If None, the provider is this Devicehub. """ device_id = Column(BigInteger, # We don't want to delete the tag on device deletion, only set to null ForeignKey(Device.id, ondelete=DB_CASCADE_SET_NULL)) device = relationship(Device, backref=backref('tags', lazy=True, collection_class=Tags), primaryjoin=Device.id == device_id) """The device linked to this tag.""" secondary = Column(db.CIText(), index=True) secondary.comment = """ A secondary identifier for this tag. It has the same constraints as the main one. Only needed in special cases. """ __table_args__ = ( db.Index('device_id_index', device_id, postgresql_using='hash'), ) def __init__(self, id: str, **kwargs) -> None: super().__init__(id=id, **kwargs) def like_etag(self): """Checks if the tag conforms to the `eTag spec <http: //devicehub.ereuse.org/tags.html#etags>`_. """ with suppress(ValueError): provider, id = self.id.split('-') if len(provider) == 2 and 5 <= len(id) <= 10: return True return False @classmethod def from_an_id(cls, id: str) -> Query: """Query to look for a tag from a possible identifier.""" return cls.query.filter((cls.id == id) | (cls.secondary == id)) @validates('id', 'secondary') def does_not_contain_slash(self, _, value: str): if '/' in value: raise ValidationError('Tags cannot contain slashes (/).') return value @validates('provider') def use_only_domain(self, _, url: URL): if url.path: raise ValidationError('Provider can only contain scheme and host', field_names=['provider']) return url __table_args__ = ( UniqueConstraint(id, org_id, name='one tag id per organization'), UniqueConstraint(secondary, org_id, name='one secondary tag per organization') ) @property def type(self) -> str: 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 self.org_id == Organization.get_default_org_id() @classmethod def is_printable_q(cls): """Return a SQLAlchemy filter expression for printable queries""" return cls.org_id == Organization.get_default_org_id() def __repr__(self) -> str: return '<Tag {0.id} org:{0.org_id} device:{0.device_id}>'.format(self) def __str__(self) -> str: return '{0.id} org: {0.org.name} device: {0.device}'.format(self) def __format__(self, format_spec: str) -> str: return '{0.org.name} {0.id}'.format(self)
def is_printable_q(cls): """Return a SQLAlchemy filter expression for printable queries""" return cls.org_id == Organization.get_default_org_id()
def test_organization_no_slash_name(): with pytest.raises(ValidationError): Organization(name='/')