diff --git a/doc/source/contributor/vitrage-api.rst b/doc/source/contributor/vitrage-api.rst index e46c9bc73..2443cbdbf 100644 --- a/doc/source/contributor/vitrage-api.rst +++ b/doc/source/contributor/vitrage-api.rst @@ -892,10 +892,10 @@ Response Examples Template Show ^^^^^^^^^^^^^ -Shows the template body for given template ID +Shows the template body for given template ID or Name -GET /v1/template/[template_uuid] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +GET /v1/template/[id] +~~~~~~~~~~~~~~~~~~~~~ Headers ======= @@ -907,7 +907,7 @@ Headers Path Parameters =============== -- template uuid +- template id or name Query Parameters ================ @@ -1126,7 +1126,7 @@ Headers Path Parameters =============== -template uuid +template id or name Query Parameters ================ diff --git a/releasenotes/notes/support_template_show_and_add_by_name-a73455f462175160.yaml b/releasenotes/notes/support_template_show_and_add_by_name-a73455f462175160.yaml new file mode 100644 index 000000000..b68dfd70c --- /dev/null +++ b/releasenotes/notes/support_template_show_and_add_by_name-a73455f462175160.yaml @@ -0,0 +1,3 @@ +--- +features: + - Added support to show and delete template by name. diff --git a/vitrage/api/controllers/v1/template.py b/vitrage/api/controllers/v1/template.py index bcd251b2a..b48fc9d33 100644 --- a/vitrage/api/controllers/v1/template.py +++ b/vitrage/api/controllers/v1/template.py @@ -12,10 +12,12 @@ # License for the specific language governing permissions and limitations # under the License. import json + import pecan from pytz import utc from oslo_log import log +from oslo_utils.uuidutils import is_uuid_like from osprofiler import profiler from pecan.core import abort @@ -27,7 +29,6 @@ from vitrage.common.exception import VitrageError LOG = log.getLogger(__name__) -# noinspection PyBroadException @profiler.trace_cls("template controller", info={}, hide_args=False, trace_private=False) class TemplateController(RootRestController): @@ -48,7 +49,7 @@ class TemplateController(RootRestController): abort(404, 'Failed to get template list') @pecan.expose('json') - def get(self, template_uuid): + def get(self, _id): LOG.info('get template content') @@ -58,23 +59,25 @@ class TemplateController(RootRestController): {}) try: - return self._show_template(template_uuid) + return self._show_template(_id) except Exception: - LOG.exception('Failed to show template %s.', - template_uuid) + LOG.exception('Failed to show template %s.', _id) abort(404, 'Failed to show template.') @pecan.expose('json') def delete(self, **kwargs): - uuid = kwargs['uuid'] - LOG.info("delete template. uuid: %s", uuid) + # for backward computability + values = kwargs['uuid'] if 'uuid'in kwargs else kwargs['id'] + LOG.info("delete template. values: %s", values) + uuids = self._to_uuids(values) + LOG.info("delete template. uuids: %s", uuids) enforce("template delete", pecan.request.headers, pecan.request.enforcer, {}) try: - return self._delete(uuid) + return self._delete(uuids) except Exception: LOG.exception('Failed to delete template.') abort(404, 'Failed to delete template.') @@ -126,14 +129,15 @@ class TemplateController(RootRestController): if template.updated_at: template.updated_at = utc.localize(template.updated_at) templates = [t for t in templates if t.status != TStatus.DELETED] - templates.sort(key=lambda template: template.created_at) + templates.sort(key=lambda templ: templ.created_at) return [cls._db_template_to_dict(t) for t in templates] except Exception: LOG.exception('Failed to get template list.') abort(404, 'Failed to get template list.') @staticmethod - def _show_template(uuid): + def _show_template(_id): + uuid = TemplateController._to_uuid(_id) try: templates = pecan.request.storage.templates.query(uuid=uuid) if not templates or templates[0].status == TStatus.DELETED: @@ -184,13 +188,41 @@ class TemplateController(RootRestController): } @staticmethod - def _delete(uuid): + def _delete(uuids): try: results = pecan.request.client.call( pecan.request.context, 'delete_template', - uuids=uuid) + uuids=uuids) return results except Exception: LOG.exception('Failed to delete template.') abort(404, 'Failed to delete template.') + + def _to_uuids(self, values): + # if it is a single string + # make sure I don't iterate + # on the characters + if type(values) is not list: + values = [values] + + return [self._to_uuid(val) for val in values] + + @staticmethod + def _to_uuid(val): + if is_uuid(val): + return val + template = pecan.request.storage.templates.query_with_status_not( + status=TStatus.DELETED, name=val) + if template: + return template.uuid + return val + + +def is_uuid(val): + # unwrap the name or id + # from the list if in a list + # [value] --> value + if type(val) is list: + val, = val + return is_uuid_like(val) diff --git a/vitrage/storage/impl_sqlalchemy.py b/vitrage/storage/impl_sqlalchemy.py index 95167ba87..73782fa43 100644 --- a/vitrage/storage/impl_sqlalchemy.py +++ b/vitrage/storage/impl_sqlalchemy.py @@ -191,6 +191,18 @@ class TemplatesConnection(base.TemplatesConnection, BaseTableConn): ) return query.all() + def query_with_status_not(self, name, status): + session = self._engine_facade.get_session() + query = session.query(models.Template) + query = query.filter( + and_ + ( + models.Template.status != status, + models.Template.name == name + ) + ) + return query.first() + def delete(self, name=None, uuid=None): query = self.query_filter( models.Template, diff --git a/vitrage/tests/functional/api/v1/test_noauth.py b/vitrage/tests/functional/api/v1/test_noauth.py index 69f5a2248..f94c570bb 100755 --- a/vitrage/tests/functional/api/v1/test_noauth.py +++ b/vitrage/tests/functional/api/v1/test_noauth.py @@ -129,6 +129,8 @@ class NoAuthTest(FunctionalTest): data = self.get_json('/template/1234') self.assertEqual(1, request.storage.templates.query.call_count) + self.assertEqual( + 1, request.storage.templates.query_with_status_not.call_count) self.assert_is_empty(data) def test_noauth_mode_validate_template(self):