From 53c1b4c895f6f6e1300ddd277bf79b8d1047e8ea Mon Sep 17 00:00:00 2001
From: Martin Weise <martin.weise@tuwien.ac.at>
Date: Thu, 28 Jul 2022 13:12:31 +0200
Subject: [PATCH] FIxed permission errors for modifying tuples

---
 .jupyter/api_authentication/__init__.py       |   5 +-
 .../api_authentication/models/__init__.py     |   5 +-
 .../api_authentication/models/database_dto.py |   6 +-
 .jupyter/api_container/__init__.py            |   4 +-
 .jupyter/api_container/models/__init__.py     |   4 +-
 .jupyter/api_container/models/database_dto.py |   6 +-
 .jupyter/api_database/__init__.py             |   4 +-
 .jupyter/api_database/models/__init__.py      |   4 +-
 .jupyter/api_database/models/database_dto.py  |   6 +-
 .jupyter/api_identifier/__init__.py           |   5 +-
 .jupyter/api_identifier/models/__init__.py    |   5 +-
 .jupyter/api_query/__init__.py                |   6 +-
 .jupyter/api_query/api/query_endpoint_api.py  |   2 +-
 .../api_query/api/table_data_endpoint_api.py  | 121 +++++++++++
 .jupyter/api_query/models/__init__.py         |   6 +-
 .jupyter/api_query/models/database_dto.py     |   6 +-
 .jupyter/load_test.py                         |  29 +++
 .jupyter/requirements.txt                     |   3 +-
 .../at/tuwien/api/database/DatabaseDto.java   |   6 +-
 .../tuwien/api/database/table/TableDto.java   |   8 +-
 .../at/tuwien/endpoint/AbstractEndpoint.java  |  21 +-
 .../src/main/resources/application-docker.yml |   2 +-
 .../java/at/tuwien/mapper/QueryMapper.java    |   3 +-
 .../at/tuwien/endpoints/TableEndpoint.java    |   6 +-
 .../java/at/tuwien/mapper/TableMapper.java    |   4 -
 fda-ui/components/TableList.vue               |  24 ++-
 fda-ui/components/dialogs/ColumnUnit.vue      | 204 ++++++++++--------
 .../_database_id/table/_table_id/index.vue    |  33 ++-
 fda-ui/pages/container/index.vue              |   4 +-
 fda-units-service/app.py                      |   4 +-
 .../us-yml/savecolumnsconcept.yml             |   5 +
 fda-units-service/us-yml/saveconcept.yml      |   6 +-
 32 files changed, 385 insertions(+), 172 deletions(-)

diff --git a/.jupyter/api_authentication/__init__.py b/.jupyter/api_authentication/__init__.py
index 1caebb3b03..c20f434f5e 100644
--- a/.jupyter/api_authentication/__init__.py
+++ b/.jupyter/api_authentication/__init__.py
@@ -23,8 +23,6 @@ from api_authentication.api_client import ApiClient
 from api_authentication.configuration import Configuration
 # import models into sdk package
 from api_authentication.models.api_error_dto import ApiErrorDto
-from api_authentication.models.column_dto import ColumnDto
-from api_authentication.models.concept_dto import ConceptDto
 from api_authentication.models.container_dto import ContainerDto
 from api_authentication.models.database_dto import DatabaseDto
 from api_authentication.models.granted_authority_dto import GrantedAuthorityDto
@@ -36,7 +34,8 @@ from api_authentication.models.jwt_response_dto import JwtResponseDto
 from api_authentication.models.license_dto import LicenseDto
 from api_authentication.models.login_request_dto import LoginRequestDto
 from api_authentication.models.signup_request_dto import SignupRequestDto
-from api_authentication.models.table_dto import TableDto
+from api_authentication.models.table_brief_dto import TableBriefDto
+from api_authentication.models.user_brief_dto import UserBriefDto
 from api_authentication.models.user_dto import UserDto
 from api_authentication.models.user_email_dto import UserEmailDto
 from api_authentication.models.user_forgot_dto import UserForgotDto
diff --git a/.jupyter/api_authentication/models/__init__.py b/.jupyter/api_authentication/models/__init__.py
index 0cd55935b6..fc192aa3f4 100644
--- a/.jupyter/api_authentication/models/__init__.py
+++ b/.jupyter/api_authentication/models/__init__.py
@@ -15,8 +15,6 @@ from __future__ import absolute_import
 
 # import models into model package
 from api_authentication.models.api_error_dto import ApiErrorDto
-from api_authentication.models.column_dto import ColumnDto
-from api_authentication.models.concept_dto import ConceptDto
 from api_authentication.models.container_dto import ContainerDto
 from api_authentication.models.database_dto import DatabaseDto
 from api_authentication.models.granted_authority_dto import GrantedAuthorityDto
@@ -28,7 +26,8 @@ from api_authentication.models.jwt_response_dto import JwtResponseDto
 from api_authentication.models.license_dto import LicenseDto
 from api_authentication.models.login_request_dto import LoginRequestDto
 from api_authentication.models.signup_request_dto import SignupRequestDto
-from api_authentication.models.table_dto import TableDto
+from api_authentication.models.table_brief_dto import TableBriefDto
+from api_authentication.models.user_brief_dto import UserBriefDto
 from api_authentication.models.user_dto import UserDto
 from api_authentication.models.user_email_dto import UserEmailDto
 from api_authentication.models.user_forgot_dto import UserForgotDto
diff --git a/.jupyter/api_authentication/models/database_dto.py b/.jupyter/api_authentication/models/database_dto.py
index c5ef5bef66..3ff298f11a 100644
--- a/.jupyter/api_authentication/models/database_dto.py
+++ b/.jupyter/api_authentication/models/database_dto.py
@@ -38,7 +38,7 @@ class DatabaseDto(object):
         'description': 'str',
         'publisher': 'str',
         'contact': 'UserDto',
-        'tables': 'list[TableDto]',
+        'tables': 'list[TableBriefDto]',
         'image': 'ImageDto',
         'container': 'ContainerDto',
         'created': 'datetime',
@@ -352,7 +352,7 @@ class DatabaseDto(object):
 
 
         :return: The tables of this DatabaseDto.  # noqa: E501
-        :rtype: list[TableDto]
+        :rtype: list[TableBriefDto]
         """
         return self._tables
 
@@ -362,7 +362,7 @@ class DatabaseDto(object):
 
 
         :param tables: The tables of this DatabaseDto.  # noqa: E501
-        :type: list[TableDto]
+        :type: list[TableBriefDto]
         """
 
         self._tables = tables
