Create eTags, lots, and events in dummy
This commit is contained in:
parent
32837f5f59
commit
f56ca473e8
|
@ -1,4 +1,7 @@
|
|||
import itertools
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Set
|
||||
|
||||
import click
|
||||
import click_spinner
|
||||
|
@ -7,22 +10,26 @@ import yaml
|
|||
from ereuse_devicehub.client import UserClient
|
||||
from ereuse_devicehub.db import db
|
||||
from ereuse_devicehub.resources.agent.models import Person
|
||||
from ereuse_devicehub.resources.event.models import Snapshot
|
||||
from ereuse_devicehub.resources.event import models as m
|
||||
from ereuse_devicehub.resources.inventory import Inventory
|
||||
from ereuse_devicehub.resources.tag.model import Tag
|
||||
from ereuse_devicehub.resources.user import User
|
||||
|
||||
|
||||
class Dummy:
|
||||
SNAPSHOTS = (
|
||||
'workbench-server-1',
|
||||
'computer-monitor'
|
||||
)
|
||||
TAGS = (
|
||||
'tag1',
|
||||
'tag2',
|
||||
'tag3'
|
||||
)
|
||||
"""Tags to create."""
|
||||
ET = (
|
||||
('A0000000000001', 'DT-AAAAA'),
|
||||
('A0000000000002', 'DT-BBBBB'),
|
||||
)
|
||||
"""eTags to create."""
|
||||
ORG = 'eReuse.org CAT', 'G-60437761', 'ES'
|
||||
"""An organization to create."""
|
||||
|
||||
def __init__(self, app) -> None:
|
||||
super().__init__()
|
||||
|
@ -34,20 +41,60 @@ class Dummy:
|
|||
'Do you want to continue?')
|
||||
def run(self):
|
||||
print('Preparing the database...'.ljust(30), end='')
|
||||
runner = self.app.test_cli_runner()
|
||||
with click_spinner.spinner():
|
||||
self.app.init_db(erase=True)
|
||||
out = runner.invoke(args=['create-org', *self.ORG], catch_exceptions=False).output
|
||||
org_id = json.loads(out)['id']
|
||||
user = self.user_client('user@dhub.com', '1234')
|
||||
# todo put user's agent into Org
|
||||
for id in self.TAGS:
|
||||
user.post({'id': id}, res=Tag)
|
||||
for id, sec in self.ET:
|
||||
runner.invoke(args=[
|
||||
'create-tag', id,
|
||||
'-p', 'https://t.devicetag.io',
|
||||
'-s', sec,
|
||||
'-o', org_id
|
||||
],
|
||||
catch_exceptions=False)
|
||||
files = tuple(Path(__file__).parent.joinpath('files').iterdir())
|
||||
print('done.')
|
||||
pcs = set() # type: Set[int]
|
||||
with click.progressbar(files, label='Creating devices...'.ljust(28)) as bar:
|
||||
for path in bar:
|
||||
with path.open() as f:
|
||||
snapshot = yaml.load(f)
|
||||
user.post(res=Snapshot, data=snapshot)
|
||||
s, _ = user.post(res=m.Snapshot, data=snapshot)
|
||||
pcs.add(s['device']['id'])
|
||||
inventory, _ = user.get(res=Inventory)
|
||||
assert len(inventory['devices'])
|
||||
|
||||
# Link tags and eTags
|
||||
for tag, pc in zip((self.TAGS[1], self.TAGS[2], self.ET[0][0], self.ET[1][1]), pcs):
|
||||
user.put({}, res=Tag, item='{}/device/{}'.format(tag, pc), status=204)
|
||||
|
||||
# Perform generic events
|
||||
for pc, model in zip(pcs,
|
||||
{m.ToRepair, m.Repair, m.ToPrepare, m.ReadyToUse, m.ToPrepare,
|
||||
m.Prepare}):
|
||||
user.post({'type': model.t, 'devices': [pc]}, res=m.Event)
|
||||
|
||||
# Perform a Sell to several devices
|
||||
user.post(
|
||||
{
|
||||
'type': m.Sell.t,
|
||||
'to': user.user['individuals'][0]['id'],
|
||||
'devices': list(itertools.islice(pcs, len(pcs) // 2))
|
||||
},
|
||||
res=m.Event)
|
||||
|
||||
from tests.test_lot import test_post_add_children_view, test_post_add_device_view
|
||||
test_post_add_children_view(user)
|
||||
|
||||
# todo this does not add devices to lots
|
||||
test_post_add_device_view(user)
|
||||
|
||||
print('⭐ Done.')
|
||||
|
||||
def user_client(self, email: str, password: str):
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import json
|
||||
|
||||
import click
|
||||
from flask import current_app as app
|
||||
from teal.db import SQLAlchemy
|
||||
|
@ -39,7 +41,9 @@ class OrganizationDef(AgentDef):
|
|||
))
|
||||
db.session.add(org)
|
||||
db.session.commit()
|
||||
return self.schema.dump(org)
|
||||
o = self.schema.dump(org)
|
||||
print(json.dumps(o, indent=2))
|
||||
return o
|
||||
|
||||
def init_db(self, db: SQLAlchemy):
|
||||
"""Creates the default organization."""
|
||||
|
|
|
@ -43,11 +43,6 @@ class Agent(Thing):
|
|||
telephone = Column(PhoneNumberType())
|
||||
email = Column(EmailType, unique=True)
|
||||
|
||||
user_id = Column(UUID(as_uuid=True), ForeignKey(User.id), unique=True)
|
||||
user = relationship(User,
|
||||
backref=backref('individuals', lazy=True, collection_class=set),
|
||||
primaryjoin=user_id == User.id)
|
||||
|
||||
__table_args__ = (
|
||||
UniqueConstraint(tax_id, country, name='Registration Number per country.'),
|
||||
)
|
||||
|
@ -100,6 +95,11 @@ class Individual(JoinedTableMixin, Agent):
|
|||
active_org_id = Column(UUID(as_uuid=True), ForeignKey(Organization.id))
|
||||
active_org = relationship(Organization, primaryjoin=active_org_id == Organization.id)
|
||||
|
||||
user_id = Column(UUID(as_uuid=True), ForeignKey(User.id), unique=True)
|
||||
user = relationship(User,
|
||||
backref=backref('individuals', lazy=True, collection_class=set),
|
||||
primaryjoin=user_id == User.id)
|
||||
|
||||
|
||||
class Membership(Thing):
|
||||
"""Organizations that are related to the Individual.
|
||||
|
|
|
@ -727,10 +727,7 @@ class Trade(JoinedTableMixin, EventWithMultipleDevices):
|
|||
If no price is set it is supposed that the trade was
|
||||
not payed, usual in donations.
|
||||
"""
|
||||
to_id = Column(UUID(as_uuid=True),
|
||||
ForeignKey(Agent.id),
|
||||
nullable=False,
|
||||
default=lambda: g.user.id)
|
||||
to_id = Column(UUID(as_uuid=True), ForeignKey(Agent.id), nullable=False)
|
||||
# todo compute the org
|
||||
to = relationship(Agent,
|
||||
backref=backref('events_to',
|
||||
|
@ -738,6 +735,9 @@ class Trade(JoinedTableMixin, EventWithMultipleDevices):
|
|||
collection_class=OrderedSet,
|
||||
order_by=lambda: Event.created),
|
||||
primaryjoin=to_id == Agent.id)
|
||||
to_comment = """
|
||||
The agent that gets the device due this deal.
|
||||
"""
|
||||
confirms_id = Column(UUID(as_uuid=True), ForeignKey(Organize.id))
|
||||
confirms = relationship(Organize,
|
||||
backref=backref('confirmation', lazy=True, uselist=False),
|
||||
|
|
|
@ -352,7 +352,7 @@ class Trade(EventWithMultipleDevices):
|
|||
shipping_date = DateTime(data_key='shippingDate')
|
||||
invoice_number = String(validate=Length(max=STR_SIZE), data_key='invoiceNumber')
|
||||
price = NestedOn(Price)
|
||||
to = NestedOn(Agent, only_query='id')
|
||||
to = NestedOn(Agent, only_query='id', required=True, comment=m.Trade.to_comment)
|
||||
confirms = NestedOn(Organize)
|
||||
|
||||
|
||||
|
|
|
@ -26,9 +26,10 @@ class Tag(Thing):
|
|||
primaryjoin=Organization.id == org_id,
|
||||
collection_class=set)
|
||||
"""The organization that issued the tag."""
|
||||
provider = Column(URL(),
|
||||
comment='The tag provider URL. If None, the provider is this Devicehub.')
|
||||
provider.comment = """The provider URL."""
|
||||
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))
|
||||
|
@ -68,6 +69,13 @@ class Tag(Thing):
|
|||
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(device_id, org_id, name='one_tag_per_org'),
|
||||
UniqueConstraint(secondary, org_id, name='one_secondary_per_org')
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
from marshmallow.fields import String
|
||||
from sqlalchemy.util import OrderedSet
|
||||
from teal.marshmallow import URL
|
||||
|
||||
from ereuse_devicehub.marshmallow import NestedOn
|
||||
from ereuse_devicehub.resources.agent.schemas import Organization
|
||||
from ereuse_devicehub.resources.device.schemas import Device
|
||||
from ereuse_devicehub.resources.schemas import Thing
|
||||
from ereuse_devicehub.resources.tag import model as m
|
||||
|
@ -19,5 +21,5 @@ class Tag(Thing):
|
|||
provider = URL(description=m.Tag.provider.comment,
|
||||
validator=without_slash)
|
||||
device = NestedOn(Device, dump_only=True)
|
||||
org = String()
|
||||
org = NestedOn(Organization, collection_class=OrderedSet, only_query='id')
|
||||
secondary = String(description=m.Tag.secondary.comment)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from click import argument, option
|
||||
from flask import current_app
|
||||
from teal.resource import Converters, Resource
|
||||
|
||||
from ereuse_devicehub.db import db
|
||||
|
@ -22,14 +23,26 @@ class UserDef(Resource):
|
|||
self.add_url_rule('/login', view_func=login, methods={'POST'})
|
||||
|
||||
@argument('email')
|
||||
@option('--password', prompt=True, hide_input=True, confirmation_prompt=True)
|
||||
def create_user(self, email: str, password: str) -> dict:
|
||||
"""
|
||||
Creates an user.
|
||||
@option('-a', '--agent', help='The name of an agent to create with the user.')
|
||||
@option('-c', '--country', help='The country of the agent (if --agent is set).')
|
||||
@option('-t', '--telephone', help='The telephone of the agent (if --agent is set).')
|
||||
@option('-t', '--tax-id', help='The tax id of the agent (if --agent is set).')
|
||||
@option('-p', '--password', prompt=True, hide_input=True, confirmation_prompt=True)
|
||||
def create_user(self, email: str, password: str, agent: str = None, country: str = None,
|
||||
telephone: str = None, tax_id: str = None) -> dict:
|
||||
"""Creates an user.
|
||||
|
||||
If ``--agent`` is passed, it creates an ``Individual`` agent
|
||||
that represents the user.
|
||||
"""
|
||||
from ereuse_devicehub.resources.agent.models import Individual
|
||||
u = self.SCHEMA(only={'email', 'password'}, exclude=('token',)) \
|
||||
.load({'email': email, 'password': password})
|
||||
user = User(**u)
|
||||
agent = Individual(**current_app.resources[Individual.t].schema.load(
|
||||
dict(name=agent, email=email, country=country, telephone=telephone, taxId=tax_id)
|
||||
))
|
||||
user.individuals.add(agent)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
return self.schema.dump(user)
|
||||
|
|
|
@ -3,6 +3,8 @@ from uuid import UUID
|
|||
|
||||
import pytest
|
||||
from sqlalchemy_utils import Password
|
||||
from teal.enums import Country
|
||||
from teal.marshmallow import ValidationError
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from ereuse_devicehub.client import Client
|
||||
|
@ -11,23 +13,33 @@ from ereuse_devicehub.devicehub import Devicehub
|
|||
from ereuse_devicehub.resources.user import UserDef
|
||||
from ereuse_devicehub.resources.user.exceptions import WrongCredentials
|
||||
from ereuse_devicehub.resources.user.models import User
|
||||
from teal.marshmallow import ValidationError
|
||||
from tests.conftest import app_context, create_user
|
||||
|
||||
|
||||
@pytest.mark.usefixtures(app_context.__name__)
|
||||
def test_create_user_method(app: Devicehub):
|
||||
def test_create_user_method_with_agent(app: Devicehub):
|
||||
"""
|
||||
Tests creating an user through the main method.
|
||||
|
||||
This method checks that the token is correct, too.
|
||||
"""
|
||||
user_def = app.resources['User'] # type: UserDef
|
||||
u = user_def.create_user(email='foo@foo.com', password='foo')
|
||||
u = user_def.create_user(email='foo@foo.com',
|
||||
password='foo',
|
||||
agent='Nice Person',
|
||||
country=Country.ES.name,
|
||||
telephone='+34 666 66 66 66',
|
||||
tax_id='1234')
|
||||
user = User.query.filter_by(id=u['id']).one() # type: User
|
||||
assert user.email == 'foo@foo.com'
|
||||
assert isinstance(user.token, UUID)
|
||||
assert User.query.filter_by(email='foo@foo.com').one() == user
|
||||
individual = next(iter(user.individuals))
|
||||
assert individual.name == 'Nice Person'
|
||||
assert individual.tax_id == '1234'
|
||||
assert individual.telephone.e164 == '+34666666666'
|
||||
assert individual.country == Country.ES
|
||||
assert individual.email == user.email
|
||||
|
||||
|
||||
@pytest.mark.usefixtures(app_context.__name__)
|
||||
|
|
Reference in a new issue