Add namespace support for workbooks
This patch brings namespace support to workbooks. Namespace of the workbook is inherited by workflows. Implements: blueprint mistral-namespace-for-actions-workbooks Change-Id: I2c66b3961915f0f35a9c468eb6dd0c0c70995234
This commit is contained in:
parent
0867becb8f
commit
834747b5d9
@ -41,6 +41,7 @@ class Workbook(resource.Resource, ScopedResource):
|
|||||||
|
|
||||||
id = wtypes.text
|
id = wtypes.text
|
||||||
name = wtypes.text
|
name = wtypes.text
|
||||||
|
namespace = wtypes.text
|
||||||
|
|
||||||
definition = wtypes.text
|
definition = wtypes.text
|
||||||
"workbook definition in Mistral v2 DSL"
|
"workbook definition in Mistral v2 DSL"
|
||||||
@ -62,7 +63,8 @@ class Workbook(resource.Resource, ScopedResource):
|
|||||||
scope='private',
|
scope='private',
|
||||||
project_id='a7eb669e9819420ea4bd1453e672c0a7',
|
project_id='a7eb669e9819420ea4bd1453e672c0a7',
|
||||||
created_at='1970-01-01T00:00:00.000000',
|
created_at='1970-01-01T00:00:00.000000',
|
||||||
updated_at='1970-01-01T00:00:00.000000')
|
updated_at='1970-01-01T00:00:00.000000',
|
||||||
|
namespace='')
|
||||||
|
|
||||||
|
|
||||||
class Workbooks(resource.ResourceList):
|
class Workbooks(resource.ResourceList):
|
||||||
|
@ -43,11 +43,12 @@ class WorkbooksController(rest.RestController, hooks.HookController):
|
|||||||
spec_parser.get_workbook_spec_from_yaml)
|
spec_parser.get_workbook_spec_from_yaml)
|
||||||
|
|
||||||
@rest_utils.wrap_wsme_controller_exception
|
@rest_utils.wrap_wsme_controller_exception
|
||||||
@wsme_pecan.wsexpose(resources.Workbook, wtypes.text)
|
@wsme_pecan.wsexpose(resources.Workbook, wtypes.text, wtypes.text)
|
||||||
def get(self, name):
|
def get(self, name, namespace=''):
|
||||||
"""Return the named workbook.
|
"""Return the named workbook.
|
||||||
|
|
||||||
:param name: Name of workbook to retrieve
|
:param name: Name of workbook to retrieve
|
||||||
|
:param namespace: Namespace of workbook to retrieve
|
||||||
"""
|
"""
|
||||||
acl.enforce('workbooks:get', context.ctx())
|
acl.enforce('workbooks:get', context.ctx())
|
||||||
|
|
||||||
@ -55,13 +56,15 @@ class WorkbooksController(rest.RestController, hooks.HookController):
|
|||||||
|
|
||||||
# Use retries to prevent possible failures.
|
# Use retries to prevent possible failures.
|
||||||
r = rest_utils.create_db_retry_object()
|
r = rest_utils.create_db_retry_object()
|
||||||
db_model = r.call(db_api.get_workbook, name)
|
db_model = r.call(db_api.get_workbook,
|
||||||
|
name,
|
||||||
|
namespace=namespace)
|
||||||
|
|
||||||
return resources.Workbook.from_db_model(db_model)
|
return resources.Workbook.from_db_model(db_model)
|
||||||
|
|
||||||
@rest_utils.wrap_pecan_controller_exception
|
@rest_utils.wrap_pecan_controller_exception
|
||||||
@pecan.expose(content_type="text/plain")
|
@pecan.expose(content_type="text/plain")
|
||||||
def put(self):
|
def put(self, namespace=''):
|
||||||
"""Update a workbook."""
|
"""Update a workbook."""
|
||||||
acl.enforce('workbooks:update', context.ctx())
|
acl.enforce('workbooks:update', context.ctx())
|
||||||
|
|
||||||
@ -73,15 +76,23 @@ class WorkbooksController(rest.RestController, hooks.HookController):
|
|||||||
LOG.debug("Update workbook [definition=%s]", definition)
|
LOG.debug("Update workbook [definition=%s]", definition)
|
||||||
|
|
||||||
wb_db = rest_utils.rest_retry_on_db_error(
|
wb_db = rest_utils.rest_retry_on_db_error(
|
||||||
workbooks.update_workbook_v2
|
workbooks.update_workbook_v2)(
|
||||||
)(definition, scope=scope)
|
definition,
|
||||||
|
namespace=namespace,
|
||||||
|
scope=scope
|
||||||
|
)
|
||||||
|
|
||||||
return resources.Workbook.from_db_model(wb_db).to_json()
|
return resources.Workbook.from_db_model(wb_db).to_json()
|
||||||
|
|
||||||
@rest_utils.wrap_pecan_controller_exception
|
@rest_utils.wrap_pecan_controller_exception
|
||||||
@pecan.expose(content_type="text/plain")
|
@pecan.expose(content_type="text/plain")
|
||||||
def post(self):
|
def post(self, namespace=''):
|
||||||
"""Create a new workbook."""
|
"""Create a new workbook.
|
||||||
|
|
||||||
|
:param namespace: Optional. The namespace to create the workbook
|
||||||
|
in. Workbooks with the same name can be added to a given
|
||||||
|
project if they are in two different namespaces.
|
||||||
|
"""
|
||||||
acl.enforce('workbooks:create', context.ctx())
|
acl.enforce('workbooks:create', context.ctx())
|
||||||
|
|
||||||
definition = pecan.request.text
|
definition = pecan.request.text
|
||||||
@ -92,16 +103,19 @@ class WorkbooksController(rest.RestController, hooks.HookController):
|
|||||||
LOG.debug("Create workbook [definition=%s]", definition)
|
LOG.debug("Create workbook [definition=%s]", definition)
|
||||||
|
|
||||||
wb_db = rest_utils.rest_retry_on_db_error(
|
wb_db = rest_utils.rest_retry_on_db_error(
|
||||||
workbooks.create_workbook_v2
|
workbooks.create_workbook_v2)(
|
||||||
)(definition, scope=scope)
|
definition,
|
||||||
|
namespace=namespace,
|
||||||
|
scope=scope
|
||||||
|
)
|
||||||
|
|
||||||
pecan.response.status = 201
|
pecan.response.status = 201
|
||||||
|
|
||||||
return resources.Workbook.from_db_model(wb_db).to_json()
|
return resources.Workbook.from_db_model(wb_db).to_json()
|
||||||
|
|
||||||
@rest_utils.wrap_wsme_controller_exception
|
@rest_utils.wrap_wsme_controller_exception
|
||||||
@wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
|
@wsme_pecan.wsexpose(None, wtypes.text, wtypes.text, status_code=204)
|
||||||
def delete(self, name):
|
def delete(self, name, namespace=''):
|
||||||
"""Delete the named workbook.
|
"""Delete the named workbook.
|
||||||
|
|
||||||
:param name: Name of workbook to delete
|
:param name: Name of workbook to delete
|
||||||
@ -110,17 +124,21 @@ class WorkbooksController(rest.RestController, hooks.HookController):
|
|||||||
|
|
||||||
LOG.debug("Delete workbook [name=%s]", name)
|
LOG.debug("Delete workbook [name=%s]", name)
|
||||||
|
|
||||||
rest_utils.rest_retry_on_db_error(db_api.delete_workbook)(name)
|
rest_utils.rest_retry_on_db_error(db_api.delete_workbook)(
|
||||||
|
name,
|
||||||
|
namespace
|
||||||
|
)
|
||||||
|
|
||||||
@rest_utils.wrap_wsme_controller_exception
|
@rest_utils.wrap_wsme_controller_exception
|
||||||
@wsme_pecan.wsexpose(resources.Workbooks, types.uuid, int,
|
@wsme_pecan.wsexpose(resources.Workbooks, types.uuid, int,
|
||||||
types.uniquelist, types.list, types.uniquelist,
|
types.uniquelist, types.list, types.uniquelist,
|
||||||
wtypes.text, wtypes.text, wtypes.text,
|
wtypes.text, wtypes.text, wtypes.text,
|
||||||
resources.SCOPE_TYPES, wtypes.text, wtypes.text)
|
resources.SCOPE_TYPES, wtypes.text,
|
||||||
|
wtypes.text, wtypes.text)
|
||||||
def get_all(self, marker=None, limit=None, sort_keys='created_at',
|
def get_all(self, marker=None, limit=None, sort_keys='created_at',
|
||||||
sort_dirs='asc', fields='', created_at=None,
|
sort_dirs='asc', fields='', created_at=None,
|
||||||
definition=None, name=None, scope=None, tags=None,
|
definition=None, name=None, scope=None, tags=None,
|
||||||
updated_at=None):
|
updated_at=None, namespace=None):
|
||||||
"""Return a list of workbooks.
|
"""Return a list of workbooks.
|
||||||
|
|
||||||
:param marker: Optional. Pagination marker for large data sets.
|
:param marker: Optional. Pagination marker for large data sets.
|
||||||
@ -154,7 +172,8 @@ class WorkbooksController(rest.RestController, hooks.HookController):
|
|||||||
name=name,
|
name=name,
|
||||||
scope=scope,
|
scope=scope,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
updated_at=updated_at
|
updated_at=updated_at,
|
||||||
|
namespace=namespace
|
||||||
)
|
)
|
||||||
|
|
||||||
LOG.debug("Fetch workbooks. marker=%s, limit=%s, sort_keys=%s, "
|
LOG.debug("Fetch workbooks. marker=%s, limit=%s, sort_keys=%s, "
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
# Copyright 2018 OpenStack Foundation.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""add namespace column to workbooks
|
||||||
|
|
||||||
|
Revision ID: 028
|
||||||
|
Revises: 027
|
||||||
|
Create Date: 2018-07-17 15:39:25.031935
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '028'
|
||||||
|
down_revision = '027'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.engine import reflection
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
|
||||||
|
op.add_column(
|
||||||
|
'workbooks_v2',
|
||||||
|
sa.Column('namespace', sa.String(length=255), nullable=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
inspect = reflection.Inspector.from_engine(op.get_bind())
|
||||||
|
|
||||||
|
unique_constraints = [
|
||||||
|
unique_constraint['name'] for unique_constraint in
|
||||||
|
inspect.get_unique_constraints('workbooks_v2')
|
||||||
|
]
|
||||||
|
|
||||||
|
if 'name' in unique_constraints:
|
||||||
|
op.drop_index('name', table_name='workbooks_v2')
|
||||||
|
|
||||||
|
op.create_unique_constraint(
|
||||||
|
None,
|
||||||
|
'workbooks_v2',
|
||||||
|
['name', 'namespace', 'project_id']
|
||||||
|
)
|
@ -71,13 +71,13 @@ def acquire_lock(model, id):
|
|||||||
|
|
||||||
# Workbooks.
|
# Workbooks.
|
||||||
|
|
||||||
def get_workbook(name, fields=()):
|
def get_workbook(name, namespace, fields=()):
|
||||||
return IMPL.get_workbook(name, fields=fields)
|
return IMPL.get_workbook(name, namespace=namespace, fields=fields)
|
||||||
|
|
||||||
|
|
||||||
def load_workbook(name, fields=()):
|
def load_workbook(name, namespace, fields=()):
|
||||||
"""Unlike get_workbook this method is allowed to return None."""
|
"""Unlike get_workbook this method is allowed to return None."""
|
||||||
return IMPL.load_workbook(name, fields=fields)
|
return IMPL.load_workbook(name, namespace=namespace, fields=fields)
|
||||||
|
|
||||||
|
|
||||||
def get_workbooks(limit=None, marker=None, sort_keys=None,
|
def get_workbooks(limit=None, marker=None, sort_keys=None,
|
||||||
@ -104,8 +104,8 @@ def create_or_update_workbook(name, values):
|
|||||||
return IMPL.create_or_update_workbook(name, values)
|
return IMPL.create_or_update_workbook(name, values)
|
||||||
|
|
||||||
|
|
||||||
def delete_workbook(name):
|
def delete_workbook(name, namespace=None):
|
||||||
IMPL.delete_workbook(name)
|
IMPL.delete_workbook(name, namespace)
|
||||||
|
|
||||||
|
|
||||||
def delete_workbooks(**kwargs):
|
def delete_workbooks(**kwargs):
|
||||||
@ -147,8 +147,8 @@ def create_workflow_definition(values):
|
|||||||
return IMPL.create_workflow_definition(values)
|
return IMPL.create_workflow_definition(values)
|
||||||
|
|
||||||
|
|
||||||
def update_workflow_definition(identifier, values, namespace):
|
def update_workflow_definition(identifier, values):
|
||||||
return IMPL.update_workflow_definition(identifier, values, namespace)
|
return IMPL.update_workflow_definition(identifier, values)
|
||||||
|
|
||||||
|
|
||||||
def create_or_update_workflow_definition(name, values):
|
def create_or_update_workflow_definition(name, values):
|
||||||
|
@ -319,23 +319,55 @@ def _get_db_object_by_name_and_namespace_or_id(model, identifier,
|
|||||||
return query.first()
|
return query.first()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_db_object_by_name_and_namespace(model, name,
|
||||||
|
namespace, insecure=False,
|
||||||
|
columns=()):
|
||||||
|
query = (
|
||||||
|
b.model_query(model, columns=columns)
|
||||||
|
if insecure
|
||||||
|
else _secure_query(model, *columns)
|
||||||
|
)
|
||||||
|
|
||||||
|
if namespace is None:
|
||||||
|
namespace = ''
|
||||||
|
|
||||||
|
query = query.filter(
|
||||||
|
sa.and_(
|
||||||
|
model.name == name,
|
||||||
|
model.namespace == namespace
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return query.first()
|
||||||
|
|
||||||
|
|
||||||
# Workbook definitions.
|
# Workbook definitions.
|
||||||
|
|
||||||
@b.session_aware()
|
@b.session_aware()
|
||||||
def get_workbook(name, fields=(), session=None):
|
def get_workbook(name, namespace=None, fields=(), session=None):
|
||||||
wb = _get_db_object_by_name(models.Workbook, name, columns=fields)
|
wb = _get_db_object_by_name_and_namespace(
|
||||||
|
models.Workbook,
|
||||||
|
name,
|
||||||
|
namespace,
|
||||||
|
columns=fields
|
||||||
|
)
|
||||||
|
|
||||||
if not wb:
|
if not wb:
|
||||||
raise exc.DBEntityNotFoundError(
|
raise exc.DBEntityNotFoundError(
|
||||||
"Workbook not found [workbook_name=%s]" % name
|
"Workbook not found [name=%s, namespace=%s]" % (name, namespace)
|
||||||
)
|
)
|
||||||
|
|
||||||
return wb
|
return wb
|
||||||
|
|
||||||
|
|
||||||
@b.session_aware()
|
@b.session_aware()
|
||||||
def load_workbook(name, fields=(), session=None):
|
def load_workbook(name, namespace=None, fields=(), session=None):
|
||||||
return _get_db_object_by_name(models.Workbook, name, columns=fields)
|
return _get_db_object_by_name_and_namespace(
|
||||||
|
models.Workbook,
|
||||||
|
name,
|
||||||
|
namespace,
|
||||||
|
columns=fields
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@b.session_aware()
|
@b.session_aware()
|
||||||
@ -353,8 +385,9 @@ def create_workbook(values, session=None):
|
|||||||
wb.save(session=session)
|
wb.save(session=session)
|
||||||
except db_exc.DBDuplicateEntry:
|
except db_exc.DBDuplicateEntry:
|
||||||
raise exc.DBDuplicateEntryError(
|
raise exc.DBDuplicateEntryError(
|
||||||
"Duplicate entry for WorkbookDefinition ['name', 'project_id']: "
|
"Duplicate entry for WorkbookDefinition "
|
||||||
"{}, {}".format(wb.name, wb.project_id)
|
"['name', 'namespace', 'project_id']: {}, {}, {}".format(
|
||||||
|
wb.name, wb.namespace, wb.project_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
return wb
|
return wb
|
||||||
@ -362,7 +395,8 @@ def create_workbook(values, session=None):
|
|||||||
|
|
||||||
@b.session_aware()
|
@b.session_aware()
|
||||||
def update_workbook(name, values, session=None):
|
def update_workbook(name, values, session=None):
|
||||||
wb = get_workbook(name)
|
namespace = values.get('namespace')
|
||||||
|
wb = get_workbook(name, namespace=namespace)
|
||||||
|
|
||||||
wb.update(values.copy())
|
wb.update(values.copy())
|
||||||
|
|
||||||
@ -378,13 +412,20 @@ def create_or_update_workbook(name, values, session=None):
|
|||||||
|
|
||||||
|
|
||||||
@b.session_aware()
|
@b.session_aware()
|
||||||
def delete_workbook(name, session=None):
|
def delete_workbook(name, namespace=None, session=None):
|
||||||
|
namespace = namespace or ''
|
||||||
|
|
||||||
count = _secure_query(models.Workbook).filter(
|
count = _secure_query(models.Workbook).filter(
|
||||||
models.Workbook.name == name).delete()
|
sa.and_(
|
||||||
|
models.Workbook.name == name,
|
||||||
|
models.Workbook.namespace == namespace
|
||||||
|
)
|
||||||
|
).delete()
|
||||||
|
|
||||||
if count == 0:
|
if count == 0:
|
||||||
raise exc.DBEntityNotFoundError(
|
raise exc.DBEntityNotFoundError(
|
||||||
"Workbook not found [workbook_name=%s]" % name
|
"Workbook not found [workbook_name=%s, namespace=%s]"
|
||||||
|
% (name, namespace)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -490,7 +531,8 @@ def create_workflow_definition(values, session=None):
|
|||||||
|
|
||||||
|
|
||||||
@b.session_aware()
|
@b.session_aware()
|
||||||
def update_workflow_definition(identifier, values, namespace='', session=None):
|
def update_workflow_definition(identifier, values, session=None):
|
||||||
|
namespace = values.get('namespace')
|
||||||
wf_def = get_workflow_definition(identifier, namespace=namespace)
|
wf_def = get_workflow_definition(identifier, namespace=namespace)
|
||||||
|
|
||||||
m_dbutils.check_db_obj_access(wf_def)
|
m_dbutils.check_db_obj_access(wf_def)
|
||||||
@ -528,10 +570,13 @@ def update_workflow_definition(identifier, values, namespace='', session=None):
|
|||||||
|
|
||||||
@b.session_aware()
|
@b.session_aware()
|
||||||
def create_or_update_workflow_definition(name, values, session=None):
|
def create_or_update_workflow_definition(name, values, session=None):
|
||||||
if not _get_db_object_by_name(models.WorkflowDefinition, name):
|
namespace = values.get('namespace')
|
||||||
return create_workflow_definition(values)
|
if _get_db_object_by_name_and_namespace_or_id(
|
||||||
else:
|
models.WorkflowDefinition,
|
||||||
|
name,
|
||||||
|
namespace=namespace):
|
||||||
return update_workflow_definition(name, values)
|
return update_workflow_definition(name, values)
|
||||||
|
return create_workflow_definition(values)
|
||||||
|
|
||||||
|
|
||||||
@b.session_aware()
|
@b.session_aware()
|
||||||
|
@ -113,9 +113,14 @@ class Workbook(Definition):
|
|||||||
"""Contains info about workbook (including definition in Mistral DSL)."""
|
"""Contains info about workbook (including definition in Mistral DSL)."""
|
||||||
|
|
||||||
__tablename__ = 'workbooks_v2'
|
__tablename__ = 'workbooks_v2'
|
||||||
|
namespace = sa.Column(sa.String(255), nullable=True)
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
sa.UniqueConstraint('name', 'project_id'),
|
sa.UniqueConstraint(
|
||||||
|
'name',
|
||||||
|
'namespace',
|
||||||
|
'project_id'
|
||||||
|
),
|
||||||
sa.Index('%s_project_id' % __tablename__, 'project_id'),
|
sa.Index('%s_project_id' % __tablename__, 'project_id'),
|
||||||
sa.Index('%s_scope' % __tablename__, 'scope'),
|
sa.Index('%s_scope' % __tablename__, 'scope'),
|
||||||
)
|
)
|
||||||
|
@ -17,39 +17,43 @@ from mistral.lang import parser as spec_parser
|
|||||||
from mistral.services import actions
|
from mistral.services import actions
|
||||||
|
|
||||||
|
|
||||||
def create_workbook_v2(definition, scope='private'):
|
def create_workbook_v2(definition, namespace='', scope='private'):
|
||||||
wb_spec = spec_parser.get_workbook_spec_from_yaml(definition)
|
wb_spec = spec_parser.get_workbook_spec_from_yaml(definition)
|
||||||
|
|
||||||
wb_values = _get_workbook_values(
|
wb_values = _get_workbook_values(
|
||||||
wb_spec,
|
wb_spec,
|
||||||
definition,
|
definition,
|
||||||
scope
|
scope,
|
||||||
|
namespace
|
||||||
)
|
)
|
||||||
|
|
||||||
with db_api_v2.transaction():
|
with db_api_v2.transaction():
|
||||||
wb_db = db_api_v2.create_workbook(wb_values)
|
wb_db = db_api_v2.create_workbook(wb_values)
|
||||||
|
|
||||||
_on_workbook_update(wb_db, wb_spec)
|
_on_workbook_update(wb_db, wb_spec, namespace)
|
||||||
|
|
||||||
return wb_db
|
return wb_db
|
||||||
|
|
||||||
|
|
||||||
def update_workbook_v2(definition, scope='private'):
|
def update_workbook_v2(definition, namespace='', scope='private'):
|
||||||
wb_spec = spec_parser.get_workbook_spec_from_yaml(definition)
|
wb_spec = spec_parser.get_workbook_spec_from_yaml(definition)
|
||||||
|
|
||||||
values = _get_workbook_values(wb_spec, definition, scope)
|
values = _get_workbook_values(wb_spec, definition, scope, namespace)
|
||||||
|
|
||||||
with db_api_v2.transaction():
|
with db_api_v2.transaction():
|
||||||
wb_db = db_api_v2.update_workbook(values['name'], values)
|
wb_db = db_api_v2.update_workbook(values['name'], values)
|
||||||
|
|
||||||
_, db_wfs = _on_workbook_update(wb_db, wb_spec)
|
_, db_wfs = _on_workbook_update(wb_db, wb_spec, namespace)
|
||||||
|
|
||||||
return wb_db
|
return wb_db
|
||||||
|
|
||||||
|
|
||||||
def _on_workbook_update(wb_db, wb_spec):
|
def _on_workbook_update(wb_db, wb_spec, namespace):
|
||||||
|
# TODO(hardikj) Handle actions for namespace
|
||||||
db_actions = _create_or_update_actions(wb_db, wb_spec.get_actions())
|
db_actions = _create_or_update_actions(wb_db, wb_spec.get_actions())
|
||||||
db_wfs = _create_or_update_workflows(wb_db, wb_spec.get_workflows())
|
db_wfs = _create_or_update_workflows(wb_db,
|
||||||
|
wb_spec.get_workflows(),
|
||||||
|
namespace)
|
||||||
|
|
||||||
return db_actions, db_wfs
|
return db_actions, db_wfs
|
||||||
|
|
||||||
@ -86,7 +90,7 @@ def _create_or_update_actions(wb_db, actions_spec):
|
|||||||
return db_actions
|
return db_actions
|
||||||
|
|
||||||
|
|
||||||
def _create_or_update_workflows(wb_db, workflows_spec):
|
def _create_or_update_workflows(wb_db, workflows_spec, namespace):
|
||||||
db_wfs = []
|
db_wfs = []
|
||||||
|
|
||||||
if workflows_spec:
|
if workflows_spec:
|
||||||
@ -99,7 +103,7 @@ def _create_or_update_workflows(wb_db, workflows_spec):
|
|||||||
'spec': wf_spec.to_dict(),
|
'spec': wf_spec.to_dict(),
|
||||||
'scope': wb_db.scope,
|
'scope': wb_db.scope,
|
||||||
'project_id': wb_db.project_id,
|
'project_id': wb_db.project_id,
|
||||||
'namespace': '',
|
'namespace': namespace,
|
||||||
'tags': wf_spec.get_tags(),
|
'tags': wf_spec.get_tags(),
|
||||||
'is_system': False
|
'is_system': False
|
||||||
}
|
}
|
||||||
@ -111,13 +115,14 @@ def _create_or_update_workflows(wb_db, workflows_spec):
|
|||||||
return db_wfs
|
return db_wfs
|
||||||
|
|
||||||
|
|
||||||
def _get_workbook_values(wb_spec, definition, scope):
|
def _get_workbook_values(wb_spec, definition, scope, namespace=None):
|
||||||
values = {
|
values = {
|
||||||
'name': wb_spec.get_name(),
|
'name': wb_spec.get_name(),
|
||||||
'tags': wb_spec.get_tags(),
|
'tags': wb_spec.get_tags(),
|
||||||
'definition': definition,
|
'definition': definition,
|
||||||
'spec': wb_spec.to_dict(),
|
'spec': wb_spec.to_dict(),
|
||||||
'scope': scope,
|
'scope': scope,
|
||||||
|
'namespace': namespace,
|
||||||
'is_system': False
|
'is_system': False
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,6 +165,5 @@ def _update_workflow(wf_spec, definition, scope, identifier=None,
|
|||||||
|
|
||||||
return db_api.update_workflow_definition(
|
return db_api.update_workflow_definition(
|
||||||
identifier if identifier else values['name'],
|
identifier if identifier else values['name'],
|
||||||
values,
|
values
|
||||||
namespace=namespace
|
|
||||||
)
|
)
|
||||||
|
@ -72,6 +72,17 @@ WORKBOOK = {
|
|||||||
'updated_at': '1970-01-01 00:00:00'
|
'updated_at': '1970-01-01 00:00:00'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WB_WITH_NAMESPACE = {
|
||||||
|
'id': '123',
|
||||||
|
'name': 'test',
|
||||||
|
'namespace': 'xyz',
|
||||||
|
'definition': WORKBOOK_DEF,
|
||||||
|
'tags': ['deployment', 'demo'],
|
||||||
|
'scope': 'public',
|
||||||
|
'created_at': '1970-01-01 00:00:00',
|
||||||
|
'updated_at': '1970-01-01 00:00:00'
|
||||||
|
}
|
||||||
|
|
||||||
ACTION = {
|
ACTION = {
|
||||||
'id': '123e4567-e89b-12d3-a456-426655440000',
|
'id': '123e4567-e89b-12d3-a456-426655440000',
|
||||||
'name': 'step',
|
'name': 'step',
|
||||||
@ -95,6 +106,8 @@ ACTION_DB.update(ACTION)
|
|||||||
WORKBOOK_DB = models.Workbook()
|
WORKBOOK_DB = models.Workbook()
|
||||||
WORKBOOK_DB.update(WORKBOOK)
|
WORKBOOK_DB.update(WORKBOOK)
|
||||||
|
|
||||||
|
WB_DB_WITH_NAMESPACE = models.Workbook(**WB_WITH_NAMESPACE)
|
||||||
|
|
||||||
WF_DB = models.WorkflowDefinition()
|
WF_DB = models.WorkflowDefinition()
|
||||||
WF_DB.update(WF)
|
WF_DB.update(WF)
|
||||||
|
|
||||||
@ -139,6 +152,7 @@ workflows:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
MOCK_WORKBOOK = mock.MagicMock(return_value=WORKBOOK_DB)
|
MOCK_WORKBOOK = mock.MagicMock(return_value=WORKBOOK_DB)
|
||||||
|
MOCK_WB_WITH_NAMESPACE = mock.MagicMock(return_value=WB_DB_WITH_NAMESPACE)
|
||||||
MOCK_WORKBOOKS = mock.MagicMock(return_value=[WORKBOOK_DB])
|
MOCK_WORKBOOKS = mock.MagicMock(return_value=[WORKBOOK_DB])
|
||||||
MOCK_UPDATED_WORKBOOK = mock.MagicMock(return_value=UPDATED_WORKBOOK_DB)
|
MOCK_UPDATED_WORKBOOK = mock.MagicMock(return_value=UPDATED_WORKBOOK_DB)
|
||||||
MOCK_DELETE = mock.MagicMock(return_value=None)
|
MOCK_DELETE = mock.MagicMock(return_value=None)
|
||||||
@ -155,6 +169,13 @@ class TestWorkbooksController(base.APITest):
|
|||||||
self.assertEqual(200, resp.status_int)
|
self.assertEqual(200, resp.status_int)
|
||||||
self.assertDictEqual(WORKBOOK, resp.json)
|
self.assertDictEqual(WORKBOOK, resp.json)
|
||||||
|
|
||||||
|
@mock.patch.object(db_api, "get_workbook", MOCK_WB_WITH_NAMESPACE)
|
||||||
|
def test_get_with_namespace(self):
|
||||||
|
resp = self.app.get('/v2/workbooks/123?namespace=xyz')
|
||||||
|
|
||||||
|
self.assertEqual(200, resp.status_int)
|
||||||
|
self.assertDictEqual(WB_WITH_NAMESPACE, resp.json)
|
||||||
|
|
||||||
@mock.patch.object(db_api, 'get_workbook')
|
@mock.patch.object(db_api, 'get_workbook')
|
||||||
def test_get_operational_error(self, mocked_get):
|
def test_get_operational_error(self, mocked_get):
|
||||||
mocked_get.side_effect = [
|
mocked_get.side_effect = [
|
||||||
@ -258,6 +279,19 @@ class TestWorkbooksController(base.APITest):
|
|||||||
self.assertEqual(201, resp.status_int)
|
self.assertEqual(201, resp.status_int)
|
||||||
self.assertEqual(WORKBOOK, resp.json)
|
self.assertEqual(WORKBOOK, resp.json)
|
||||||
|
|
||||||
|
@mock.patch.object(workbooks, "create_workbook_v2", MOCK_WB_WITH_NAMESPACE)
|
||||||
|
def test_post_namespace(self):
|
||||||
|
|
||||||
|
namespace = 'xyz'
|
||||||
|
resp = self.app.post(
|
||||||
|
'/v2/workbooks?namespace=%s' % namespace,
|
||||||
|
WORKBOOK_DEF,
|
||||||
|
headers={'Content-Type': 'text/plain'}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(201, resp.status_int)
|
||||||
|
self.assertEqual(WB_WITH_NAMESPACE, resp.json)
|
||||||
|
|
||||||
@mock.patch.object(workbooks, "create_workbook_v2", MOCK_DUPLICATE)
|
@mock.patch.object(workbooks, "create_workbook_v2", MOCK_DUPLICATE)
|
||||||
def test_post_dup(self):
|
def test_post_dup(self):
|
||||||
resp = self.app.post(
|
resp = self.app.post(
|
||||||
|
@ -37,6 +37,7 @@ ADM_CTX = test_base.get_context(default=False, admin=True)
|
|||||||
WORKBOOKS = [
|
WORKBOOKS = [
|
||||||
{
|
{
|
||||||
'name': 'my_workbook1',
|
'name': 'my_workbook1',
|
||||||
|
'namespace': 'test',
|
||||||
'definition': 'empty',
|
'definition': 'empty',
|
||||||
'spec': {},
|
'spec': {},
|
||||||
'tags': ['mc'],
|
'tags': ['mc'],
|
||||||
@ -48,6 +49,7 @@ WORKBOOKS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'my_workbook2',
|
'name': 'my_workbook2',
|
||||||
|
'namespace': 'test',
|
||||||
'description': 'my description',
|
'description': 'my description',
|
||||||
'definition': 'empty',
|
'definition': 'empty',
|
||||||
'spec': {},
|
'spec': {},
|
||||||
@ -58,6 +60,19 @@ WORKBOOKS = [
|
|||||||
'trust_id': '12345',
|
'trust_id': '12345',
|
||||||
'created_at': datetime.datetime(2016, 12, 1, 15, 1, 0)
|
'created_at': datetime.datetime(2016, 12, 1, 15, 1, 0)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'name': 'my_workbook3',
|
||||||
|
'namespace': '',
|
||||||
|
'description': 'my description',
|
||||||
|
'definition': 'empty',
|
||||||
|
'spec': {},
|
||||||
|
'tags': ['nonamespace'],
|
||||||
|
'scope': 'private',
|
||||||
|
'updated_at': None,
|
||||||
|
'project_id': '1233',
|
||||||
|
'trust_id': '12345',
|
||||||
|
'created_at': datetime.datetime(2018, 7, 1, 15, 1, 0)
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -74,6 +89,19 @@ class WorkbookTest(SQLAlchemyTest):
|
|||||||
def test_create_and_get_and_load_workbook(self):
|
def test_create_and_get_and_load_workbook(self):
|
||||||
created = db_api.create_workbook(WORKBOOKS[0])
|
created = db_api.create_workbook(WORKBOOKS[0])
|
||||||
|
|
||||||
|
fetched = db_api.get_workbook(created['name'], created['namespace'])
|
||||||
|
|
||||||
|
self.assertEqual(created, fetched)
|
||||||
|
|
||||||
|
fetched = db_api.load_workbook(created.name, created.namespace)
|
||||||
|
|
||||||
|
self.assertEqual(created, fetched)
|
||||||
|
|
||||||
|
self.assertIsNone(db_api.load_workbook("not-existing-wb"))
|
||||||
|
|
||||||
|
def test_create_and_get_and_load_workbook_with_default_namespace(self):
|
||||||
|
created = db_api.create_workbook(WORKBOOKS[2])
|
||||||
|
|
||||||
fetched = db_api.get_workbook(created['name'])
|
fetched = db_api.get_workbook(created['name'])
|
||||||
|
|
||||||
self.assertEqual(created, fetched)
|
self.assertEqual(created, fetched)
|
||||||
@ -82,14 +110,13 @@ class WorkbookTest(SQLAlchemyTest):
|
|||||||
|
|
||||||
self.assertEqual(created, fetched)
|
self.assertEqual(created, fetched)
|
||||||
|
|
||||||
self.assertIsNone(db_api.load_workbook("not-existing-wb"))
|
|
||||||
|
|
||||||
def test_get_workbook_with_fields(self):
|
def test_get_workbook_with_fields(self):
|
||||||
with db_api.transaction():
|
with db_api.transaction():
|
||||||
created = db_api.create_workbook(WORKBOOKS[0])
|
created = db_api.create_workbook(WORKBOOKS[0])
|
||||||
|
|
||||||
fetched = db_api.get_workbook(
|
fetched = db_api.get_workbook(
|
||||||
created['name'],
|
created['name'],
|
||||||
|
namespace=created['namespace'],
|
||||||
fields=(db_models.Workbook.scope,)
|
fields=(db_models.Workbook.scope,)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -104,8 +131,9 @@ class WorkbookTest(SQLAlchemyTest):
|
|||||||
|
|
||||||
self.assertRaisesWithMessage(
|
self.assertRaisesWithMessage(
|
||||||
exc.DBDuplicateEntryError,
|
exc.DBDuplicateEntryError,
|
||||||
"Duplicate entry for WorkbookDefinition ['name', 'project_id']:"
|
"Duplicate entry for WorkbookDefinition "
|
||||||
" my_workbook1, <default-project>",
|
"['name', 'namespace', 'project_id']:"
|
||||||
|
" my_workbook1, test, <default-project>",
|
||||||
db_api.create_workbook,
|
db_api.create_workbook,
|
||||||
WORKBOOKS[0]
|
WORKBOOKS[0]
|
||||||
)
|
)
|
||||||
@ -117,20 +145,27 @@ class WorkbookTest(SQLAlchemyTest):
|
|||||||
|
|
||||||
updated = db_api.update_workbook(
|
updated = db_api.update_workbook(
|
||||||
created.name,
|
created.name,
|
||||||
{'definition': 'my new definition'}
|
{
|
||||||
|
'definition': 'my new definition',
|
||||||
|
'namespace': 'test'
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual('my new definition', updated.definition)
|
self.assertEqual('my new definition', updated.definition)
|
||||||
|
|
||||||
fetched = db_api.get_workbook(created['name'])
|
fetched = db_api.get_workbook(
|
||||||
|
created['name'],
|
||||||
|
namespace=created['namespace']
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEqual(updated, fetched)
|
self.assertEqual(updated, fetched)
|
||||||
self.assertIsNotNone(fetched.updated_at)
|
self.assertIsNotNone(fetched.updated_at)
|
||||||
|
|
||||||
def test_create_or_update_workbook(self):
|
def test_create_or_update_workbook(self):
|
||||||
name = WORKBOOKS[0]['name']
|
name = WORKBOOKS[0]['name']
|
||||||
|
namespace = WORKBOOKS[0]['namespace']
|
||||||
|
|
||||||
self.assertIsNone(db_api.load_workbook(name))
|
self.assertIsNone(db_api.load_workbook(name, namespace=namespace))
|
||||||
|
|
||||||
created = db_api.create_or_update_workbook(
|
created = db_api.create_or_update_workbook(
|
||||||
name,
|
name,
|
||||||
@ -142,16 +177,19 @@ class WorkbookTest(SQLAlchemyTest):
|
|||||||
|
|
||||||
updated = db_api.create_or_update_workbook(
|
updated = db_api.create_or_update_workbook(
|
||||||
created.name,
|
created.name,
|
||||||
{'definition': 'my new definition'}
|
{
|
||||||
|
'definition': 'my new definition',
|
||||||
|
'namespace': 'test'
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual('my new definition', updated.definition)
|
self.assertEqual('my new definition', updated.definition)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'my new definition',
|
'my new definition',
|
||||||
db_api.load_workbook(updated.name).definition
|
db_api.load_workbook(updated.name, updated.namespace).definition
|
||||||
)
|
)
|
||||||
|
|
||||||
fetched = db_api.get_workbook(created.name)
|
fetched = db_api.get_workbook(created.name, created.namespace)
|
||||||
|
|
||||||
self.assertEqual(updated, fetched)
|
self.assertEqual(updated, fetched)
|
||||||
|
|
||||||
@ -331,16 +369,17 @@ class WorkbookTest(SQLAlchemyTest):
|
|||||||
def test_delete_workbook(self):
|
def test_delete_workbook(self):
|
||||||
created = db_api.create_workbook(WORKBOOKS[0])
|
created = db_api.create_workbook(WORKBOOKS[0])
|
||||||
|
|
||||||
fetched = db_api.get_workbook(created.name)
|
fetched = db_api.get_workbook(created.name, created.namespace)
|
||||||
|
|
||||||
self.assertEqual(created, fetched)
|
self.assertEqual(created, fetched)
|
||||||
|
|
||||||
db_api.delete_workbook(created.name)
|
db_api.delete_workbook(created.name, created.namespace)
|
||||||
|
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exc.DBEntityNotFoundError,
|
exc.DBEntityNotFoundError,
|
||||||
db_api.get_workbook,
|
db_api.get_workbook,
|
||||||
created.name
|
created.name,
|
||||||
|
created.namespace
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_workbooks_in_two_projects(self):
|
def test_workbooks_in_two_projects(self):
|
||||||
@ -2714,7 +2753,7 @@ class TXTest(SQLAlchemyTest):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
created = db_api.create_workbook(WORKBOOKS[0])
|
created = db_api.create_workbook(WORKBOOKS[0])
|
||||||
fetched = db_api.get_workbook(created.name)
|
fetched = db_api.get_workbook(created.name, namespace='test')
|
||||||
|
|
||||||
self.assertEqual(created, fetched)
|
self.assertEqual(created, fetched)
|
||||||
self.assertTrue(self.is_db_session_open())
|
self.assertTrue(self.is_db_session_open())
|
||||||
@ -2736,7 +2775,7 @@ class TXTest(SQLAlchemyTest):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
created = db_api.create_workbook(WORKBOOKS[0])
|
created = db_api.create_workbook(WORKBOOKS[0])
|
||||||
fetched = db_api.get_workbook(created.name)
|
fetched = db_api.get_workbook(created.name, namespace='test')
|
||||||
|
|
||||||
self.assertEqual(created, fetched)
|
self.assertEqual(created, fetched)
|
||||||
self.assertTrue(self.is_db_session_open())
|
self.assertTrue(self.is_db_session_open())
|
||||||
@ -2747,7 +2786,7 @@ class TXTest(SQLAlchemyTest):
|
|||||||
|
|
||||||
self.assertFalse(self.is_db_session_open())
|
self.assertFalse(self.is_db_session_open())
|
||||||
|
|
||||||
fetched = db_api.get_workbook(created.name)
|
fetched = db_api.get_workbook(created.name, namespace='test')
|
||||||
|
|
||||||
self.assertEqual(created, fetched)
|
self.assertEqual(created, fetched)
|
||||||
self.assertFalse(self.is_db_session_open())
|
self.assertFalse(self.is_db_session_open())
|
||||||
@ -2755,14 +2794,14 @@ class TXTest(SQLAlchemyTest):
|
|||||||
def test_commit_transaction(self):
|
def test_commit_transaction(self):
|
||||||
with db_api.transaction():
|
with db_api.transaction():
|
||||||
created = db_api.create_workbook(WORKBOOKS[0])
|
created = db_api.create_workbook(WORKBOOKS[0])
|
||||||
fetched = db_api.get_workbook(created.name)
|
fetched = db_api.get_workbook(created.name, namespace='test')
|
||||||
|
|
||||||
self.assertEqual(created, fetched)
|
self.assertEqual(created, fetched)
|
||||||
self.assertTrue(self.is_db_session_open())
|
self.assertTrue(self.is_db_session_open())
|
||||||
|
|
||||||
self.assertFalse(self.is_db_session_open())
|
self.assertFalse(self.is_db_session_open())
|
||||||
|
|
||||||
fetched = db_api.get_workbook(created.name)
|
fetched = db_api.get_workbook(created.name, namespace='test')
|
||||||
|
|
||||||
self.assertEqual(created, fetched)
|
self.assertEqual(created, fetched)
|
||||||
self.assertFalse(self.is_db_session_open())
|
self.assertFalse(self.is_db_session_open())
|
||||||
@ -2777,7 +2816,10 @@ class TXTest(SQLAlchemyTest):
|
|||||||
self.assertEqual(created, fetched)
|
self.assertEqual(created, fetched)
|
||||||
|
|
||||||
created_wb = db_api.create_workbook(WORKBOOKS[0])
|
created_wb = db_api.create_workbook(WORKBOOKS[0])
|
||||||
fetched_wb = db_api.get_workbook(created_wb.name)
|
fetched_wb = db_api.get_workbook(
|
||||||
|
created_wb.name,
|
||||||
|
namespace=created_wb.namespace
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEqual(created_wb, fetched_wb)
|
self.assertEqual(created_wb, fetched_wb)
|
||||||
self.assertTrue(self.is_db_session_open())
|
self.assertTrue(self.is_db_session_open())
|
||||||
@ -2804,7 +2846,10 @@ class TXTest(SQLAlchemyTest):
|
|||||||
try:
|
try:
|
||||||
with db_api.transaction():
|
with db_api.transaction():
|
||||||
created = db_api.create_workbook(WORKBOOKS[0])
|
created = db_api.create_workbook(WORKBOOKS[0])
|
||||||
fetched = db_api.get_workbook(created.name)
|
fetched = db_api.get_workbook(
|
||||||
|
created.name,
|
||||||
|
namespace=created.namespace
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEqual(created, fetched)
|
self.assertEqual(created, fetched)
|
||||||
self.assertTrue(self.is_db_session_open())
|
self.assertTrue(self.is_db_session_open())
|
||||||
@ -2831,7 +2876,10 @@ class TXTest(SQLAlchemyTest):
|
|||||||
self.assertEqual(created, fetched)
|
self.assertEqual(created, fetched)
|
||||||
|
|
||||||
created_wb = db_api.create_workbook(WORKBOOKS[0])
|
created_wb = db_api.create_workbook(WORKBOOKS[0])
|
||||||
fetched_wb = db_api.get_workbook(created_wb.name)
|
fetched_wb = db_api.get_workbook(
|
||||||
|
created_wb.name,
|
||||||
|
namespace=created_wb.namespace
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEqual(created_wb, fetched_wb)
|
self.assertEqual(created_wb, fetched_wb)
|
||||||
self.assertTrue(self.is_db_session_open())
|
self.assertTrue(self.is_db_session_open())
|
||||||
@ -2847,7 +2895,10 @@ class TXTest(SQLAlchemyTest):
|
|||||||
|
|
||||||
self.assertEqual(created, fetched)
|
self.assertEqual(created, fetched)
|
||||||
|
|
||||||
fetched_wb = db_api.get_workbook(created_wb.name)
|
fetched_wb = db_api.get_workbook(
|
||||||
|
created_wb.name,
|
||||||
|
namespace=created_wb.namespace
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEqual(created_wb, fetched_wb)
|
self.assertEqual(created_wb, fetched_wb)
|
||||||
|
|
||||||
|
@ -170,10 +170,13 @@ ACTION_DEFINITION = """concat:
|
|||||||
|
|
||||||
class WorkbookServiceTest(base.DbTestCase):
|
class WorkbookServiceTest(base.DbTestCase):
|
||||||
def test_create_workbook(self):
|
def test_create_workbook(self):
|
||||||
wb_db = wb_service.create_workbook_v2(WORKBOOK)
|
namespace = 'test_workbook_service_0123_namespace'
|
||||||
|
|
||||||
|
wb_db = wb_service.create_workbook_v2(WORKBOOK, namespace=namespace)
|
||||||
|
|
||||||
self.assertIsNotNone(wb_db)
|
self.assertIsNotNone(wb_db)
|
||||||
self.assertEqual('my_wb', wb_db.name)
|
self.assertEqual('my_wb', wb_db.name)
|
||||||
|
self.assertEqual(namespace, wb_db.namespace)
|
||||||
self.assertEqual(WORKBOOK, wb_db.definition)
|
self.assertEqual(WORKBOOK, wb_db.definition)
|
||||||
self.assertIsNotNone(wb_db.spec)
|
self.assertIsNotNone(wb_db.spec)
|
||||||
self.assertListEqual(['test'], wb_db.tags)
|
self.assertListEqual(['test'], wb_db.tags)
|
||||||
@ -205,6 +208,7 @@ class WorkbookServiceTest(base.DbTestCase):
|
|||||||
self.assertEqual('reverse', wf1_spec.get_type())
|
self.assertEqual('reverse', wf1_spec.get_type())
|
||||||
self.assertListEqual(['wf_test'], wf1_spec.get_tags())
|
self.assertListEqual(['wf_test'], wf1_spec.get_tags())
|
||||||
self.assertListEqual(['wf_test'], wf1_db.tags)
|
self.assertListEqual(['wf_test'], wf1_db.tags)
|
||||||
|
self.assertEqual(namespace, wf1_db.namespace)
|
||||||
self.assertEqual(WORKBOOK_WF1_DEFINITION, wf1_db.definition)
|
self.assertEqual(WORKBOOK_WF1_DEFINITION, wf1_db.definition)
|
||||||
|
|
||||||
# Workflow 2.
|
# Workflow 2.
|
||||||
@ -213,20 +217,36 @@ class WorkbookServiceTest(base.DbTestCase):
|
|||||||
|
|
||||||
self.assertEqual('wf2', wf2_spec.get_name())
|
self.assertEqual('wf2', wf2_spec.get_name())
|
||||||
self.assertEqual('direct', wf2_spec.get_type())
|
self.assertEqual('direct', wf2_spec.get_type())
|
||||||
|
self.assertEqual(namespace, wf2_db.namespace)
|
||||||
self.assertEqual(WORKBOOK_WF2_DEFINITION, wf2_db.definition)
|
self.assertEqual(WORKBOOK_WF2_DEFINITION, wf2_db.definition)
|
||||||
|
|
||||||
def test_update_workbook(self):
|
def test_create_workbook_with_default_namespace(self):
|
||||||
# Create workbook.
|
|
||||||
wb_db = wb_service.create_workbook_v2(WORKBOOK)
|
wb_db = wb_service.create_workbook_v2(WORKBOOK)
|
||||||
|
|
||||||
|
self.assertIsNotNone(wb_db)
|
||||||
|
self.assertEqual('my_wb', wb_db.name)
|
||||||
|
self.assertEqual('', wb_db.namespace)
|
||||||
|
|
||||||
|
db_api.delete_workbook('my_wb')
|
||||||
|
|
||||||
|
def test_update_workbook(self):
|
||||||
|
namespace = 'test_workbook_service_0123_namespace'
|
||||||
|
|
||||||
|
# Create workbook.
|
||||||
|
wb_db = wb_service.create_workbook_v2(WORKBOOK, namespace=namespace)
|
||||||
|
|
||||||
self.assertIsNotNone(wb_db)
|
self.assertIsNotNone(wb_db)
|
||||||
self.assertEqual(2, len(db_api.get_workflow_definitions()))
|
self.assertEqual(2, len(db_api.get_workflow_definitions()))
|
||||||
|
|
||||||
# Update workbook.
|
# Update workbook.
|
||||||
wb_db = wb_service.update_workbook_v2(UPDATED_WORKBOOK)
|
wb_db = wb_service.update_workbook_v2(
|
||||||
|
UPDATED_WORKBOOK,
|
||||||
|
namespace=namespace
|
||||||
|
)
|
||||||
|
|
||||||
self.assertIsNotNone(wb_db)
|
self.assertIsNotNone(wb_db)
|
||||||
self.assertEqual('my_wb', wb_db.name)
|
self.assertEqual('my_wb', wb_db.name)
|
||||||
|
self.assertEqual(namespace, wb_db.namespace)
|
||||||
self.assertEqual(UPDATED_WORKBOOK, wb_db.definition)
|
self.assertEqual(UPDATED_WORKBOOK, wb_db.definition)
|
||||||
self.assertListEqual(['test'], wb_db.tags)
|
self.assertListEqual(['test'], wb_db.tags)
|
||||||
|
|
||||||
@ -240,6 +260,7 @@ class WorkbookServiceTest(base.DbTestCase):
|
|||||||
|
|
||||||
self.assertEqual('wf1', wf1_spec.get_name())
|
self.assertEqual('wf1', wf1_spec.get_name())
|
||||||
self.assertEqual('direct', wf1_spec.get_type())
|
self.assertEqual('direct', wf1_spec.get_type())
|
||||||
|
self.assertEqual(namespace, wf1_db.namespace)
|
||||||
self.assertEqual(UPDATED_WORKBOOK_WF1_DEFINITION, wf1_db.definition)
|
self.assertEqual(UPDATED_WORKBOOK_WF1_DEFINITION, wf1_db.definition)
|
||||||
|
|
||||||
# Workflow 2.
|
# Workflow 2.
|
||||||
@ -248,4 +269,26 @@ class WorkbookServiceTest(base.DbTestCase):
|
|||||||
|
|
||||||
self.assertEqual('wf2', wf2_spec.get_name())
|
self.assertEqual('wf2', wf2_spec.get_name())
|
||||||
self.assertEqual('reverse', wf2_spec.get_type())
|
self.assertEqual('reverse', wf2_spec.get_type())
|
||||||
|
self.assertEqual(namespace, wf2_db.namespace)
|
||||||
self.assertEqual(UPDATED_WORKBOOK_WF2_DEFINITION, wf2_db.definition)
|
self.assertEqual(UPDATED_WORKBOOK_WF2_DEFINITION, wf2_db.definition)
|
||||||
|
|
||||||
|
def test_delete_workbook(self):
|
||||||
|
namespace = 'pqr'
|
||||||
|
|
||||||
|
# Create workbook.
|
||||||
|
wb_service.create_workbook_v2(WORKBOOK, namespace=namespace)
|
||||||
|
|
||||||
|
db_wfs = db_api.get_workflow_definitions()
|
||||||
|
db_actions = db_api.get_action_definitions(name='my_wb.concat')
|
||||||
|
|
||||||
|
self.assertEqual(2, len(db_wfs))
|
||||||
|
self.assertEqual(1, len(db_actions))
|
||||||
|
|
||||||
|
db_api.delete_workbook('my_wb', namespace=namespace)
|
||||||
|
|
||||||
|
db_wfs = db_api.get_workflow_definitions()
|
||||||
|
db_actions = db_api.get_action_definitions(name='my_wb.concat')
|
||||||
|
|
||||||
|
# Deleting workbook shouldn't delete workflows and actions
|
||||||
|
self.assertEqual(2, len(db_wfs))
|
||||||
|
self.assertEqual(1, len(db_actions))
|
||||||
|
15
releasenotes/notes/namespace_for_workbooks.yaml
Normal file
15
releasenotes/notes/namespace_for_workbooks.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Add support for creating workbooks in a namespace. Creating workbooks
|
||||||
|
with same name is now possible inside the same project now. This feature
|
||||||
|
is backward compatible.
|
||||||
|
|
||||||
|
All existing workbooks are assumed to be in the default namespace,
|
||||||
|
represented by an empty string. Also, if a workbook is created without a
|
||||||
|
namespace specified, it is assumed to be in the default namespace.
|
||||||
|
|
||||||
|
When a workbook is created, its namespace is inherited by the
|
||||||
|
workflows contained within it. All operations on a particular workbook
|
||||||
|
require combination of name and namespace to uniquely identify a workbook
|
||||||
|
inside a project.
|
Loading…
Reference in New Issue
Block a user