diff --git a/.jupyter/api_container/__init__.py b/.jupyter/api_container/__init__.py
index d659282125..e8c65699a9 100644
--- a/.jupyter/api_container/__init__.py
+++ b/.jupyter/api_container/__init__.py
@@ -22,8 +22,6 @@ from api_container.api_client import ApiClient
 from api_container.configuration import Configuration
 # import models into sdk package
 from api_container.models.api_error_dto import ApiErrorDto
-from api_container.models.column_dto import ColumnDto
-from api_container.models.concept_dto import ConceptDto
 from api_container.models.container_brief_dto import ContainerBriefDto
 from api_container.models.container_change_dto import ContainerChangeDto
 from api_container.models.container_create_request_dto import ContainerCreateRequestDto
@@ -37,6 +35,6 @@ from api_container.models.image_date_dto import ImageDateDto
 from api_container.models.image_dto import ImageDto
 from api_container.models.image_env_item_dto import ImageEnvItemDto
 from api_container.models.license_dto import LicenseDto
-from api_container.models.table_dto import TableDto
+from api_container.models.table_brief_dto import TableBriefDto
 from api_container.models.user_brief_dto import UserBriefDto
 from api_container.models.user_dto import UserDto
diff --git a/.jupyter/api_container/models/__init__.py b/.jupyter/api_container/models/__init__.py
index 9f7e2ea748..563386400d 100644
--- a/.jupyter/api_container/models/__init__.py
+++ b/.jupyter/api_container/models/__init__.py
@@ -15,8 +15,6 @@ from __future__ import absolute_import
 
 # import models into model package
 from api_container.models.api_error_dto import ApiErrorDto
-from api_container.models.column_dto import ColumnDto
-from api_container.models.concept_dto import ConceptDto
 from api_container.models.container_brief_dto import ContainerBriefDto
 from api_container.models.container_change_dto import ContainerChangeDto
 from api_container.models.container_create_request_dto import ContainerCreateRequestDto
@@ -30,6 +28,6 @@ from api_container.models.image_date_dto import ImageDateDto
 from api_container.models.image_dto import ImageDto
 from api_container.models.image_env_item_dto import ImageEnvItemDto
 from api_container.models.license_dto import LicenseDto
-from api_container.models.table_dto import TableDto
+from api_container.models.table_brief_dto import TableBriefDto
 from api_container.models.user_brief_dto import UserBriefDto
 from api_container.models.user_dto import UserDto
diff --git a/.jupyter/api_container/models/database_dto.py b/.jupyter/api_container/models/database_dto.py
index 20dd3f3b56..408803f482 100644
--- a/.jupyter/api_container/models/database_dto.py
+++ b/.jupyter/api_container/models/database_dto.py
@@ -38,7 +38,7 @@ class DatabaseDto(object):
         'description': 'str',
         'publisher': 'str',
         'contact': 'UserDto',
-        'tables': 'list[TableDto]',
+        'tables': 'list[TableBriefDto]',
         'image': 'ImageDto',
         'container': 'ContainerDto',
         'created': 'datetime',
@@ -352,7 +352,7 @@ class DatabaseDto(object):
 
 
         :return: The tables of this DatabaseDto.  # noqa: E501
-        :rtype: list[TableDto]
+        :rtype: list[TableBriefDto]
         """
         return self._tables
 
@@ -362,7 +362,7 @@ class DatabaseDto(object):
 
 
         :param tables: The tables of this DatabaseDto.  # noqa: E501
-        :type: list[TableDto]
+        :type: list[TableBriefDto]
         """
 
         self._tables = tables
diff --git a/.jupyter/api_database/__init__.py b/.jupyter/api_database/__init__.py
index 8c1403ffcf..5da8bf97a4 100644
--- a/.jupyter/api_database/__init__.py
+++ b/.jupyter/api_database/__init__.py
@@ -22,8 +22,6 @@ from api_database.api_client import ApiClient
 from api_database.configuration import Configuration
 # import models into sdk package
 from api_database.models.api_error_dto import ApiErrorDto
-from api_database.models.column_dto import ColumnDto
-from api_database.models.concept_dto import ConceptDto
 from api_database.models.container_brief_dto import ContainerBriefDto
 from api_database.models.container_dto import ContainerDto
 from api_database.models.database_brief_dto import DatabaseBriefDto
@@ -36,6 +34,6 @@ from api_database.models.image_date_dto import ImageDateDto
 from api_database.models.image_dto import ImageDto
 from api_database.models.image_env_item_dto import ImageEnvItemDto
 from api_database.models.license_dto import LicenseDto
-from api_database.models.table_dto import TableDto
+from api_database.models.table_brief_dto import TableBriefDto
 from api_database.models.user_brief_dto import UserBriefDto
 from api_database.models.user_dto import UserDto
diff --git a/.jupyter/api_database/models/__init__.py b/.jupyter/api_database/models/__init__.py
index 918bf85903..17ab89a69b 100644
--- a/.jupyter/api_database/models/__init__.py
+++ b/.jupyter/api_database/models/__init__.py
@@ -15,8 +15,6 @@ from __future__ import absolute_import
 
 # import models into model package
 from api_database.models.api_error_dto import ApiErrorDto
-from api_database.models.column_dto import ColumnDto
-from api_database.models.concept_dto import ConceptDto
 from api_database.models.container_brief_dto import ContainerBriefDto
 from api_database.models.container_dto import ContainerDto
 from api_database.models.database_brief_dto import DatabaseBriefDto
@@ -29,6 +27,6 @@ from api_database.models.image_date_dto import ImageDateDto
 from api_database.models.image_dto import ImageDto
 from api_database.models.image_env_item_dto import ImageEnvItemDto
 from api_database.models.license_dto import LicenseDto
-from api_database.models.table_dto import TableDto
+from api_database.models.table_brief_dto import TableBriefDto
 from api_database.models.user_brief_dto import UserBriefDto
 from api_database.models.user_dto import UserDto
diff --git a/.jupyter/api_database/models/database_dto.py b/.jupyter/api_database/models/database_dto.py
index eb17e89e25..6e1826899a 100644
--- a/.jupyter/api_database/models/database_dto.py
+++ b/.jupyter/api_database/models/database_dto.py
@@ -38,7 +38,7 @@ class DatabaseDto(object):
         'description': 'str',
         'publisher': 'str',
         'contact': 'UserDto',
-        'tables': 'list[TableDto]',
+        'tables': 'list[TableBriefDto]',
         'image': 'ImageDto',
         'container': 'ContainerDto',
         'created': 'datetime',
@@ -352,7 +352,7 @@ class DatabaseDto(object):
 
 
         :return: The tables of this DatabaseDto.  # noqa: E501
-        :rtype: list[TableDto]
+        :rtype: list[TableBriefDto]
         """
         return self._tables
 
@@ -362,7 +362,7 @@ class DatabaseDto(object):
 
 
         :param tables: The tables of this DatabaseDto.  # noqa: E501
-        :type: list[TableDto]
+        :type: list[TableBriefDto]
         """
 
         self._tables = tables
