Implement organization and product tables
added: migrations, models, simple db api, tests for db api Addresses-Spec: https://review.openstack.org/#/c/268922/ Change-Id: Ie02c2f160db195fb0e793b6bd9284f2bb006ecbd
This commit is contained in:
parent
efe42b6589
commit
759f687fe2
|
@ -47,3 +47,23 @@ SHARED_TEST_RUN = 'shared'
|
|||
# Roles
|
||||
ROLE_USER = 'user'
|
||||
ROLE_OWNER = 'owner'
|
||||
|
||||
# Organization types.
|
||||
# OpenStack Foundation
|
||||
FOUNDATION = 0
|
||||
# User's private unofficial Vendor (allows creation and testing
|
||||
# of user's products)
|
||||
PRIVATE_VENDOR = 1
|
||||
# Vendor applied and waiting for official status.
|
||||
PENDING_VENDOR = 2
|
||||
# Official Vendor approved by the Foundation.
|
||||
OFFICIAL_VENDOR = 3
|
||||
|
||||
# Product object types.
|
||||
CLOUD = 0
|
||||
SOFTWARE = 1
|
||||
|
||||
# Product specific types.
|
||||
DISTRO = 0
|
||||
PUBLIC_CLOUD = 1
|
||||
HOSTED_PRIVATE_CLOUD = 2
|
||||
|
|
|
@ -170,3 +170,43 @@ def add_user_to_group(user_openid, group_id, created_by_user):
|
|||
def remove_user_from_group(user_openid, group_id):
|
||||
"""Remove specified user from specified group."""
|
||||
return IMPL.remove_user_from_group(user_openid, group_id)
|
||||
|
||||
|
||||
def add_organization(organization_info, creator):
|
||||
"""Add organization."""
|
||||
return IMPL.add_organization(organization_info, creator)
|
||||
|
||||
|
||||
def update_organization(organization_info):
|
||||
"""Update organization."""
|
||||
return IMPL.update_organization(organization_info)
|
||||
|
||||
|
||||
def get_organization(organization_id):
|
||||
"""Get organization by id."""
|
||||
return IMPL.get_organization(organization_id)
|
||||
|
||||
|
||||
def delete_organization(organization_id):
|
||||
"""delete organization by id."""
|
||||
return IMPL.delete_organization(organization_id)
|
||||
|
||||
|
||||
def add_product(product_info, creator):
|
||||
"""Add product from product_info dicionary with creator."""
|
||||
return IMPL.add_product(product_info, creator)
|
||||
|
||||
|
||||
def update_product(product_info):
|
||||
"""Update product from prodict_info dicionary."""
|
||||
return IMPL.update_product(product_info)
|
||||
|
||||
|
||||
def get_product(id):
|
||||
"""Get product by id."""
|
||||
return IMPL.get_product(id)
|
||||
|
||||
|
||||
def delete_product(id):
|
||||
"""delete product by id."""
|
||||
return IMPL.delete_product(id)
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
"""Create organization table.
|
||||
|
||||
Revision ID: 19fded785b8c
|
||||
Revises: 319ee8fe47c7
|
||||
Create Date: 2016-01-18 14:40:00
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '19fded785b8c'
|
||||
down_revision = '319ee8fe47c7'
|
||||
MYSQL_CHARSET = 'utf8'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
"""Upgrade DB."""
|
||||
op.create_table(
|
||||
'organization',
|
||||
sa.Column('updated_at', sa.DateTime()),
|
||||
sa.Column('deleted_at', sa.DateTime()),
|
||||
sa.Column('deleted', sa.Integer, default=0),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('id', sa.String(36), nullable=False),
|
||||
sa.Column('type', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=80), nullable=False),
|
||||
sa.Column('description', sa.Text()),
|
||||
sa.Column('group_id', sa.String(36), nullable=False),
|
||||
sa.Column('created_by_user', sa.String(128), nullable=False),
|
||||
sa.Column('properties', sa.Text()),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.ForeignKeyConstraint(['group_id'], ['group.id'], ),
|
||||
sa.ForeignKeyConstraint(['created_by_user'], ['user.openid'], ),
|
||||
mysql_charset=MYSQL_CHARSET
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
"""Downgrade DB."""
|
||||
op.drop_table('organization')
|
|
@ -0,0 +1,45 @@
|
|||
"""Create product table.
|
||||
|
||||
Revision ID: 7092392cbb8e
|
||||
Revises: 19fded785b8c
|
||||
Create Date: 2016-01-18 16:10:00
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '7092392cbb8e'
|
||||
down_revision = '19fded785b8c'
|
||||
MYSQL_CHARSET = 'utf8'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
"""Upgrade DB."""
|
||||
op.create_table(
|
||||
'product',
|
||||
sa.Column('updated_at', sa.DateTime()),
|
||||
sa.Column('deleted_at', sa.DateTime()),
|
||||
sa.Column('deleted', sa.Integer, default=0),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('created_by_user', sa.String(128), nullable=False),
|
||||
sa.Column('id', sa.String(36), nullable=False),
|
||||
sa.Column('name', sa.String(length=80), nullable=False),
|
||||
sa.Column('description', sa.Text()),
|
||||
sa.Column('product_id', sa.String(36), nullable=False),
|
||||
sa.Column('type', sa.Integer(), nullable=False),
|
||||
sa.Column('product_type', sa.Integer(), nullable=False),
|
||||
sa.Column('public', sa.Boolean(), nullable=False),
|
||||
sa.Column('organization_id', sa.String(36), nullable=False),
|
||||
sa.Column('properties', sa.Text()),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.ForeignKeyConstraint(['organization_id'], ['organization.id'], ),
|
||||
sa.ForeignKeyConstraint(['created_by_user'], ['user.openid'], ),
|
||||
mysql_charset=MYSQL_CHARSET
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
"""Downgrade DB."""
|
||||
op.drop_table('product')
|
|
@ -356,3 +356,130 @@ def remove_user_from_group(user_openid, group_id):
|
|||
filter_by(user_openid=user_openid).
|
||||
filter_by(group_id=group_id).
|
||||
delete(synchronize_session=False))
|
||||
|
||||
|
||||
def add_organization(organization_info, creator):
|
||||
"""Add organization."""
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
group = models.Group()
|
||||
group.name = 'Group for %s' % organization_info['name']
|
||||
group.save(session=session)
|
||||
group_id = group.id
|
||||
|
||||
item = models.UserToGroup()
|
||||
item.user_openid = creator
|
||||
item.group_id = group_id
|
||||
item.created_by_user = creator
|
||||
item.save(session=session)
|
||||
|
||||
organization = models.Organization()
|
||||
organization.type = organization_info.get(
|
||||
'type', api_const.PRIVATE_VENDOR)
|
||||
organization.name = organization_info['name']
|
||||
organization.description = organization_info.get('description')
|
||||
organization.group_id = group_id
|
||||
organization.created_by_user = creator
|
||||
organization.properties = organization_info.get('properties')
|
||||
organization.save(session=session)
|
||||
|
||||
return _to_dict(organization)
|
||||
|
||||
|
||||
def update_organization(organization_info):
|
||||
"""Update organization."""
|
||||
session = get_session()
|
||||
_id = organization_info['id']
|
||||
organization = (session.query(models.Organization).
|
||||
filter_by(id=_id).first())
|
||||
if organization is None:
|
||||
raise NotFound('Organization with id %s not found' % _id)
|
||||
|
||||
with session.begin():
|
||||
organization.type = organization_info.get(
|
||||
'type', organization.type)
|
||||
organization.name = organization_info.get(
|
||||
'name', organization.name)
|
||||
organization.description = organization_info.get(
|
||||
'description', organization.description)
|
||||
organization.properties = organization_info.get(
|
||||
'properties', organization.properties)
|
||||
organization.save(session=session)
|
||||
|
||||
|
||||
def get_organization(organization_id):
|
||||
"""Get organization by id."""
|
||||
session = get_session()
|
||||
organization = (session.query(models.Organization).
|
||||
filter_by(id=organization_id).first())
|
||||
if organization is None:
|
||||
raise NotFound('Organization with id %s not found' % organization_id)
|
||||
return _to_dict(organization)
|
||||
|
||||
|
||||
def delete_organization(organization_id):
|
||||
"""delete organization by id."""
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
(session.query(models.Product).
|
||||
filter_by(organization_id=organization_id).
|
||||
delete(synchronize_session=False))
|
||||
(session.query(models.Organization).
|
||||
filter_by(id=organization_id).
|
||||
delete(synchronize_session=False))
|
||||
|
||||
|
||||
def add_product(product_info, creator):
|
||||
"""Add product."""
|
||||
product = models.Product()
|
||||
_id = six.text_type(uuid.uuid4())
|
||||
product.id = _id
|
||||
product.type = product_info['type']
|
||||
product.product_type = product_info['product_type']
|
||||
product.product_id = product_info['product_id']
|
||||
product.name = product_info['name']
|
||||
product.description = product_info.get('description')
|
||||
product.organization_id = product_info['organization_id']
|
||||
product.created_by_user = creator
|
||||
product.public = product_info.get('public', False)
|
||||
product.properties = product_info.get('properties')
|
||||
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
product.save(session=session)
|
||||
return _to_dict(product)
|
||||
|
||||
|
||||
def update_product(product_info):
|
||||
"""Update product by product_id."""
|
||||
session = get_session()
|
||||
_id = product_info.get('product_id')
|
||||
product = session.query(models.Product).filter_by(product_id=_id).first()
|
||||
if product is None:
|
||||
raise NotFound('Product with product_id %s not found' % _id)
|
||||
|
||||
product.name = product_info.get('name', product.name)
|
||||
product.description = product_info.get('description', product.description)
|
||||
product.public = product_info.get('public', product.public)
|
||||
product.properties = product_info.get('properties', product.properties)
|
||||
|
||||
with session.begin():
|
||||
product.save(session=session)
|
||||
return _to_dict(product)
|
||||
|
||||
|
||||
def get_product(id):
|
||||
"""Get product by id."""
|
||||
session = get_session()
|
||||
product = session.query(models.Product).filter_by(id=id).first()
|
||||
if product is None:
|
||||
raise NotFound('Product with id "%s" not found' % id)
|
||||
return _to_dict(product)
|
||||
|
||||
|
||||
def delete_product(id):
|
||||
"""delete product by id."""
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
(session.query(models.Product).filter_by(id=id).
|
||||
delete(synchronize_session=False))
|
||||
|
|
|
@ -193,3 +193,52 @@ class UserToGroup(BASE, RefStackBase): # pragma: no cover
|
|||
def default_allowed_keys(self):
|
||||
"""Default keys."""
|
||||
return 'user_openid', 'group_id'
|
||||
|
||||
|
||||
class Organization(BASE, RefStackBase): # pragma: no cover
|
||||
"""Organization definition."""
|
||||
|
||||
__tablename__ = 'organization'
|
||||
|
||||
id = sa.Column(sa.String(36), primary_key=True,
|
||||
default=lambda: six.text_type(uuid.uuid4()))
|
||||
type = sa.Column(sa.Integer, nullable=False)
|
||||
name = sa.Column(sa.String(80), nullable=False)
|
||||
description = sa.Column(sa.Text())
|
||||
group_id = sa.Column(sa.String(36), sa.ForeignKey('group.id'),
|
||||
nullable=False)
|
||||
created_by_user = sa.Column(sa.String(128), sa.ForeignKey('user.openid'),
|
||||
nullable=False)
|
||||
properties = sa.Column(sa.Text())
|
||||
|
||||
@property
|
||||
def default_allowed_keys(self):
|
||||
"""Default keys."""
|
||||
return ('id', 'type', 'name', 'description', 'group_id',
|
||||
'created_by_user', 'properties')
|
||||
|
||||
|
||||
class Product(BASE, RefStackBase): # pragma: no cover
|
||||
"""Product definition."""
|
||||
|
||||
__tablename__ = 'product'
|
||||
|
||||
id = sa.Column(sa.Integer(), primary_key=True)
|
||||
product_id = sa.Column(sa.String(36), nullable=False)
|
||||
name = sa.Column(sa.String(80), nullable=False)
|
||||
description = sa.Column(sa.Text())
|
||||
organization_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('organization.id'),
|
||||
nullable=False)
|
||||
created_by_user = sa.Column(sa.String(128), sa.ForeignKey('user.openid'),
|
||||
nullable=False)
|
||||
public = sa.Column(sa.Boolean(), nullable=False)
|
||||
properties = sa.Column(sa.Text())
|
||||
type = sa.Column(sa.Integer(), nullable=False)
|
||||
product_type = sa.Column(sa.Integer(), nullable=False)
|
||||
|
||||
@property
|
||||
def default_allowed_keys(self):
|
||||
"""Default keys."""
|
||||
return ('id', 'product_id', 'name', 'description', 'organization_id',
|
||||
'created_by_user', 'properties', 'type', 'product_type')
|
||||
|
|
|
@ -638,3 +638,131 @@ class DBBackendTestCase(base.BaseTestCase):
|
|||
mock.call().filter_by(group_id='GUID'),
|
||||
mock.call().filter_by().delete(synchronize_session=False)))
|
||||
session.begin.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(api, 'get_session')
|
||||
@mock.patch('refstack.db.sqlalchemy.models.Organization')
|
||||
@mock.patch('refstack.db.sqlalchemy.models.Group')
|
||||
@mock.patch('refstack.db.sqlalchemy.models.UserToGroup')
|
||||
@mock.patch.object(api, '_to_dict', side_effect=lambda x: x)
|
||||
def test_organization_add(self, mock_to_dict, mock_model_user_to_group,
|
||||
mock_model_group, mock_model_organization,
|
||||
mock_get_session):
|
||||
|
||||
organization_info = {'name': 'a', 'description': 'b', 'type': 1}
|
||||
session = mock_get_session.return_value
|
||||
organization = mock_model_organization.return_value
|
||||
result = api.add_organization(organization_info, 'user-123')
|
||||
self.assertEqual(result, organization)
|
||||
|
||||
group = mock_model_group.return_value
|
||||
self.assertIsNotNone(group.id)
|
||||
self.assertIsNotNone(organization.id)
|
||||
self.assertIsNotNone(organization.group_id)
|
||||
|
||||
mock_model_organization.assert_called_once_with()
|
||||
mock_model_group.assert_called_once_with()
|
||||
mock_model_user_to_group.assert_called_once_with()
|
||||
mock_get_session.assert_called_once_with()
|
||||
organization.save.assert_called_once_with(session=session)
|
||||
group.save.assert_called_once_with(session=session)
|
||||
user_to_group = mock_model_user_to_group.return_value
|
||||
user_to_group.save.assert_called_once_with(session=session)
|
||||
session.begin.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(api, 'get_session')
|
||||
@mock.patch('refstack.db.sqlalchemy.models.Product')
|
||||
@mock.patch.object(api, '_to_dict', side_effect=lambda x: x)
|
||||
def test_product_add(self, mock_to_dict, mock_product, mock_get_session):
|
||||
session = mock_get_session.return_value
|
||||
product = mock_product.return_value
|
||||
product_info = {'product_id': 'hash_or_guid', 'name': 'a',
|
||||
'organization_id': 'GUID0', 'type': 0,
|
||||
'product_type': 0}
|
||||
result = api.add_product(product_info, 'user-123')
|
||||
self.assertEqual(result, product)
|
||||
|
||||
self.assertIsNotNone(product.id)
|
||||
|
||||
mock_get_session.assert_called_once_with()
|
||||
product.save.assert_called_once_with(session=session)
|
||||
session.begin.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(api, 'get_session')
|
||||
@mock.patch('refstack.db.sqlalchemy.models.Product')
|
||||
def test_incomplete_product_add(self, mock_product, mock_get_session):
|
||||
product_info = {}
|
||||
self.assertRaises(KeyError, api.add_product, product_info, 'u')
|
||||
|
||||
@mock.patch.object(api, 'get_session')
|
||||
@mock.patch('refstack.db.sqlalchemy.models.Product.save')
|
||||
def test_product_update(self, mock_product_save, mock_get_session):
|
||||
session = mock_get_session.return_value
|
||||
query = session.query.return_value
|
||||
filtered = query.filter_by.return_value
|
||||
product = models.Product()
|
||||
product.product_id = '123'
|
||||
filtered.first.return_value = product
|
||||
|
||||
product_info = {'product_id': '098', 'name': 'a', 'description': 'b',
|
||||
'creator_openid': 'abc', 'organization_id': '1',
|
||||
'type': 0, 'product_type': 0}
|
||||
api.update_product(product_info)
|
||||
|
||||
self.assertEqual('123', product.product_id)
|
||||
self.assertIsNone(product.created_by_user)
|
||||
self.assertIsNone(product.organization_id)
|
||||
self.assertIsNone(product.type)
|
||||
self.assertIsNone(product.product_type)
|
||||
|
||||
mock_get_session.assert_called_once_with()
|
||||
mock_product_save.assert_called_once_with(session=session)
|
||||
session.begin.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(api, 'get_session',
|
||||
return_value=mock.Mock(name='session'),)
|
||||
@mock.patch('refstack.db.sqlalchemy.models.Organization')
|
||||
@mock.patch.object(api, '_to_dict', side_effect=lambda x: x)
|
||||
def test_organization_get(self, mock_to_dict, mock_model,
|
||||
mock_get_session):
|
||||
organization_id = 12345
|
||||
session = mock_get_session.return_value
|
||||
query = session.query.return_value
|
||||
filtered = query.filter_by.return_value
|
||||
organization = filtered.first.return_value
|
||||
|
||||
result = api.get_organization(organization_id)
|
||||
self.assertEqual(result, organization)
|
||||
|
||||
session.query.assert_called_once_with(mock_model)
|
||||
query.filter_by.assert_called_once_with(id=organization_id)
|
||||
filtered.first.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(api, 'get_session',
|
||||
return_value=mock.Mock(name='session'),)
|
||||
@mock.patch('refstack.db.sqlalchemy.models.Product')
|
||||
@mock.patch.object(api, '_to_dict', side_effect=lambda x: x)
|
||||
def test_product_get(self, mock_to_dict, mock_model, mock_get_session):
|
||||
_id = 12345
|
||||
session = mock_get_session.return_value
|
||||
query = session.query.return_value
|
||||
filtered = query.filter_by.return_value
|
||||
product = filtered.first.return_value
|
||||
|
||||
result = api.get_product(_id)
|
||||
self.assertEqual(result, product)
|
||||
|
||||
session.query.assert_called_once_with(mock_model)
|
||||
query.filter_by.assert_called_once_with(id=_id)
|
||||
filtered.first.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(api, 'get_session')
|
||||
@mock.patch('refstack.db.sqlalchemy.api.models')
|
||||
def test_product_delete(self, mock_models, mock_get_session):
|
||||
session = mock_get_session.return_value
|
||||
db.delete_product('product_id')
|
||||
|
||||
session.query.assert_called_once_with(mock_models.Product)
|
||||
session.query.return_value.filter_by.assert_has_calls((
|
||||
mock.call(id='product_id'),
|
||||
mock.call().delete(synchronize_session=False)))
|
||||
session.begin.assert_called_once_with()
|
||||
|
|
Loading…
Reference in New Issue