From 2d4733f446dd4e62a554bfe637b1d595571842f8 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Sat, 25 Nov 2017 18:43:36 +0100 Subject: Fix: POST /project with an existing project name fails --- NEWS | 3 +++ ick2/__init__.py | 2 ++ ick2/apibase.py | 16 ++++++++++++---- ick2/exceptions.py | 6 ++++++ ick2/responses.py | 7 +++++++ yarns/100-projects.yarn | 9 +++++++++ 6 files changed, 39 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 91e2186..a6cec8f 100644 --- a/NEWS +++ b/NEWS @@ -23,6 +23,9 @@ Version 0.17+git, not yet released * Add subommand `icktool status` which shows current status of every pipeline in every project. +* `POST /projects` (or `icktool create-project`) with a project + description that names an already existing project fails. + Version 0.17, released 2017-11-19 ---------------------------------- diff --git a/ick2/__init__.py b/ick2/__init__.py index abbde96..13912fe 100644 --- a/ick2/__init__.py +++ b/ick2/__init__.py @@ -18,12 +18,14 @@ from .logging import setup_logging, log from .state import ControllerState, NotFound, WrongPipelineStatus from .exceptions import ( BadUpdate, + ExistsAlready, IckException, MethodNotAllowed, ) from .responses import ( OK, bad_request, + conflict, created, not_found, text_plain, diff --git a/ick2/apibase.py b/ick2/apibase.py index 7694b47..9795178 100644 --- a/ick2/apibase.py +++ b/ick2/apibase.py @@ -73,8 +73,11 @@ class APIbase: ick2.log.log( 'trace', msg_text='POST called', kwargs=kwargs, content_type=content_type, body=body) - body = callback(body) - ick2.log.log('trace', msg_text='returned body', body=repr(body)) + try: + body = callback(body) + except ick2.ExistsAlready as e: + ick2.log.log('error', msg_text=str(e)) + return ick2.conflict(str(e)) return ick2.created(body) return wrapper @@ -144,8 +147,13 @@ class ResourceApiBase(APIbase): return self._state.get_resource(self._type_name, name) def create(self, body, **kwargs): - return self._state.add_resource( - self._type_name, self.get_resource_name(body), body) + name = self.get_resource_name(body) + try: + self._state.get_resource(self._type_name, name) + except ick2.NotFound: + return self._state.add_resource(self._type_name, name, body) + else: + raise ick2.ExistsAlready(name) def get_resource_name(self, resource): # pragma: no cover raise NotImplementedError() diff --git a/ick2/exceptions.py b/ick2/exceptions.py index c99c3e5..124b207 100644 --- a/ick2/exceptions.py +++ b/ick2/exceptions.py @@ -18,6 +18,12 @@ class IckException(Exception): pass +class ExistsAlready(IckException): + + def __init__(self, name): + super().__init__('Resource {} already exists'.format(name)) + + class BadUpdate(IckException): def __init__(self, how): diff --git a/ick2/responses.py b/ick2/responses.py index ef68783..1ceae52 100644 --- a/ick2/responses.py +++ b/ick2/responses.py @@ -58,3 +58,10 @@ def created(body): # pragma: no cover 'Content-Type': 'application/json', } return response(apifw.HTTP_CREATED, body, headers) + + +def conflict(body): # pragma: no cover + headers = { + 'Content-Type': 'text/plain', + } + return response(apifw.HTTP_CONFLICT, body, headers) diff --git a/yarns/100-projects.yarn b/yarns/100-projects.yarn index 0aa57b0..de7f9c7 100644 --- a/yarns/100-projects.yarn +++ b/yarns/100-projects.yarn @@ -106,6 +106,15 @@ building them. We start by starting an instance of the controller. ... } AND controller state directory contains project website +Creating a new project with the same name is forbidden. + + WHEN user makes request POST /projects with a valid token and body + ... { + ... "project": "website", + ... "pipelines": [] + ... } + THEN result has status code 409 + WHEN user makes request GET /projects THEN result has status code 200 AND body matches -- cgit v1.2.1