diff --git a/.jupyter/api_identifier/__init__.py b/.jupyter/api_identifier/__init__.py
index 588a1b3d2e..004d1c92b4 100644
--- a/.jupyter/api_identifier/__init__.py
+++ b/.jupyter/api_identifier/__init__.py
@@ -22,8 +22,6 @@ from api_identifier.api_client import ApiClient
 from api_identifier.configuration import Configuration
 # import models into sdk package
 from api_identifier.models.api_error_dto import ApiErrorDto
-from api_identifier.models.column_dto import ColumnDto
-from api_identifier.models.concept_dto import ConceptDto
 from api_identifier.models.container_dto import ContainerDto
 from api_identifier.models.creator_create_dto import CreatorCreateDto
 from api_identifier.models.creator_dto import CreatorDto
@@ -38,5 +36,6 @@ from api_identifier.models.image_env_item_dto import ImageEnvItemDto
 from api_identifier.models.license_dto import LicenseDto
 from api_identifier.models.related_identifier_create_dto import RelatedIdentifierCreateDto
 from api_identifier.models.related_identifier_dto import RelatedIdentifierDto
-from api_identifier.models.table_dto import TableDto
+from api_identifier.models.table_brief_dto import TableBriefDto
+from api_identifier.models.user_brief_dto import UserBriefDto
 from api_identifier.models.user_dto import UserDto
diff --git a/.jupyter/api_identifier/models/__init__.py b/.jupyter/api_identifier/models/__init__.py
index e39aeeecc9..10de3f2d88 100644
--- a/.jupyter/api_identifier/models/__init__.py
+++ b/.jupyter/api_identifier/models/__init__.py
@@ -15,8 +15,6 @@ from __future__ import absolute_import
 
 # import models into model package
 from api_identifier.models.api_error_dto import ApiErrorDto
-from api_identifier.models.column_dto import ColumnDto
-from api_identifier.models.concept_dto import ConceptDto
 from api_identifier.models.container_dto import ContainerDto
 from api_identifier.models.creator_create_dto import CreatorCreateDto
 from api_identifier.models.creator_dto import CreatorDto
@@ -31,5 +29,6 @@ from api_identifier.models.image_env_item_dto import ImageEnvItemDto
 from api_identifier.models.license_dto import LicenseDto
 from api_identifier.models.related_identifier_create_dto import RelatedIdentifierCreateDto
 from api_identifier.models.related_identifier_dto import RelatedIdentifierDto
-from api_identifier.models.table_dto import TableDto
+from api_identifier.models.table_brief_dto import TableBriefDto
+from api_identifier.models.user_brief_dto import UserBriefDto
 from api_identifier.models.user_dto import UserDto
diff --git a/.jupyter/api_query/__init__.py b/.jupyter/api_query/__init__.py
index 125c6c436b..b0dc0ccd7f 100644
--- a/.jupyter/api_query/__init__.py
+++ b/.jupyter/api_query/__init__.py
@@ -25,8 +25,6 @@ from api_query.api_client import ApiClient
 from api_query.configuration import Configuration
 # import models into sdk package
 from api_query.models.api_error_dto import ApiErrorDto
-from api_query.models.column_dto import ColumnDto
-from api_query.models.concept_dto import ConceptDto
 from api_query.models.container_dto import ContainerDto
 from api_query.models.database_dto import DatabaseDto
 from api_query.models.execute_statement_dto import ExecuteStatementDto
@@ -39,8 +37,10 @@ from api_query.models.import_dto import ImportDto
 from api_query.models.license_dto import LicenseDto
 from api_query.models.query_dto import QueryDto
 from api_query.models.query_result_dto import QueryResultDto
+from api_query.models.table_brief_dto import TableBriefDto
 from api_query.models.table_csv_delete_dto import TableCsvDeleteDto
 from api_query.models.table_csv_dto import TableCsvDto
-from api_query.models.table_dto import TableDto
+from api_query.models.table_csv_update_dto import TableCsvUpdateDto
 from api_query.models.table_history_dto import TableHistoryDto
+from api_query.models.user_brief_dto import UserBriefDto
 from api_query.models.user_dto import UserDto
diff --git a/.jupyter/api_query/api/query_endpoint_api.py b/.jupyter/api_query/api/query_endpoint_api.py
index 74bda079d9..158773461b 100644
--- a/.jupyter/api_query/api/query_endpoint_api.py
+++ b/.jupyter/api_query/api/query_endpoint_api.py
@@ -361,7 +361,7 @@ class QueryEndpointApi(object):
             ['*/*'])  # noqa: E501
 
         # Authentication setting
-        auth_settings = []  # noqa: E501
+        auth_settings = ['bearerAuth']  # noqa: E501
 
         return self.api_client.call_api(
             '/api/container/{id}/database/{databaseId}/query/{queryId}', 'PUT',
diff --git a/.jupyter/api_query/api/table_data_endpoint_api.py b/.jupyter/api_query/api/table_data_endpoint_api.py
index 31b90e9df3..632bf0023c 100644
--- a/.jupyter/api_query/api/table_data_endpoint_api.py
+++ b/.jupyter/api_query/api/table_data_endpoint_api.py
@@ -636,3 +636,124 @@ class TableDataEndpointApi(object):
             _preload_content=params.get('_preload_content', True),
             _request_timeout=params.get('_request_timeout'),
             collection_formats=collection_formats)
+
+    def update(self, body, id, database_id, table_id, **kwargs):  # noqa: E501
+        """Update data  # noqa: E501
+
+        This method makes a synchronous HTTP request by default. To make an
+        asynchronous HTTP request, please pass async_req=True
+        >>> thread = api.update(body, id, database_id, table_id, async_req=True)
+        >>> result = thread.get()
+
+        :param async_req bool
+        :param TableCsvUpdateDto body: (required)
+        :param int id: (required)
+        :param int database_id: (required)
+        :param int table_id: (required)
+        :return: None
+                 If the method is called asynchronously,
+                 returns the request thread.
+        """
+        kwargs['_return_http_data_only'] = True
+        if kwargs.get('async_req'):
+            return self.update_with_http_info(body, id, database_id, table_id, **kwargs)  # noqa: E501
+        else:
+            (data) = self.update_with_http_info(body, id, database_id, table_id, **kwargs)  # noqa: E501
+            return data
+
+    def update_with_http_info(self, body, id, database_id, table_id, **kwargs):  # noqa: E501
+        """Update data  # noqa: E501
+
+        This method makes a synchronous HTTP request by default. To make an
+        asynchronous HTTP request, please pass async_req=True
+        >>> thread = api.update_with_http_info(body, id, database_id, table_id, async_req=True)
+        >>> result = thread.get()
+
+        :param async_req bool
+        :param TableCsvUpdateDto body: (required)
+        :param int id: (required)
+        :param int database_id: (required)
+        :param int table_id: (required)
+        :return: None
+                 If the method is called asynchronously,
+                 returns the request thread.
+        """
+
+        all_params = ['body', 'id', 'database_id', 'table_id']  # noqa: E501
+        all_params.append('async_req')
+        all_params.append('_return_http_data_only')
+        all_params.append('_preload_content')
+        all_params.append('_request_timeout')
+
+        params = locals()
+        for key, val in six.iteritems(params['kwargs']):
+            if key not in all_params:
+                raise TypeError(
+                    "Got an unexpected keyword argument '%s'"
+                    " to method update" % key
+                )
+            params[key] = val
+        del params['kwargs']
+        # verify the required parameter 'body' is set
+        if ('body' not in params or
+                params['body'] is None):
+            raise ValueError("Missing the required parameter `body` when calling `update`")  # noqa: E501
+        # verify the required parameter 'id' is set
+        if ('id' not in params or
+                params['id'] is None):
+            raise ValueError("Missing the required parameter `id` when calling `update`")  # noqa: E501
+        # verify the required parameter 'database_id' is set
+        if ('database_id' not in params or
+                params['database_id'] is None):
+            raise ValueError("Missing the required parameter `database_id` when calling `update`")  # noqa: E501
+        # verify the required parameter 'table_id' is set
+        if ('table_id' not in params or
+                params['table_id'] is None):
+            raise ValueError("Missing the required parameter `table_id` when calling `update`")  # noqa: E501
+
+        collection_formats = {}
+
+        path_params = {}
+        if 'id' in params:
+            path_params['id'] = params['id']  # noqa: E501
+        if 'database_id' in params:
+            path_params['databaseId'] = params['database_id']  # noqa: E501
+        if 'table_id' in params:
+            path_params['tableId'] = params['table_id']  # noqa: E501
+
+        query_params = []
+
+        header_params = {}
+
+        form_params = []
+        local_var_files = {}
+
+        body_params = None
+        if 'body' in params:
+            body_params = params['body']
+        # HTTP header `Accept`
+        header_params['Accept'] = self.api_client.select_header_accept(
+            ['*/*'])  # noqa: E501
+
+        # HTTP header `Content-Type`
+        header_params['Content-Type'] = self.api_client.select_header_content_type(  # noqa: E501
+            ['application/json'])  # noqa: E501
+
+        # Authentication setting
+        auth_settings = ['bearerAuth']  # noqa: E501
+
+        return self.api_client.call_api(
+            '/api/container/{id}/database/{databaseId}/table/{tableId}/data', 'PUT',
+            path_params,
+            query_params,
+            header_params,
+            body=body_params,
+            post_params=form_params,
+            files=local_var_files,
+            response_type=None,  # noqa: E501
+            auth_settings=auth_settings,
+            async_req=params.get('async_req'),
+            _return_http_data_only=params.get('_return_http_data_only'),
+            _preload_content=params.get('_preload_content', True),
+            _request_timeout=params.get('_request_timeout'),
+            collection_formats=collection_formats)
diff --git a/.jupyter/api_query/models/__init__.py b/.jupyter/api_query/models/__init__.py
index 2ce6fd3875..947ce39c5d 100644
--- a/.jupyter/api_query/models/__init__.py
+++ b/.jupyter/api_query/models/__init__.py
@@ -15,8 +15,6 @@ from __future__ import absolute_import
 
 # import models into model package
 from api_query.models.api_error_dto import ApiErrorDto
-from api_query.models.column_dto import ColumnDto
-from api_query.models.concept_dto import ConceptDto
 from api_query.models.container_dto import ContainerDto
 from api_query.models.database_dto import DatabaseDto
 from api_query.models.execute_statement_dto import ExecuteStatementDto
@@ -29,8 +27,10 @@ from api_query.models.import_dto import ImportDto
 from api_query.models.license_dto import LicenseDto
 from api_query.models.query_dto import QueryDto
 from api_query.models.query_result_dto import QueryResultDto
+from api_query.models.table_brief_dto import TableBriefDto
 from api_query.models.table_csv_delete_dto import TableCsvDeleteDto
 from api_query.models.table_csv_dto import TableCsvDto
-from api_query.models.table_dto import TableDto
+from api_query.models.table_csv_update_dto import TableCsvUpdateDto
 from api_query.models.table_history_dto import TableHistoryDto
+from api_query.models.user_brief_dto import UserBriefDto
 from api_query.models.user_dto import UserDto
diff --git a/.jupyter/api_query/models/database_dto.py b/.jupyter/api_query/models/database_dto.py
index b1dbaea6d8..6cb99a2415 100644
--- a/.jupyter/api_query/models/database_dto.py
+++ b/.jupyter/api_query/models/database_dto.py
@@ -38,7 +38,7 @@ class DatabaseDto(object):
         'description': 'str',
         'publisher': 'str',
         'contact': 'UserDto',
-        'tables': 'list[TableDto]',
+        'tables': 'list[TableBriefDto]',
         'image': 'ImageDto',
         'container': 'ContainerDto',
         'created': 'datetime',
@@ -352,7 +352,7 @@ class DatabaseDto(object):
 
 
         :return: The tables of this DatabaseDto.  # noqa: E501
-        :rtype: list[TableDto]
+        :rtype: list[TableBriefDto]
         """
         return self._tables
 
@@ -362,7 +362,7 @@ class DatabaseDto(object):
 
 
         :param tables: The tables of this DatabaseDto.  # noqa: E501
-        :type: list[TableDto]
+        :type: list[TableBriefDto]
         """
 
         self._tables = tables
diff --git a/.jupyter/load_test.py b/.jupyter/load_test.py
index 688fa6dd8b..bdf47dee1d 100644
--- a/.jupyter/load_test.py
+++ b/.jupyter/load_test.py
@@ -4,6 +4,7 @@ import time
 import os
 import shutil
 import uuid
+from postgres import Postgres
 
 import api_query.rest
 from api_authentication.api.authentication_endpoint_api import AuthenticationEndpointApi
@@ -219,6 +220,27 @@ def delete_tuple(container_id, database_id, table_id, keys):
     return response
 
 
+def update_user(user_id):
+    response = user.update({
+        "firstname": "Josiah",
+        "lastname": "Carberry",
+        "affiliation": "Wesleyan University",
+        "orcid": "0000-0002-1825-0097",
+        "titles_after": "PhD"
+    }, user_id)
+    print("updated user with id %d" % user_id)
+
+
+def update_theme(user_id):
+    response = user.update_theme({
+        "theme_dark": True
+    }, user_id)
+    print("updated theme user with id %d" % user_id)
+
+def verify_user(user_id):
+    db = Postgres("dbname=fda user=postgres password=postgres")
+    token = db.one("SELECT ")
+
 if __name__ == '__main__':
     #
     # create 1 user and 3 containers (public, private, public)
@@ -310,3 +332,10 @@ if __name__ == '__main__':
     tname = find_table(1, 1, 1).internal_name
     qid = create_query(1, 1, "select `id` from `" + tname + "`").id
     create_identifier(1, 1, qid)
+    #
+    # create 1 user and modify information
+    #
+    uid = create_user("test3").id
+    auth_user("test3")
+    update_user(uid)
+    update_theme(uid)
diff --git a/.jupyter/requirements.txt b/.jupyter/requirements.txt
index 2a252d8604..1ea469ff4a 100644
--- a/.jupyter/requirements.txt
+++ b/.jupyter/requirements.txt
@@ -1,2 +1,3 @@
 requests>=2.28.0
-pandas>=1.4.3
\ No newline at end of file
+pandas>=1.4.3
+postgres>=4.0
\ No newline at end of file
diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/database/DatabaseDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/database/DatabaseDto.java
index 5da1350e8f..7a3b8c47c0 100644
--- a/fda-metadata-db/api/src/main/java/at/tuwien/api/database/DatabaseDto.java
+++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/database/DatabaseDto.java
@@ -2,8 +2,10 @@ package at.tuwien.api.database;
 
 import at.tuwien.api.container.ContainerDto;
 import at.tuwien.api.container.image.ImageDto;
+import at.tuwien.api.database.table.TableBriefDto;
 import at.tuwien.api.database.table.TableDto;
 import at.tuwien.api.identifier.CreatorDto;
+import at.tuwien.api.user.UserBriefDto;
 import at.tuwien.api.user.UserDto;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -37,7 +39,7 @@ public class DatabaseDto {
 
     @NotNull
     @Parameter(name = "database creator")
-    private UserDto creator;
+    private UserBriefDto creator;
 
     @NotBlank
     @JsonProperty("internal_name")
@@ -67,7 +69,7 @@ public class DatabaseDto {
     private String publication;
 
     @Parameter(name = "tables")
-    private List<TableDto> tables;
+    private List<TableBriefDto> tables;
 
     @JsonProperty("is_public")
     @Parameter(name = "database public")
diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/database/table/TableDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/database/table/TableDto.java
index b739f660d4..07c3205f3c 100644
--- a/fda-metadata-db/api/src/main/java/at/tuwien/api/database/table/TableDto.java
+++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/database/table/TableDto.java
@@ -1,6 +1,7 @@
 package at.tuwien.api.database.table;
 
 import at.tuwien.api.database.table.columns.ColumnDto;
+import at.tuwien.api.user.UserBriefDto;
 import at.tuwien.api.user.UserDto;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -10,6 +11,7 @@ import lombok.*;
 import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
 import java.time.Instant;
+import java.util.List;
 
 @Getter
 @Setter
@@ -32,6 +34,10 @@ public class TableDto {
     @Parameter(name = "table internal name", example = "weather_australia")
     private String internalName;
 
+    @NotNull
+    @Parameter(name = "database creator")
+    private UserBriefDto creator;
+
     @NotBlank
     @Parameter(name = "topic name", example = "fda.c1.d1.t1")
     private String topic;
@@ -46,6 +52,6 @@ public class TableDto {
 
     @NotNull
     @Parameter(name = "table columns")
-    private ColumnDto[] columns;
+    private List<ColumnDto> columns;
 
 }
diff --git a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/AbstractEndpoint.java b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/AbstractEndpoint.java
index d065595722..fc367c723e 100644
--- a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/AbstractEndpoint.java
+++ b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/AbstractEndpoint.java
@@ -36,28 +36,29 @@ public abstract class AbstractEndpoint {
             log.debug("failed to find database with id {}", databaseId);
             return false;
         }
-        if (principal != null && database.getCreator().getUsername().equals(principal.getName())) {
-            log.debug("grant permission {} because user is creator of database with id {}", permissionCode, databaseId);
-            return true;
-        }
         /* view-only operations are allowed on public databases */
         if (database.getIsPublic() && List.of("DATA_EXPORT", "DATA_VIEW", "DATA_HISTORY", "QUERY_VIEW_ALL").contains(permissionCode)) {
             log.debug("grant permission {} because database is public", permissionCode);
             return true;
         }
-        /* modification operations are limited to the creator */
         if (principal == null) {
             log.debug("failed to grant permission {} because principal is null", permissionCode);
             return false;
         }
+        /* modification operations are limited to the creator */
+        if (database.getCreator().getUsername().equals(principal.getName())) {
+            log.debug("grant permission {} because user {} is creator {}", permissionCode, principal.getName(),
+                    database.getCreator().getUsername());
+            return true;
+        }
         final Authentication authentication = (Authentication) principal /* with pre-authorization this always holds */;
         if (authentication.getAuthorities().stream().noneMatch(a -> a.getAuthority().equals("ROLE_RESEARCHER"))) {
             log.debug("failed to grant permission {} because current user misses authority 'ROLE_RESEARCHER'",
                     permissionCode);
             return false;
         }
-        log.debug("grant permission {} because user is creator", permissionCode);
-        return true;
+        log.debug("failed to grant permission {} because database is not owner by the current user", permissionCode);
+        return false;
     }
 
     protected Boolean hasQueryPermission(Long containerId, Long databaseId, Long queryId, String permissionCode, Principal principal) {
@@ -97,8 +98,7 @@ public abstract class AbstractEndpoint {
                     permissionCode);
             return true;
         }
-        log.debug("failed to grant permission {} because database is private and creator is not the " +
-                "current user", permissionCode);
+        log.debug("failed to grant permission {} because database is private and creator is not the current user", permissionCode);
         return false;
     }
 
@@ -138,8 +138,7 @@ public abstract class AbstractEndpoint {
                     permissionCode);
             return true;
         }
-        log.debug("failed to grant permission {} because database is private and identifier creator is not the " +
-                "current user", permissionCode);
+        log.debug("failed to grant permission {} because database is private and identifier creator is not the current user", permissionCode);
         return false;
     }
 
diff --git a/fda-query-service/rest-service/src/main/resources/application-docker.yml b/fda-query-service/rest-service/src/main/resources/application-docker.yml
index 955a46143c..5fe2015917 100644
--- a/fda-query-service/rest-service/src/main/resources/application-docker.yml
+++ b/fda-query-service/rest-service/src/main/resources/application-docker.yml
@@ -30,7 +30,7 @@ logging:
   level:
     root: warn
     at.tuwien.: debug
-    at.tuwien.mapper.: trace
+    at.tuwien.mapper.: debug
     at.tuwien.service.: debug
     at.tuwien.config.: debug
     at.tuwien.auth.UserPermissionEvaluator: trace
diff --git a/fda-query-service/services/src/main/java/at/tuwien/mapper/QueryMapper.java b/fda-query-service/services/src/main/java/at/tuwien/mapper/QueryMapper.java
index fab90ea031..f1bc79f819 100644
--- a/fda-query-service/services/src/main/java/at/tuwien/mapper/QueryMapper.java
+++ b/fda-query-service/services/src/main/java/at/tuwien/mapper/QueryMapper.java
@@ -403,8 +403,7 @@ public interface QueryMapper {
                     log.debug("failed to map column names, tuple contains columns names that are not present in the database, tuple column names are {}", data.getData().keySet());
                     throw new TableMalformedException("Failed to map column names");
                 }
-                ps.setObject(idx[0]++, dataColumnToObject(tuple.get()
-                        .getValue(), column));
+                prepareStatementWithColumnTypeObject(ps, column.getColumnType(), idx[0]++, tuple.get().getValue());
             }
             return ps;
         } catch (SQLException e) {
diff --git a/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
index 101c2d82e4..c9d5320916 100644
--- a/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
+++ b/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
@@ -93,9 +93,9 @@ public class TableEndpoint extends AbstractEndpoint {
             throw new NotAllowedException("Missing table view permission");
         }
         final Table table = tableService.findById(containerId, databaseId, tableId, principal);
-        log.debug(table);
-        TableDto tableDto = tableMapper.tableToTableDto(table);
-        log.debug(tableDto);
+        log.info("Found table with id {}", tableId);
+        log.debug("found table {}", table);
+        final TableDto tableDto = tableMapper.tableToTableDto(table);
         return ResponseEntity.ok(tableDto);
     }
 
diff --git a/fda-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java b/fda-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java
index cbb946cfc0..4df1d5f362 100644
--- a/fda-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java
+++ b/fda-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java
@@ -51,11 +51,7 @@ public interface TableMapper {
 
     /* keep */
     @Mappings({
-            @Mapping(target = "name", expression = "java(data.getName())"),
-            @Mapping(target = "internalName", expression = "java(data.getInternalName())"),
             @Mapping(target = "unique", source = "isUnique"),
-            @Mapping(target = "checkExpression", expression = "java(data.getCheckExpression())"),
-            @Mapping(target = "foreignKey", expression = "java(data.getForeignKey())")
     })
     ColumnDto tableColumnToColumnDto(TableColumn data);
 
diff --git a/fda-ui/components/TableList.vue b/fda-ui/components/TableList.vue
index e7168b8a15..a5149bccf7 100644
--- a/fda-ui/components/TableList.vue
+++ b/fda-ui/components/TableList.vue
@@ -97,6 +97,7 @@
             <v-data-table
               class="full-width"
               disable-sort
+              :loading="loadingDetails"
               hide-default-footer
               :headers="headers"
               :items="tableDetails.columns">
@@ -134,7 +135,7 @@
     <v-dialog
       v-model="unitDialog"
       max-width="600px">
-      <DialogsColumnUnit :concept="unit" :table-id="tableDetails.id" @close="closed" />
+      <DialogsColumnUnit :column="column" :table-id="tableDetails.id" @close="closed" />
     </v-dialog>
     <v-dialog v-model="dialogDelete" max-width="640">
       <v-card>
@@ -164,10 +165,11 @@ export default {
   data () {
     return {
       loading: false,
+      loadingDetails: false,
       error: false,
       tables: [],
       panel: null,
-      unit: null,
+      column: null,
       unitDialog: false,
       database: {
         exchange: null,
@@ -230,7 +232,7 @@ export default {
   },
   methods: {
     pickUnit (item) {
-      this.unit = item.column_concept
+      this.column = item
       this.unitDialog = true
       console.debug('select', this.unit)
     },
@@ -255,12 +257,20 @@ export default {
       }
       return column.column_type
     },
-    details (tableId) {
-      const select = this.tables.filter(t => t.id === tableId)
-      if (select.length > 0) {
-        this.tableDetails = select[0]
+    async details (tableId) {
+      try {
+        this.loadingDetails = true
+        const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${tableId}`, this.config)
+        this.tableDetails = res.data
         console.debug('table details', this.tableDetails)
+        if (tableId) {
+          this.openPanelByTableId(tableId)
+        }
+      } catch (err) {
+        this.$toast.error('Failed to load table details')
+        console.error('Failed to load table details', err)
       }
+      this.loadingDetails = false
     },
     closed (data) {
       console.debug('closed dialog', data)
diff --git a/fda-ui/components/dialogs/ColumnUnit.vue b/fda-ui/components/dialogs/ColumnUnit.vue
index 24fde4976d..896d201784 100644
--- a/fda-ui/components/dialogs/ColumnUnit.vue
+++ b/fda-ui/components/dialogs/ColumnUnit.vue
@@ -1,84 +1,91 @@
 <template>
   <div>
-    <v-card>
-      <v-card-title>
-        Assign Unit of Measurement
-      </v-card-title>
-      <v-card-text>
-        <v-autocomplete
-          v-model="model"
-          solo
-          clearable
-          auto-select-first
-          :cache-items="false"
-          autofocus
-          :search-input.sync="search"
-          :items="items"
-          hide-no-data
-          hide-details
-          dense>
-          <template
-            v-slot:item="{ item, attrs, on }">
-            <v-list-item v-bind="attrs" v-on="on">
+    <v-form v-model="valid">
+      <v-card>
+        <v-card-title>
+          Unit of Measurement
+        </v-card-title>
+        <v-card-subtitle>
+          Assign a unit of measurement to column <strong>{{ column.name }}</strong>
+        </v-card-subtitle>
+        <v-card-text>
+          <v-autocomplete
+            v-model="model"
+            solo
+            clearable
+            auto-select-first
+            :cache-items="false"
+            autofocus
+            :loading="isLoading"
+            placeholder="Search Unit of Measurements"
+            :search-input.sync="search"
+            :items="items"
+            hide-no-data
+            hide-details
+            dense>
+            <template
+              v-slot:item="{ item, attrs, on }">
+              <v-list-item v-bind="attrs" v-on="on">
+                <v-list-item-content>
+                  <v-list-item-title>{{ item.value.name }}</v-list-item-title>
+                  <v-list-item-subtitle>{{ item.value.comment }}</v-list-item-subtitle>
+                </v-list-item-content>
+              </v-list-item>
+            </template>
+          </v-autocomplete>
+        </v-card-text>
+        <v-expand-transition>
+          <v-list v-if="model" class="lighten-3" subheader three-line>
+            <v-list-item v-if="model.name">
               <v-list-item-content>
-                <v-list-item-title>{{ item.value.name }}</v-list-item-title>
-                <v-list-item-subtitle>{{ item.value.comment }}</v-list-item-subtitle>
+                <v-list-item-title>Name</v-list-item-title>
+                <v-list-item-subtitle>{{ model.name }}</v-list-item-subtitle>
               </v-list-item-content>
             </v-list-item>
-          </template>
-        </v-autocomplete>
-      </v-card-text>
-      <v-expand-transition>
-        <v-list v-if="model" class="lighten-3" subheader three-line>
-          <v-list-item v-if="model.name">
-            <v-list-item-content>
-              <v-list-item-title>Name</v-list-item-title>
-              <v-list-item-subtitle>{{ model.name }}</v-list-item-subtitle>
-            </v-list-item-content>
-          </v-list-item>
-          <v-list-item v-if="model.symbol">
-            <v-list-item-content>
-              <v-list-item-title>Symbol</v-list-item-title>
-              <v-list-item-subtitle>{{ model.symbol }}</v-list-item-subtitle>
-            </v-list-item-content>
-          </v-list-item>
-          <v-list-item v-if="model.comment">
-            <v-list-item-content>
-              <v-list-item-title>Comment</v-list-item-title>
-              <v-list-item-subtitle>{{ model.comment }}</v-list-item-subtitle>
-            </v-list-item-content>
-          </v-list-item>
-          <v-list-item v-if="model.uri" three-line>
-            <v-list-item-content>
-              <v-list-item-title>URI</v-list-item-title>
-              <v-list-item-subtitle>{{ model.uri }}</v-list-item-subtitle>
-            </v-list-item-content>
-          </v-list-item>
-        </v-list>
-      </v-expand-transition>
-      <v-card-actions>
-        <v-spacer />
-        <v-btn
-          class="mb-2 mr-2"
-          @click="cancel">
-          Cancel
-        </v-btn>
-        <v-btn
-          color="primary"
-          class="mb-2"
-          :disabled="!model"
-          @click="save">
-          Save
-        </v-btn>
-      </v-card-actions>
-    </v-card>
+            <v-list-item v-if="model.symbol">
+              <v-list-item-content>
+                <v-list-item-title>Symbol</v-list-item-title>
+                <v-list-item-subtitle>{{ model.symbol }}</v-list-item-subtitle>
+              </v-list-item-content>
+            </v-list-item>
+            <v-list-item v-if="model.comment">
+              <v-list-item-content>
+                <v-list-item-title>Comment</v-list-item-title>
+                <v-list-item-subtitle>{{ model.comment }}</v-list-item-subtitle>
+              </v-list-item-content>
+            </v-list-item>
+            <v-list-item v-if="uri" three-line>
+              <v-list-item-content>
+                <v-list-item-title>URI</v-list-item-title>
+                <v-list-item-subtitle>{{ uri }}</v-list-item-subtitle>
+              </v-list-item-content>
+            </v-list-item>
+          </v-list>
+        </v-expand-transition>
+        <v-card-actions>
+          <v-spacer />
+          <v-btn
+            class="mb-2"
+            @click="cancel">
+            Cancel
+          </v-btn>
+          <v-btn
+            color="primary"
+            class="mb-2 mr-2"
+            :disabled="!model || !uri"
+            @click="save">
+            Save
+          </v-btn>
+        </v-card-actions>
+      </v-card>
+    </v-form>
   </div>
 </template>
 
 <script>
 export default {
   props: {
-    concept: {
+    column: {
       type: Object,
       default: () => ({})
     },
@@ -89,13 +96,15 @@ export default {
       dialog: false,
       isLoading: false,
       saved: false,
+      valid: false,
       model: {
         name: null,
-        uri: null,
-        symbol: null
+        symbol: null,
+        comment: null
       },
       uri: null,
       search: null,
+      searchTerm: null,
       entries: []
     }
   },
@@ -117,25 +126,35 @@ export default {
     concept (newVal, oldVal) {
       this.loadConcept(newVal)
     },
+    model (newVal, oldVal) {
+      console.debug('selected concept', newVal)
+      this.loadConcept(newVal)
+    },
     async search (val) {
-      if (this.isLoading) { return }
-      if (!val || !val.length) { return }
+      if (!val) {
+        return
+      }
+      this.searchTerm = val
       this.isLoading = true
+      await new Promise(resolve => setTimeout(resolve, 1000))
+      if (val !== this.searchTerm) {
+        return
+      }
       try {
         const res = await this.$axios.post('/api/units/suggest', {
           offset: 0,
-          ustring: this.search
+          ustring: val
         })
         this.entries = res.data
+        console.debug('suggest', res.data)
       } catch (err) {
-        this.$toast.error('Could not load unit suggestions.')
-        console.log(err)
+        console.error('suggest', err)
       }
       this.isLoading = false
     }
   },
   mounted () {
-    this.loadConcept(this.concept)
+    this.loadConcept(this.column)
   },
   methods: {
     cancel () {
@@ -148,30 +167,42 @@ export default {
         return
       }
       this.model = concept
-      console.debug('load concept', concept)
+      console.debug('loading concept', concept)
       try {
         const res = await this.$axios.get(`/api/units/uri/${concept.name}`)
-        this.model.uri = res.data.URI
+        if (!res.data) {
+          console.warn('concept', concept.name, 'returned invalid data')
+          console.debug('concept', concept, 'returned', res.data)
+          return
+        }
+        this.uri = res.data.URI
+        console.debug('uri loaded', this.uri)
       } catch (err) {
         this.$toast.error(`Could not load URI of unit "${concept.name}"`)
-        console.log(err)
+        console.error('load concept', err)
       }
     },
     async save () {
       const payload = {
         name: this.model.name,
-        uri: this.model.uri
+        uri: this.uri
       }
+      /* save concept */
       try {
         console.debug('save', payload)
-        await this.$axios.post('/api/units/saveconcept', payload)
+        const res = await this.$axios.post('/api/units/saveconcept', payload)
+        console.info('Concept saved')
+        console.debug('concept saved', res.data)
       } catch (error) {
         const { status } = error.response
-        if (status !== 201 && status !== 400) {
+        if (status === 409) {
+          console.debug('concept already saved, skipping.')
+        } else {
           this.$toast.error('Could not save concept.')
-          console.log(error)
+          console.error('save', error)
         }
       }
+      /* save concept */
       try {
         const res = await this.$axios.post('/api/units/savecolumnsconcept', {
           cdbid: Number(this.$route.params.database_id),
@@ -183,6 +214,7 @@ export default {
         this.column.column_concept.name = this.model.name
         this.dialog = false
         this.saved = true
+        this.$toast.success(`Assigned unit ${this.model.name}`)
         this.$emit('close', {
           success: true,
           concept: res.data
@@ -190,7 +222,7 @@ export default {
         console.debug('column', this.column)
       } catch (err) {
         this.$toast.error('Could not save column unit.')
-        console.log(err)
+        console.error('save', err)
       }
     }
   }
diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/index.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/index.vue
index 229c195476..4ea69814bb 100644
--- a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/index.vue
+++ b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/index.vue
@@ -12,13 +12,13 @@
       </v-toolbar-title>
       <v-spacer />
       <v-toolbar-title>
-        <v-btn color="primary" :disabled="!token" class="mr-2" @click="addTuple">
+        <v-btn v-if="is_owner" color="primary" class="mr-2" @click="addTuple">
           <v-icon left>mdi-plus</v-icon> Add
         </v-btn>
-        <v-btn v-if="canEdit" :disabled="!token" color="warning" class="mr-2 mb-1" @click="editTupleDialog = true">
+        <v-btn v-if="is_owner && canEdit" color="warning" class="mr-2 mb-1" @click="editTupleDialog = true">
           <v-icon left>mdi-pencil</v-icon> Edit
         </v-btn>
-        <v-btn v-if="canDelete" :disabled="!token" color="error" class="mr-2 mb-1" @click="deleteItems">
+        <v-btn v-if="is_owner && canDelete" color="error" class="mr-2 mb-1" @click="deleteItems">
           <v-icon left>mdi-delete</v-icon> Delete<span v-if="selection.length > 1">&nbsp;{{ selection.length }}</span>
         </v-btn>
         <v-btn :disabled="!token" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/query/create?tid=${$route.params.table_id}`" color="secondary" class="mr-2 mb-1" @click="deleteItems">
@@ -66,7 +66,7 @@
         :options.sync="options"
         :server-items-length="total"
         :footer-props="footerProps">
-        <template v-if="token" v-slot:item.selection="{ item }">
+        <template v-if="is_owner" v-slot:item.selection="{ item }">
           <input v-model="selection" type="checkbox" :value="item" @click="edit = true">
         </template>
       </v-data-table>
@@ -106,6 +106,9 @@ export default {
       pickVersionDialog: null,
       version: null,
       edit: false,
+      user: {
+        username: null
+      },
       error: false, // XXX: `error` is never changed
       options: {
         page: 1,
@@ -115,7 +118,10 @@ export default {
       table: {
         name: null,
         description: null,
-        columns: []
+        columns: [],
+        creator: {
+          username: null
+        }
       },
       items: [
         { text: 'Databases', to: '/container', activeClass: '' },
@@ -166,6 +172,9 @@ export default {
     },
     canDelete () {
       return this.selection.length !== 0
+    },
+    is_owner () {
+      return this.token && this.table.creator.username === this.user.username
     }
   },
   watch: {
@@ -180,6 +189,7 @@ export default {
   mounted () {
     this.loadProperties()
     this.loadData()
+    this.loadUser()
   },
   methods: {
     async download () {
@@ -255,7 +265,8 @@ export default {
       try {
         const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}`, this.config)
         this.table = res.data
-        console.debug('headers', res.data.columns, 'table', this.table)
+        console.debug('headers', res.data.columns)
+        console.debug('table', this.table)
         this.headers = [{ value: 'selection', text: '', sortable: false }]
         res.data.columns.map((c) => {
           return {
@@ -301,6 +312,16 @@ export default {
         this.$toast.error('Could not load table data.')
       }
       this.loadingData = false
+    },
+    async loadUser () {
+      try {
+        const res = await this.$axios.put('/api/auth', {}, this.config)
+        this.user = res.data
+        console.debug('user', this.user)
+      } catch (err) {
+        this.$toast.error('Failed to get user details')
+        console.error('Failed to get user details', err)
+      }
     }
   }
 }
diff --git a/fda-ui/pages/container/index.vue b/fda-ui/pages/container/index.vue
index c4eabf788c..7ef5bd9280 100644
--- a/fda-ui/pages/container/index.vue
+++ b/fda-ui/pages/container/index.vue
@@ -48,8 +48,8 @@
                 </sup>
               </td>
               <td>
-                <v-icon v-if="!item.is_public" color="primary" class="private-icon" right>mdi-lock-outline</v-icon>
-                <v-icon v-if="item.is_public" class="private-icon" right>mdi-lock-open-outline</v-icon>
+                <v-icon v-if="!item.is_public" color="primary" title="Private" class="private-icon" right>mdi-lock-outline</v-icon>
+                <v-icon v-if="item.is_public" class="private-icon" title="Public" right>mdi-lock-open-outline</v-icon>
               </td>
               <td>
                 {{ createdUTC(item.created) }}
diff --git a/fda-units-service/app.py b/fda-units-service/app.py
index 8e88836b92..85fbc5ae8c 100644
--- a/fda-units-service/app.py
+++ b/fda-units-service/app.py
@@ -104,7 +104,7 @@ def saveconcept():
             logging.info(f"Inserted values {uri}, {c_name} into mdb_concepts.")
             return jsonify({'uri': uri}), 201
         else:
-            return jsonify({'status': 'error'}), 400
+            return jsonify({'status': 'error'}), 409
     except Exception as e:
         logging.error(e)
         res = {"success": False, "message": str(e)}
@@ -123,7 +123,7 @@ def savecolumnconcept():
             logging.info(f"Inserted values {uri},{cid},{tid},{cdbid} into mdb_columns_concepts.")
             return jsonify({'uri': uri}), 201
         else:
-            return jsonify({'status': 'error'}), 400
+            return jsonify({'status': 'error'}), 409
     except Exception as e:
         print(e)
         res = {"success": False, "message": str(e)}
diff --git a/fda-units-service/us-yml/savecolumnsconcept.yml b/fda-units-service/us-yml/savecolumnsconcept.yml
index ec662c1a3c..b391fd4f77 100644
--- a/fda-units-service/us-yml/savecolumnsconcept.yml
+++ b/fda-units-service/us-yml/savecolumnsconcept.yml
@@ -27,5 +27,10 @@ parameters:
 responses:
   200: 
     description: "OK"
+  201:
+    description: "Created"
   405:
     description: "Invalid input"
+  409:
+    description: "Concept already assigned"
+
diff --git a/fda-units-service/us-yml/saveconcept.yml b/fda-units-service/us-yml/saveconcept.yml
index 22144651f1..509b71efb4 100644
--- a/fda-units-service/us-yml/saveconcept.yml
+++ b/fda-units-service/us-yml/saveconcept.yml
@@ -19,7 +19,11 @@ parameters:
         type: "string"
         example: "metre"
 responses:
-  200: 
+  200:
     description: "OK"
+  201:
+    description: "Created"
   405:
     description: "Invalid input"
+  409:
+    description: "Concept already present"
-- 
GitLab