migrates DAO classes

Thu, 19 Nov 2020 13:58:54 +0100

author
mike@uapl01.localdomain
date
Thu, 19 Nov 2020 13:58:54 +0100
changeset 159
86b5d8a1662f
parent 158
4f912cd42876
child 160
e2d09cf3fb96

migrates DAO classes

src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/ChildEntityDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/ComponentDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/DataAccessObjects.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/IssueDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/ProjectDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/RootEntityDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/UserDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/VersionDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/postgres/PGComponentDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/postgres/PGDataAccessObjects.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/postgres/PGUserDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/modules/UsersModule.java file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/AbstractChildEntityDao.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/AbstractComponentDao.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/AbstractDao.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/AbstractEntityDao.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/AbstractIssueDao.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/AbstractProjectDao.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/AbstractUserDao.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/AbstractVersionDao.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/DaoProvider.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGComponentDao.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGDaoProvider.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGIssueDao.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGProjectDao.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGUserDao.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGVersionDao.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/entities/User.kt file | annotate | diff | comparison | revisions
     1.1 --- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Fri Nov 06 10:50:32 2020 +0100
     1.2 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Thu Nov 19 13:58:54 2020 +0100
     1.3 @@ -28,8 +28,8 @@
     1.4   */
     1.5  package de.uapcore.lightpit;
     1.6  
     1.7 -import de.uapcore.lightpit.dao.DataAccessObjects;
     1.8 -import de.uapcore.lightpit.dao.postgres.PGDataAccessObjects;
     1.9 +import de.uapcore.lightpit.dao.DaoProvider;
    1.10 +import de.uapcore.lightpit.dao.postgres.PGDaoProvider;
    1.11  import org.slf4j.Logger;
    1.12  import org.slf4j.LoggerFactory;
    1.13  
    1.14 @@ -100,15 +100,15 @@
    1.15       * @param connection the SQL connection
    1.16       * @return a set of data access objects
    1.17       */
    1.18 -    private DataAccessObjects createDataAccessObjects(Connection connection) throws SQLException {
    1.19 +    private DaoProvider createDataAccessObjects(Connection connection) throws SQLException {
    1.20          final var df = (DataSourceProvider) getServletContext().getAttribute(DataSourceProvider.Companion.getSC_ATTR_NAME());
    1.21          if (df.getDialect() == DataSourceProvider.Dialect.Postgres) {
    1.22 -            return new PGDataAccessObjects(connection);
    1.23 +            return new PGDaoProvider(connection);
    1.24          }
    1.25          throw new UnsupportedOperationException("Non-exhaustive if-else - this is a bug.");
    1.26      }
    1.27  
    1.28 -    private void invokeMapping(Map.Entry<PathPattern, Method> mapping, HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
    1.29 +    private void invokeMapping(Map.Entry<PathPattern, Method> mapping, HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws IOException {
    1.30          final var pathPattern = mapping.getKey();
    1.31          final var method = mapping.getValue();
    1.32          try {
    1.33 @@ -121,7 +121,7 @@
    1.34                  } else if (paramTypes[i].isAssignableFrom(HttpServletResponse.class)) {
    1.35                      paramValues[i] = resp;
    1.36                  }
    1.37 -                if (paramTypes[i].isAssignableFrom(DataAccessObjects.class)) {
    1.38 +                if (paramTypes[i].isAssignableFrom(DaoProvider.class)) {
    1.39                      paramValues[i] = dao;
    1.40                  }
    1.41                  if (paramTypes[i].isAssignableFrom(PathParameters.class)) {
    1.42 @@ -180,7 +180,7 @@
    1.43                          paramsInjectible &= HttpServletRequest.class.isAssignableFrom(param)
    1.44                                  || HttpServletResponse.class.isAssignableFrom(param)
    1.45                                  || PathParameters.class.isAssignableFrom(param)
    1.46 -                                || DataAccessObjects.class.isAssignableFrom(param);
    1.47 +                                || DaoProvider.class.isAssignableFrom(param);
    1.48                      }
    1.49                      if (paramsInjectible) {
    1.50                          try {
     2.1 --- a/src/main/java/de/uapcore/lightpit/dao/ChildEntityDao.java	Fri Nov 06 10:50:32 2020 +0100
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,116 +0,0 @@
     2.4 -/*
     2.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     2.6 - *
     2.7 - * Copyright 2018 Mike Becker. All rights reserved.
     2.8 - *
     2.9 - * Redistribution and use in source and binary forms, with or without
    2.10 - * modification, are permitted provided that the following conditions are met:
    2.11 - *
    2.12 - *   1. Redistributions of source code must retain the above copyright
    2.13 - *      notice, this list of conditions and the following disclaimer.
    2.14 - *
    2.15 - *   2. Redistributions in binary form must reproduce the above copyright
    2.16 - *      notice, this list of conditions and the following disclaimer in the
    2.17 - *      documentation and/or other materials provided with the distribution.
    2.18 - *
    2.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    2.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    2.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    2.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    2.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    2.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    2.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    2.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    2.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    2.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    2.29 - * POSSIBILITY OF SUCH DAMAGE.
    2.30 - *
    2.31 - */
    2.32 -package de.uapcore.lightpit.dao;
    2.33 -
    2.34 -import java.sql.SQLException;
    2.35 -import java.util.List;
    2.36 -
    2.37 -public interface ChildEntityDao<T, P> {
    2.38 -
    2.39 -    /**
    2.40 -     * Lists all entities being a child of the specified parent.
    2.41 -     * @param parent the parent
    2.42 -     * @return the list of child instances
    2.43 -     * @throws SQLException on any kind of SQL errors
    2.44 -     */
    2.45 -    List<T> list(P parent) throws SQLException;
    2.46 -
    2.47 -    /**
    2.48 -     * Finds an entity by its integer ID.
    2.49 -     * It is not guaranteed that referenced entities are automatically joined.
    2.50 -     *
    2.51 -     * @param id the id
    2.52 -     * @return the enity or null if there is no such entity
    2.53 -     * @throws SQLException on any kind of SQL errors
    2.54 -     */
    2.55 -    T find(int id) throws SQLException;
    2.56 -
    2.57 -    /**
    2.58 -     * Inserts an instance into database.
    2.59 -     * It is not guaranteed that generated fields will be updated in the instance.
    2.60 -     *
    2.61 -     * @param instance the instance to insert
    2.62 -     * @param parent a reference to the parent
    2.63 -     * @throws SQLException on any kind of SQL errors
    2.64 -     */
    2.65 -    void save(T instance, P parent) throws SQLException;
    2.66 -
    2.67 -    /**
    2.68 -     * Updates an instance in the database.
    2.69 -     *
    2.70 -     * @param instance the instance to insert
    2.71 -     * @return true if an instance has been updated, false if no instance with the specified ID was found
    2.72 -     * @throws SQLException on any kind of SQL errors
    2.73 -     */
    2.74 -    boolean update(T instance) throws SQLException;
    2.75 -
    2.76 -    /**
    2.77 -     * Updates an instance in the database changing the parent.
    2.78 -     * This operation is not supported by default.
    2.79 -     *
    2.80 -     * @param instance the instance to insert
    2.81 -     * @param newParent a reference to the new parent
    2.82 -     * @return true if an instance has been updated, false if no instance with the specified ID was found
    2.83 -     * @throws SQLException on any kind of SQL errors
    2.84 -     * @see #isChangingParentSupported()
    2.85 -     */
    2.86 -    default boolean update(T instance, P newParent) throws SQLException {
    2.87 -        throw new UnsupportedOperationException();
    2.88 -    }
    2.89 -
    2.90 -    /**
    2.91 -     * Returns true if changing the parent is supported by this DAO.
    2.92 -     * This method must return true, if {@link #update(Object, Object)} is implemented.
    2.93 -     * @return true, if changing the parent is supported
    2.94 -     */
    2.95 -    default boolean isChangingParentSupported() {
    2.96 -        return false;
    2.97 -    }
    2.98 -
    2.99 -    /**
   2.100 -     * Inserts or updates an instance in the database.
   2.101 -     * Tries an update first and if that fails, performs a save.
   2.102 -     * If changing a parent is not supported by this DAO,
   2.103 -     * specifying an alternate parent for an existing instance has no effect.
   2.104 -     *
   2.105 -     * @param instance the instance to insert or update
   2.106 -     * @param parent a reference to the parent
   2.107 -     * @throws SQLException on any kind of SQL errors
   2.108 -     * @see #update(Object)
   2.109 -     * @see #update(Object, Object)
   2.110 -     * @see #save(Object, Object)
   2.111 -     */
   2.112 -    default void saveOrUpdate(T instance, P parent) throws SQLException {
   2.113 -        if (isChangingParentSupported()) {
   2.114 -            if (!update(instance, parent)) save(instance, parent);
   2.115 -        } else {
   2.116 -            if (!update(instance)) save(instance, parent);
   2.117 -        }
   2.118 -    }
   2.119 -}
     3.1 --- a/src/main/java/de/uapcore/lightpit/dao/ComponentDao.java	Fri Nov 06 10:50:32 2020 +0100
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,38 +0,0 @@
     3.4 -/*
     3.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.6 - *
     3.7 - * Copyright 2018 Mike Becker. All rights reserved.
     3.8 - *
     3.9 - * Redistribution and use in source and binary forms, with or without
    3.10 - * modification, are permitted provided that the following conditions are met:
    3.11 - *
    3.12 - *   1. Redistributions of source code must retain the above copyright
    3.13 - *      notice, this list of conditions and the following disclaimer.
    3.14 - *
    3.15 - *   2. Redistributions in binary form must reproduce the above copyright
    3.16 - *      notice, this list of conditions and the following disclaimer in the
    3.17 - *      documentation and/or other materials provided with the distribution.
    3.18 - *
    3.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    3.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    3.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    3.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    3.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    3.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    3.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    3.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    3.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    3.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    3.29 - * POSSIBILITY OF SUCH DAMAGE.
    3.30 - *
    3.31 - */
    3.32 -package de.uapcore.lightpit.dao;
    3.33 -
    3.34 -import de.uapcore.lightpit.entities.Component;
    3.35 -import de.uapcore.lightpit.entities.Project;
    3.36 -
    3.37 -import java.sql.SQLException;
    3.38 -
    3.39 -public interface ComponentDao extends ChildEntityDao<Component, Project> {
    3.40 -    Component findByNode(Project parent, String node) throws SQLException;
    3.41 -}
     4.1 --- a/src/main/java/de/uapcore/lightpit/dao/DataAccessObjects.java	Fri Nov 06 10:50:32 2020 +0100
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,41 +0,0 @@
     4.4 -/*
     4.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     4.6 - *
     4.7 - * Copyright 2018 Mike Becker. All rights reserved.
     4.8 - *
     4.9 - * Redistribution and use in source and binary forms, with or without
    4.10 - * modification, are permitted provided that the following conditions are met:
    4.11 - *
    4.12 - *   1. Redistributions of source code must retain the above copyright
    4.13 - *      notice, this list of conditions and the following disclaimer.
    4.14 - *
    4.15 - *   2. Redistributions in binary form must reproduce the above copyright
    4.16 - *      notice, this list of conditions and the following disclaimer in the
    4.17 - *      documentation and/or other materials provided with the distribution.
    4.18 - *
    4.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    4.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    4.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    4.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    4.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    4.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    4.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    4.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    4.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    4.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    4.29 - * POSSIBILITY OF SUCH DAMAGE.
    4.30 - *
    4.31 - */
    4.32 -package de.uapcore.lightpit.dao;
    4.33 -
    4.34 -public interface DataAccessObjects {
    4.35 -    UserDao getUserDao();
    4.36 -
    4.37 -    ProjectDao getProjectDao();
    4.38 -
    4.39 -    VersionDao getVersionDao();
    4.40 -
    4.41 -    ComponentDao getComponentDao();
    4.42 -
    4.43 -    IssueDao getIssueDao();
    4.44 -}
     5.1 --- a/src/main/java/de/uapcore/lightpit/dao/IssueDao.java	Fri Nov 06 10:50:32 2020 +0100
     5.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.3 @@ -1,111 +0,0 @@
     5.4 -/*
     5.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     5.6 - *
     5.7 - * Copyright 2018 Mike Becker. All rights reserved.
     5.8 - *
     5.9 - * Redistribution and use in source and binary forms, with or without
    5.10 - * modification, are permitted provided that the following conditions are met:
    5.11 - *
    5.12 - *   1. Redistributions of source code must retain the above copyright
    5.13 - *      notice, this list of conditions and the following disclaimer.
    5.14 - *
    5.15 - *   2. Redistributions in binary form must reproduce the above copyright
    5.16 - *      notice, this list of conditions and the following disclaimer in the
    5.17 - *      documentation and/or other materials provided with the distribution.
    5.18 - *
    5.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    5.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    5.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    5.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    5.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    5.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    5.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    5.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    5.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    5.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    5.29 - * POSSIBILITY OF SUCH DAMAGE.
    5.30 - *
    5.31 - */
    5.32 -package de.uapcore.lightpit.dao;
    5.33 -
    5.34 -import de.uapcore.lightpit.entities.*;
    5.35 -
    5.36 -import java.sql.SQLException;
    5.37 -import java.util.List;
    5.38 -
    5.39 -public interface IssueDao extends ChildEntityDao<Issue, Project> {
    5.40 -
    5.41 -    /**
    5.42 -     * Lists all issues that are related to the specified component and version.
    5.43 -     * If component or version is null, search for issues that are not assigned to any
    5.44 -     * component or version, respectively.
    5.45 -     *
    5.46 -     * @param project the project
    5.47 -     * @param component the component or null
    5.48 -     * @param version the version or null
    5.49 -     * @return a list of issues
    5.50 -     * @throws SQLException on any kind of SQL error
    5.51 -     */
    5.52 -    List<Issue> list(Project project, Component component, Version version) throws SQLException;
    5.53 -
    5.54 -    /**
    5.55 -     * Lists all issues that are related to the specified version.
    5.56 -     * If the version is null, lists issues that are not assigned to any version.
    5.57 -     *
    5.58 -     * @param project the project (mandatory)
    5.59 -     * @param version the version or null
    5.60 -     * @return a list of issues
    5.61 -     * @throws SQLException on any kind of SQL error
    5.62 -     */
    5.63 -    List<Issue> list(Project project, Version version) throws SQLException;
    5.64 -
    5.65 -    /**
    5.66 -     * Lists all issues that are related to the specified component.
    5.67 -     * If the component is null, lists issues that are not assigned to a component.
    5.68 -     *
    5.69 -     * @param project the project (mandatory)
    5.70 -     * @param component the component or null
    5.71 -     * @return a list of issues
    5.72 -     * @throws SQLException on any kind of SQL error
    5.73 -     */
    5.74 -    List<Issue> list(Project project, Component component) throws SQLException;
    5.75 -
    5.76 -    /**
    5.77 -     * Lists all comments for a specific issue in chronological order.
    5.78 -     *
    5.79 -     * @param issue the issue
    5.80 -     * @return the list of comments
    5.81 -     * @throws SQLException on any kind of SQL error
    5.82 -     */
    5.83 -    List<IssueComment> listComments(Issue issue) throws SQLException;
    5.84 -
    5.85 -    /**
    5.86 -     * Stores the specified comment in database.
    5.87 -     * This is an update-or-insert operation.
    5.88 -     *
    5.89 -     * @param issue the issue to save the comment for
    5.90 -     * @param comment the comment to save
    5.91 -     * @throws SQLException on any kind of SQL error
    5.92 -     */
    5.93 -    void saveComment(Issue issue, IssueComment comment) throws SQLException;
    5.94 -
    5.95 -    /**
    5.96 -     * Saves an instances to the database.
    5.97 -     * Implementations of this DAO must guarantee that the generated ID is stored in the instance.
    5.98 -     *
    5.99 -     * @param instance the instance to insert
   5.100 -     * @param project the parent project
   5.101 -     * @throws SQLException on any kind of SQL error
   5.102 -     * @see Issue#setId(int)
   5.103 -     */
   5.104 -    @Override
   5.105 -    void save(Issue instance, Project project) throws SQLException;
   5.106 -
   5.107 -    /**
   5.108 -     * Retrieves the affected, scheduled and resolved versions for the specified issue.
   5.109 -     *
   5.110 -     * @param issue the issue to join the information for
   5.111 -     * @throws SQLException on any kind of SQL error
   5.112 -     */
   5.113 -    void joinVersionInformation(Issue issue) throws SQLException;
   5.114 -}
     6.1 --- a/src/main/java/de/uapcore/lightpit/dao/ProjectDao.java	Fri Nov 06 10:50:32 2020 +0100
     6.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.3 @@ -1,40 +0,0 @@
     6.4 -/*
     6.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     6.6 - *
     6.7 - * Copyright 2018 Mike Becker. All rights reserved.
     6.8 - *
     6.9 - * Redistribution and use in source and binary forms, with or without
    6.10 - * modification, are permitted provided that the following conditions are met:
    6.11 - *
    6.12 - *   1. Redistributions of source code must retain the above copyright
    6.13 - *      notice, this list of conditions and the following disclaimer.
    6.14 - *
    6.15 - *   2. Redistributions in binary form must reproduce the above copyright
    6.16 - *      notice, this list of conditions and the following disclaimer in the
    6.17 - *      documentation and/or other materials provided with the distribution.
    6.18 - *
    6.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    6.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    6.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    6.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    6.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    6.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    6.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    6.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    6.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    6.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    6.29 - * POSSIBILITY OF SUCH DAMAGE.
    6.30 - *
    6.31 - */
    6.32 -package de.uapcore.lightpit.dao;
    6.33 -
    6.34 -import de.uapcore.lightpit.entities.IssueSummary;
    6.35 -import de.uapcore.lightpit.entities.Project;
    6.36 -
    6.37 -import java.sql.SQLException;
    6.38 -
    6.39 -public interface ProjectDao extends RootEntityDao<Project> {
    6.40 -    IssueSummary getIssueSummary(Project project) throws SQLException;
    6.41 -
    6.42 -    Project findByNode(String node) throws SQLException;
    6.43 -}
     7.1 --- a/src/main/java/de/uapcore/lightpit/dao/RootEntityDao.java	Fri Nov 06 10:50:32 2020 +0100
     7.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.3 @@ -1,83 +0,0 @@
     7.4 -/*
     7.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     7.6 - *
     7.7 - * Copyright 2018 Mike Becker. All rights reserved.
     7.8 - *
     7.9 - * Redistribution and use in source and binary forms, with or without
    7.10 - * modification, are permitted provided that the following conditions are met:
    7.11 - *
    7.12 - *   1. Redistributions of source code must retain the above copyright
    7.13 - *      notice, this list of conditions and the following disclaimer.
    7.14 - *
    7.15 - *   2. Redistributions in binary form must reproduce the above copyright
    7.16 - *      notice, this list of conditions and the following disclaimer in the
    7.17 - *      documentation and/or other materials provided with the distribution.
    7.18 - *
    7.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    7.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    7.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    7.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    7.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    7.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    7.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    7.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    7.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    7.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    7.29 - * POSSIBILITY OF SUCH DAMAGE.
    7.30 - *
    7.31 - */
    7.32 -package de.uapcore.lightpit.dao;
    7.33 -
    7.34 -import java.sql.SQLException;
    7.35 -import java.util.List;
    7.36 -
    7.37 -public interface RootEntityDao<T> {
    7.38 -
    7.39 -    /**
    7.40 -     * Lists all entities.
    7.41 -     * @return a list of all entities
    7.42 -     * @throws SQLException on any kind of SQL errors
    7.43 -     */
    7.44 -    List<T> list() throws SQLException;
    7.45 -
    7.46 -    /**
    7.47 -     * Finds an entity by its integer ID.
    7.48 -     * It is not guaranteed that referenced entities are automatically joined.
    7.49 -     *
    7.50 -     * @param id the id
    7.51 -     * @return the enity or null if there is no such entity
    7.52 -     * @throws SQLException on any kind of SQL errors
    7.53 -     */
    7.54 -    T find(int id) throws SQLException;
    7.55 -
    7.56 -    /**
    7.57 -     * Inserts an instance into database.
    7.58 -     * It is not guaranteed that generated fields will be updated in the instance.
    7.59 -     *
    7.60 -     * @param instance the instance to insert
    7.61 -     * @throws SQLException on any kind of SQL errors
    7.62 -     */
    7.63 -    void save(T instance) throws SQLException;
    7.64 -
    7.65 -    /**
    7.66 -     * Updates an instance in the database.
    7.67 -     *
    7.68 -     * @param instance the instance to insert
    7.69 -     * @return true if an instance has been updated, false if no instance with the specified ID was found
    7.70 -     * @throws SQLException on any kind of SQL errors
    7.71 -     */
    7.72 -    boolean update(T instance) throws SQLException;
    7.73 -
    7.74 -    /**
    7.75 -     * Inserts or updates an instance in the database.
    7.76 -     * Tries an update first and if that fails, performs a save.
    7.77 -     *
    7.78 -     * @param instance the instance to insert or update
    7.79 -     * @throws SQLException on any kind of SQL errors
    7.80 -     * @see #update(Object)
    7.81 -     * @see #save(Object)
    7.82 -     */
    7.83 -    default void saveOrUpdate(T instance) throws SQLException {
    7.84 -        if (!update(instance)) save(instance);
    7.85 -    }
    7.86 -}
     8.1 --- a/src/main/java/de/uapcore/lightpit/dao/UserDao.java	Fri Nov 06 10:50:32 2020 +0100
     8.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.3 @@ -1,47 +0,0 @@
     8.4 -/*
     8.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     8.6 - *
     8.7 - * Copyright 2018 Mike Becker. All rights reserved.
     8.8 - *
     8.9 - * Redistribution and use in source and binary forms, with or without
    8.10 - * modification, are permitted provided that the following conditions are met:
    8.11 - *
    8.12 - *   1. Redistributions of source code must retain the above copyright
    8.13 - *      notice, this list of conditions and the following disclaimer.
    8.14 - *
    8.15 - *   2. Redistributions in binary form must reproduce the above copyright
    8.16 - *      notice, this list of conditions and the following disclaimer in the
    8.17 - *      documentation and/or other materials provided with the distribution.
    8.18 - *
    8.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    8.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    8.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    8.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    8.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    8.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    8.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    8.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    8.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    8.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    8.29 - * POSSIBILITY OF SUCH DAMAGE.
    8.30 - *
    8.31 - */
    8.32 -package de.uapcore.lightpit.dao;
    8.33 -
    8.34 -import de.uapcore.lightpit.entities.User;
    8.35 -
    8.36 -import java.sql.SQLException;
    8.37 -import java.util.Optional;
    8.38 -
    8.39 -public interface UserDao extends RootEntityDao<User> {
    8.40 -
    8.41 -    /**
    8.42 -     * Tries to find a user by their username.
    8.43 -     * The search is case-insensitive.
    8.44 -     *
    8.45 -     * @param username the username
    8.46 -     * @return the user object or an empty optional if no such user exists
    8.47 -     * @throws SQLException
    8.48 -     */
    8.49 -    Optional<User> findByUsername(String username) throws SQLException;
    8.50 -}
     9.1 --- a/src/main/java/de/uapcore/lightpit/dao/VersionDao.java	Fri Nov 06 10:50:32 2020 +0100
     9.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.3 @@ -1,38 +0,0 @@
     9.4 -/*
     9.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     9.6 - *
     9.7 - * Copyright 2018 Mike Becker. All rights reserved.
     9.8 - *
     9.9 - * Redistribution and use in source and binary forms, with or without
    9.10 - * modification, are permitted provided that the following conditions are met:
    9.11 - *
    9.12 - *   1. Redistributions of source code must retain the above copyright
    9.13 - *      notice, this list of conditions and the following disclaimer.
    9.14 - *
    9.15 - *   2. Redistributions in binary form must reproduce the above copyright
    9.16 - *      notice, this list of conditions and the following disclaimer in the
    9.17 - *      documentation and/or other materials provided with the distribution.
    9.18 - *
    9.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    9.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    9.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    9.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    9.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    9.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    9.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    9.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    9.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    9.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    9.29 - * POSSIBILITY OF SUCH DAMAGE.
    9.30 - *
    9.31 - */
    9.32 -package de.uapcore.lightpit.dao;
    9.33 -
    9.34 -import de.uapcore.lightpit.entities.Project;
    9.35 -import de.uapcore.lightpit.entities.Version;
    9.36 -
    9.37 -import java.sql.SQLException;
    9.38 -
    9.39 -public interface VersionDao extends ChildEntityDao<Version, Project> {
    9.40 -    Version findByNode(Project parent, String node) throws SQLException;
    9.41 -}
    10.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGComponentDao.java	Fri Nov 06 10:50:32 2020 +0100
    10.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.3 @@ -1,130 +0,0 @@
    10.4 -/*
    10.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    10.6 - *
    10.7 - * Copyright 2018 Mike Becker. All rights reserved.
    10.8 - *
    10.9 - * Redistribution and use in source and binary forms, with or without
   10.10 - * modification, are permitted provided that the following conditions are met:
   10.11 - *
   10.12 - *   1. Redistributions of source code must retain the above copyright
   10.13 - *      notice, this list of conditions and the following disclaimer.
   10.14 - *
   10.15 - *   2. Redistributions in binary form must reproduce the above copyright
   10.16 - *      notice, this list of conditions and the following disclaimer in the
   10.17 - *      documentation and/or other materials provided with the distribution.
   10.18 - *
   10.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   10.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   10.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   10.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   10.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   10.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   10.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   10.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   10.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   10.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   10.29 - * POSSIBILITY OF SUCH DAMAGE.
   10.30 - *
   10.31 - */
   10.32 -package de.uapcore.lightpit.dao.postgres;
   10.33 -
   10.34 -import de.uapcore.lightpit.dao.ComponentDao;
   10.35 -import de.uapcore.lightpit.dao.Functions;
   10.36 -import de.uapcore.lightpit.entities.Component;
   10.37 -import de.uapcore.lightpit.entities.Project;
   10.38 -import de.uapcore.lightpit.entities.User;
   10.39 -import de.uapcore.lightpit.types.WebColor;
   10.40 -
   10.41 -import java.sql.Connection;
   10.42 -import java.sql.PreparedStatement;
   10.43 -import java.sql.ResultSet;
   10.44 -import java.sql.SQLException;
   10.45 -import java.util.List;
   10.46 -
   10.47 -public final class PGComponentDao implements ComponentDao {
   10.48 -
   10.49 -    private final PreparedStatement insert, update, list, find, findByNode;
   10.50 -
   10.51 -    public PGComponentDao(Connection connection) throws SQLException {
   10.52 -        final var query = "select id, name, node, color, ordinal, description, " +
   10.53 -                "userid, username, givenname, lastname, mail " +
   10.54 -                "from lpit_component " +
   10.55 -                "left join lpit_user on lead = userid";
   10.56 -
   10.57 -        list = connection.prepareStatement(query + " where project = ? order by ordinal, lower(name)");
   10.58 -
   10.59 -        find = connection.prepareStatement(query + " where id = ? ");
   10.60 -
   10.61 -        findByNode = connection.prepareStatement(query + " where project = ? and node = ?");
   10.62 -
   10.63 -        insert = connection.prepareStatement(
   10.64 -                "insert into lpit_component (name, node, color, ordinal, description, lead, project) values (?, ?, ?, ?, ?, ?, ?)"
   10.65 -        );
   10.66 -
   10.67 -        update = connection.prepareStatement(
   10.68 -                "update lpit_component set name = ?, node = ?, color = ?, ordinal = ?, description = ?, lead = ? where id = ?"
   10.69 -        );
   10.70 -    }
   10.71 -
   10.72 -    private static Component mapColumns(ResultSet result) throws SQLException {
   10.73 -        final var component = new Component(result.getInt("id"));
   10.74 -        component.setName(result.getString("name"));
   10.75 -        component.setNode(result.getString("node"));
   10.76 -        try {
   10.77 -            component.setColor(new WebColor(result.getString("color")));
   10.78 -        } catch (IllegalArgumentException ex) {
   10.79 -            // if someone tempered with the database we default the color to black
   10.80 -            component.setColor(new WebColor("000000"));
   10.81 -        }
   10.82 -        component.setOrdinal(result.getInt("ordinal"));
   10.83 -        component.setDescription(result.getString("description"));
   10.84 -        component.setLead(PGUserDao.mapColumns(result));
   10.85 -        return component;
   10.86 -    }
   10.87 -
   10.88 -    private static int setColumns(PreparedStatement stmt, Component instance) throws SQLException {
   10.89 -        int column = 0;
   10.90 -        stmt.setString(++column, instance.getName());
   10.91 -        stmt.setString(++column, instance.getNode());
   10.92 -        stmt.setString(++column, instance.getColor().getHex());
   10.93 -        stmt.setInt(++column, instance.getOrdinal());
   10.94 -        Functions.setStringOrNull(stmt, ++column, instance.getDescription());
   10.95 -        Functions.setForeignKeyOrNull(stmt, ++column, instance.getLead(), User::getId);
   10.96 -        return column;
   10.97 -    }
   10.98 -
   10.99 -    @Override
  10.100 -    public void save(Component instance, Project project) throws SQLException {
  10.101 -        int column = setColumns(insert, instance);
  10.102 -        insert.setInt(++column, project.getId());
  10.103 -        insert.executeUpdate();
  10.104 -    }
  10.105 -
  10.106 -    @Override
  10.107 -    public boolean update(Component instance) throws SQLException {
  10.108 -        if (instance.getId() < 0) return false;
  10.109 -        int column = setColumns(update, instance);
  10.110 -        update.setInt(++column, instance.getId());
  10.111 -        return update.executeUpdate() > 0;
  10.112 -    }
  10.113 -
  10.114 -
  10.115 -    @Override
  10.116 -    public List<Component> list(Project project) throws SQLException {
  10.117 -        list.setInt(1, project.getId());
  10.118 -        return Functions.list(list, PGComponentDao::mapColumns);
  10.119 -    }
  10.120 -
  10.121 -    @Override
  10.122 -    public Component find(int id) throws SQLException {
  10.123 -        find.setInt(1, id);
  10.124 -        return Functions.find(find, PGComponentDao::mapColumns);
  10.125 -    }
  10.126 -
  10.127 -    @Override
  10.128 -    public Component findByNode(Project project, String node) throws SQLException {
  10.129 -        findByNode.setInt(1, project.getId());
  10.130 -        findByNode.setString(2, node);;
  10.131 -        return Functions.find(findByNode, PGComponentDao::mapColumns);
  10.132 -    }
  10.133 -}
    11.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGDataAccessObjects.java	Fri Nov 06 10:50:32 2020 +0100
    11.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.3 @@ -1,76 +0,0 @@
    11.4 -/*
    11.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    11.6 - *
    11.7 - * Copyright 2018 Mike Becker. All rights reserved.
    11.8 - *
    11.9 - * Redistribution and use in source and binary forms, with or without
   11.10 - * modification, are permitted provided that the following conditions are met:
   11.11 - *
   11.12 - *   1. Redistributions of source code must retain the above copyright
   11.13 - *      notice, this list of conditions and the following disclaimer.
   11.14 - *
   11.15 - *   2. Redistributions in binary form must reproduce the above copyright
   11.16 - *      notice, this list of conditions and the following disclaimer in the
   11.17 - *      documentation and/or other materials provided with the distribution.
   11.18 - *
   11.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   11.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   11.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   11.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   11.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   11.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   11.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   11.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   11.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   11.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   11.29 - * POSSIBILITY OF SUCH DAMAGE.
   11.30 - *
   11.31 - */
   11.32 -package de.uapcore.lightpit.dao.postgres;
   11.33 -
   11.34 -import de.uapcore.lightpit.dao.*;
   11.35 -
   11.36 -import java.sql.Connection;
   11.37 -import java.sql.SQLException;
   11.38 -
   11.39 -public class PGDataAccessObjects implements DataAccessObjects {
   11.40 -
   11.41 -    private final UserDao userDao;
   11.42 -    private final ProjectDao projectDao;
   11.43 -    private final VersionDao versionDao;
   11.44 -    private final ComponentDao componentDao;
   11.45 -    private final IssueDao issueDao;
   11.46 -
   11.47 -    public PGDataAccessObjects(Connection connection) throws SQLException {
   11.48 -        userDao = new PGUserDao(connection);
   11.49 -        projectDao = new PGProjectDao(connection);
   11.50 -        versionDao = new PGVersionDao(connection);
   11.51 -        componentDao = new PGComponentDao(connection);
   11.52 -        issueDao = new PGIssueDao(connection);
   11.53 -    }
   11.54 -
   11.55 -    @Override
   11.56 -    public UserDao getUserDao() {
   11.57 -        return userDao;
   11.58 -    }
   11.59 -
   11.60 -    @Override
   11.61 -    public ProjectDao getProjectDao() {
   11.62 -        return projectDao;
   11.63 -    }
   11.64 -
   11.65 -    @Override
   11.66 -    public ComponentDao getComponentDao() {
   11.67 -        return componentDao;
   11.68 -    }
   11.69 -
   11.70 -    @Override
   11.71 -    public VersionDao getVersionDao() {
   11.72 -        return versionDao;
   11.73 -    }
   11.74 -
   11.75 -    @Override
   11.76 -    public IssueDao getIssueDao() {
   11.77 -        return issueDao;
   11.78 -    }
   11.79 -}
    12.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java	Fri Nov 06 10:50:32 2020 +0100
    12.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.3 @@ -1,306 +0,0 @@
    12.4 -/*
    12.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    12.6 - *
    12.7 - * Copyright 2018 Mike Becker. All rights reserved.
    12.8 - *
    12.9 - * Redistribution and use in source and binary forms, with or without
   12.10 - * modification, are permitted provided that the following conditions are met:
   12.11 - *
   12.12 - *   1. Redistributions of source code must retain the above copyright
   12.13 - *      notice, this list of conditions and the following disclaimer.
   12.14 - *
   12.15 - *   2. Redistributions in binary form must reproduce the above copyright
   12.16 - *      notice, this list of conditions and the following disclaimer in the
   12.17 - *      documentation and/or other materials provided with the distribution.
   12.18 - *
   12.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   12.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   12.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   12.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   12.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   12.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   12.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   12.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   12.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   12.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   12.29 - * POSSIBILITY OF SUCH DAMAGE.
   12.30 - *
   12.31 - */
   12.32 -package de.uapcore.lightpit.dao.postgres;
   12.33 -
   12.34 -import de.uapcore.lightpit.dao.IssueDao;
   12.35 -import de.uapcore.lightpit.entities.*;
   12.36 -
   12.37 -import java.sql.*;
   12.38 -import java.util.ArrayList;
   12.39 -import java.util.List;
   12.40 -import java.util.Objects;
   12.41 -import java.util.Optional;
   12.42 -
   12.43 -import static de.uapcore.lightpit.dao.Functions.*;
   12.44 -
   12.45 -public final class PGIssueDao implements IssueDao {
   12.46 -
   12.47 -    private final PreparedStatement insert, update, list, listForVersion, find;
   12.48 -    private final PreparedStatement affectedVersions, resolvedVersions;
   12.49 -    private final PreparedStatement clearAffected, clearResolved;
   12.50 -    private final PreparedStatement insertAffected, insertResolved;
   12.51 -    private final PreparedStatement insertComment, updateComment, listComments;
   12.52 -
   12.53 -    public PGIssueDao(Connection connection) throws SQLException {
   12.54 -        final var query = "select issueid, i.project, p.name as projectname, p.node as projectnode, "+
   12.55 -                        "component, c.name as componentname, c.node as componentnode, " +
   12.56 -                        "status, category, subject, i.description, " +
   12.57 -                        "userid, username, givenname, lastname, mail, " +
   12.58 -                        "created, updated, eta " +
   12.59 -                        "from lpit_issue i " +
   12.60 -                        "join lpit_project p on i.project = projectid " +
   12.61 -                        "left join lpit_component c on component = c.id " +
   12.62 -                        "left join lpit_user on userid = assignee ";
   12.63 -
   12.64 -        list = connection.prepareStatement(query +
   12.65 -                        "where i.project = ? and coalesce(component, -1) = coalesce(?, component, -1)");
   12.66 -
   12.67 -        listForVersion = connection.prepareStatement(
   12.68 -                "with issue_version as ( "+
   12.69 -                        "select issueid, versionid from lpit_issue_affected_version union "+
   12.70 -                        "select issueid, versionid from lpit_issue_resolved_version) "+
   12.71 -                        query +
   12.72 -                        "left join issue_version using (issueid) "+
   12.73 -                        "where i.project = ? "+
   12.74 -                        "and coalesce(versionid,-1) = ? and coalesce(component, -1) = coalesce(?, component, -1)"
   12.75 -        );
   12.76 -
   12.77 -        find = connection.prepareStatement(query + "where issueid = ? ");
   12.78 -
   12.79 -        insert = connection.prepareStatement(
   12.80 -                "insert into lpit_issue (project, component, status, category, subject, description, assignee, eta) " +
   12.81 -                        "values (?, ?, ?::issue_status, ?::issue_category, ?, ?, ?, ?) returning issueid"
   12.82 -        );
   12.83 -        update = connection.prepareStatement(
   12.84 -                "update lpit_issue set " +
   12.85 -                        "updated = now(), component = ?, status = ?::issue_status, category = ?::issue_category, " +
   12.86 -                        "subject = ?, description = ?, assignee = ?, eta = ? where issueid = ?"
   12.87 -        );
   12.88 -
   12.89 -        affectedVersions = connection.prepareStatement(
   12.90 -                "select versionid, name, status, ordinal " +
   12.91 -                        "from lpit_version join lpit_issue_affected_version using (versionid) " +
   12.92 -                        "where issueid = ? " +
   12.93 -                        "order by ordinal, name"
   12.94 -        );
   12.95 -        clearAffected = connection.prepareStatement("delete from lpit_issue_affected_version where issueid = ?");
   12.96 -        insertAffected = connection.prepareStatement("insert into lpit_issue_affected_version (issueid, versionid) values (?,?)");
   12.97 -
   12.98 -        resolvedVersions = connection.prepareStatement(
   12.99 -                "select versionid, name, status, ordinal " +
  12.100 -                        "from lpit_version v join lpit_issue_resolved_version using (versionid) " +
  12.101 -                        "where issueid = ? " +
  12.102 -                        "order by ordinal, name"
  12.103 -        );
  12.104 -        clearResolved = connection.prepareStatement("delete from lpit_issue_resolved_version where issueid = ?");
  12.105 -        insertResolved = connection.prepareStatement("insert into lpit_issue_resolved_version (issueid, versionid) values (?,?)");
  12.106 -
  12.107 -        insertComment = connection.prepareStatement(
  12.108 -                "insert into lpit_issue_comment (issueid, comment, userid) values (?, ? ,?)"
  12.109 -        );
  12.110 -        updateComment = connection.prepareStatement(
  12.111 -                "update lpit_issue_comment set comment = ?, updated = now(), updatecount = updatecount+1 where commentid = ?"
  12.112 -        );
  12.113 -        listComments = connection.prepareStatement(
  12.114 -                "select * from lpit_issue_comment left join lpit_user using (userid) where issueid = ? order by created"
  12.115 -        );
  12.116 -    }
  12.117 -
  12.118 -    private Issue mapColumns(ResultSet result) throws SQLException {
  12.119 -        final var project = new Project(result.getInt("project"));
  12.120 -        project.setName(result.getString("projectname"));
  12.121 -        project.setNode(result.getString("projectnode"));
  12.122 -        var component = new Component(result.getInt("component"));
  12.123 -        if (result.wasNull()) {
  12.124 -            component = null;
  12.125 -        } else {
  12.126 -            component.setName(result.getString("componentname"));
  12.127 -            component.setNode(result.getString("componentnode"));
  12.128 -        }
  12.129 -        final var issue = new Issue(result.getInt("issueid"));
  12.130 -        issue.setProject(project);
  12.131 -        issue.setComponent(component);
  12.132 -        issue.setStatus(IssueStatus.valueOf(result.getString("status")));
  12.133 -        issue.setCategory(IssueCategory.valueOf(result.getString("category")));
  12.134 -        issue.setSubject(result.getString("subject"));
  12.135 -        issue.setDescription(result.getString("description"));
  12.136 -        issue.setAssignee(PGUserDao.mapColumns(result));
  12.137 -        issue.setCreated(result.getTimestamp("created"));
  12.138 -        issue.setUpdated(result.getTimestamp("updated"));
  12.139 -        issue.setEta(result.getDate("eta"));
  12.140 -        return issue;
  12.141 -    }
  12.142 -
  12.143 -    private Version mapVersion(ResultSet result) throws SQLException {
  12.144 -        final var version = new Version(result.getInt("versionid"));
  12.145 -        version.setName(result.getString("name"));
  12.146 -        version.setOrdinal(result.getInt("ordinal"));
  12.147 -        version.setStatus(VersionStatus.valueOf(result.getString("status")));
  12.148 -        return version;
  12.149 -    }
  12.150 -
  12.151 -    private void updateVersionLists(Issue instance) throws SQLException {
  12.152 -        clearAffected.setInt(1, instance.getId());
  12.153 -        clearResolved.setInt(1, instance.getId());
  12.154 -        insertAffected.setInt(1, instance.getId());
  12.155 -        insertResolved.setInt(1, instance.getId());
  12.156 -        clearAffected.executeUpdate();
  12.157 -        clearResolved.executeUpdate();
  12.158 -        for (Version v : instance.getAffectedVersions()) {
  12.159 -            insertAffected.setInt(2, v.getId());
  12.160 -            insertAffected.executeUpdate();
  12.161 -        }
  12.162 -        for (Version v : instance.getResolvedVersions()) {
  12.163 -            insertResolved.setInt(2, v.getId());
  12.164 -            insertResolved.executeUpdate();
  12.165 -        }
  12.166 -    }
  12.167 -
  12.168 -    private int setData(PreparedStatement stmt, int column, Issue instance) throws SQLException {
  12.169 -        setForeignKeyOrNull(stmt, ++column, instance.getComponent(), Component::getId);
  12.170 -        stmt.setString(++column, instance.getStatus().name());
  12.171 -        stmt.setString(++column, instance.getCategory().name());
  12.172 -        stmt.setString(++column, instance.getSubject());
  12.173 -        setStringOrNull(stmt, ++column, instance.getDescription());
  12.174 -        setForeignKeyOrNull(stmt, ++column, instance.getAssignee(), User::getId);
  12.175 -        setDateOrNull(stmt, ++column, instance.getEta());
  12.176 -        return column;
  12.177 -    }
  12.178 -
  12.179 -    @Override
  12.180 -    public void save(Issue instance, Project project) throws SQLException {
  12.181 -        Objects.requireNonNull(instance.getSubject());
  12.182 -        instance.setProject(project);
  12.183 -        int column = 0;
  12.184 -        insert.setInt(++column, instance.getProject().getId());
  12.185 -        setData(insert, column, instance);
  12.186 -        // insert and retrieve the ID
  12.187 -        final var rs = insert.executeQuery();
  12.188 -        rs.next();
  12.189 -        instance.setId(rs.getInt(1));
  12.190 -        updateVersionLists(instance);
  12.191 -    }
  12.192 -
  12.193 -    @Override
  12.194 -    public boolean update(Issue instance) throws SQLException {
  12.195 -        if (instance.getId() < 0) return false;
  12.196 -        Objects.requireNonNull(instance.getSubject());
  12.197 -        int column = setData(update, 0, instance);
  12.198 -        update.setInt(++column, instance.getId());
  12.199 -        boolean success = update.executeUpdate() > 0;
  12.200 -        if (success) {
  12.201 -            updateVersionLists(instance);
  12.202 -            return true;
  12.203 -        } else {
  12.204 -            return false;
  12.205 -        }
  12.206 -    }
  12.207 -
  12.208 -    private List<Issue> executeQuery(PreparedStatement query) throws SQLException {
  12.209 -        List<Issue> issues = new ArrayList<>();
  12.210 -        try (var result = query.executeQuery()) {
  12.211 -            while (result.next()) {
  12.212 -                issues.add(mapColumns(result));
  12.213 -            }
  12.214 -        }
  12.215 -        return issues;
  12.216 -    }
  12.217 -
  12.218 -    @Override
  12.219 -    public List<Issue> list(Project project) throws SQLException {
  12.220 -        list.setInt(1, project.getId());
  12.221 -        list.setNull(2, Types.INTEGER);
  12.222 -        return executeQuery(list);
  12.223 -    }
  12.224 -
  12.225 -    @Override
  12.226 -    public List<Issue> list(Project project, Component component, Version version) throws SQLException {
  12.227 -        listForVersion.setInt(1, project.getId());
  12.228 -        listForVersion.setInt(2, Optional.ofNullable(version).map(Version::getId).orElse(-1));
  12.229 -        listForVersion.setInt(3, Optional.ofNullable(component).map(Component::getId).orElse(-1));
  12.230 -        return executeQuery(listForVersion);
  12.231 -    }
  12.232 -
  12.233 -    @Override
  12.234 -    public List<Issue> list(Project project, Version version) throws SQLException {
  12.235 -        listForVersion.setInt(1, project.getId());
  12.236 -        listForVersion.setInt(2, Optional.ofNullable(version).map(Version::getId).orElse(-1));
  12.237 -        listForVersion.setNull(3, Types.INTEGER);
  12.238 -        return executeQuery(listForVersion);
  12.239 -    }
  12.240 -
  12.241 -    @Override
  12.242 -    public List<Issue> list(Project project, Component component) throws SQLException {
  12.243 -        list.setInt(1, project.getId());
  12.244 -        list.setInt(2, Optional.ofNullable(component).map(Component::getId).orElse(-1));
  12.245 -        return executeQuery(list);
  12.246 -    }
  12.247 -
  12.248 -    @Override
  12.249 -    public Issue find(int id) throws SQLException {
  12.250 -        find.setInt(1, id);
  12.251 -        try (var result = find.executeQuery()) {
  12.252 -            if (result.next()) {
  12.253 -                return mapColumns(result);
  12.254 -            } else {
  12.255 -                return null;
  12.256 -            }
  12.257 -        }
  12.258 -    }
  12.259 -
  12.260 -    private List<Version> listVersions(PreparedStatement stmt, Issue issue) throws SQLException {
  12.261 -        stmt.setInt(1, issue.getId());
  12.262 -        List<Version> versions = new ArrayList<>();
  12.263 -        try (var result = stmt.executeQuery()) {
  12.264 -            while (result.next()) {
  12.265 -                versions.add(mapVersion(result));
  12.266 -            }
  12.267 -        }
  12.268 -        return versions;
  12.269 -    }
  12.270 -
  12.271 -    @Override
  12.272 -    public void joinVersionInformation(Issue issue) throws SQLException {
  12.273 -        Objects.requireNonNull(issue.getProject());
  12.274 -        issue.setAffectedVersions(listVersions(affectedVersions, issue));
  12.275 -        issue.setResolvedVersions(listVersions(resolvedVersions, issue));
  12.276 -    }
  12.277 -
  12.278 -    @Override
  12.279 -    public List<IssueComment> listComments(Issue issue) throws SQLException {
  12.280 -        listComments.setInt(1, issue.getId());
  12.281 -        List<IssueComment> comments = new ArrayList<>();
  12.282 -        try (var result = listComments.executeQuery()) {
  12.283 -            while (result.next()) {
  12.284 -                final var comment = new IssueComment(result.getInt("commentid"));
  12.285 -                comment.setCreated(result.getTimestamp("created"));
  12.286 -                comment.setUpdated(result.getTimestamp("updated"));
  12.287 -                comment.setUpdateCount(result.getInt("updatecount"));
  12.288 -                comment.setComment(result.getString("comment"));
  12.289 -                comment.setAuthor(PGUserDao.mapColumns(result));
  12.290 -                comments.add(comment);
  12.291 -            }
  12.292 -        }
  12.293 -        return comments;
  12.294 -    }
  12.295 -
  12.296 -    @Override
  12.297 -    public void saveComment(Issue issue, IssueComment comment) throws SQLException {
  12.298 -        if (comment.getId() >= 0) {
  12.299 -            updateComment.setString(1, comment.getComment());
  12.300 -            updateComment.setInt(2, comment.getId());
  12.301 -            updateComment.execute();
  12.302 -        } else {
  12.303 -            insertComment.setInt(1, issue.getId());
  12.304 -            insertComment.setString(2, comment.getComment());
  12.305 -            setForeignKeyOrNull(insertComment, 3, comment.getAuthor(), User::getId);
  12.306 -            insertComment.execute();
  12.307 -        }
  12.308 -    }
  12.309 -}
    13.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java	Fri Nov 06 10:50:32 2020 +0100
    13.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.3 @@ -1,151 +0,0 @@
    13.4 -/*
    13.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    13.6 - *
    13.7 - * Copyright 2018 Mike Becker. All rights reserved.
    13.8 - *
    13.9 - * Redistribution and use in source and binary forms, with or without
   13.10 - * modification, are permitted provided that the following conditions are met:
   13.11 - *
   13.12 - *   1. Redistributions of source code must retain the above copyright
   13.13 - *      notice, this list of conditions and the following disclaimer.
   13.14 - *
   13.15 - *   2. Redistributions in binary form must reproduce the above copyright
   13.16 - *      notice, this list of conditions and the following disclaimer in the
   13.17 - *      documentation and/or other materials provided with the distribution.
   13.18 - *
   13.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   13.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   13.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   13.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   13.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   13.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   13.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   13.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   13.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   13.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   13.29 - * POSSIBILITY OF SUCH DAMAGE.
   13.30 - *
   13.31 - */
   13.32 -package de.uapcore.lightpit.dao.postgres;
   13.33 -
   13.34 -import de.uapcore.lightpit.dao.Functions;
   13.35 -import de.uapcore.lightpit.dao.ProjectDao;
   13.36 -import de.uapcore.lightpit.entities.IssueSummary;
   13.37 -import de.uapcore.lightpit.entities.Project;
   13.38 -import de.uapcore.lightpit.entities.User;
   13.39 -
   13.40 -import java.sql.Connection;
   13.41 -import java.sql.PreparedStatement;
   13.42 -import java.sql.ResultSet;
   13.43 -import java.sql.SQLException;
   13.44 -import java.util.List;
   13.45 -
   13.46 -import static de.uapcore.lightpit.dao.Functions.setForeignKeyOrNull;
   13.47 -import static de.uapcore.lightpit.dao.Functions.setStringOrNull;
   13.48 -
   13.49 -public final class PGProjectDao implements ProjectDao {
   13.50 -
   13.51 -    private final PreparedStatement insert, update, list, find, findByNode;
   13.52 -    private final PreparedStatement issue_summary;
   13.53 -
   13.54 -    public PGProjectDao(Connection connection) throws SQLException {
   13.55 -        final var query = "select projectid, name, node, description, repourl, " +
   13.56 -                "userid, username, lastname, givenname, mail " +
   13.57 -                "from lpit_project " +
   13.58 -                "left join lpit_user owner on lpit_project.owner = owner.userid ";
   13.59 -
   13.60 -        list = connection.prepareStatement(query + " order by name");
   13.61 -
   13.62 -        find = connection.prepareStatement(query + " where projectid = ?");
   13.63 -        findByNode = connection.prepareStatement(query + " where node = ?");
   13.64 -
   13.65 -        issue_summary = connection.prepareStatement(
   13.66 -                "select phase, count(*) as total "+
   13.67 -                        "from lpit_issue " +
   13.68 -                        "join lpit_issue_phases using(status) " +
   13.69 -                        "where project = ? "+
   13.70 -                        "group by phase "
   13.71 -        );
   13.72 -
   13.73 -        insert = connection.prepareStatement(
   13.74 -                "insert into lpit_project (name, node, description, repourl, owner) values (?, ?, ?, ?, ?)"
   13.75 -        );
   13.76 -        update = connection.prepareStatement(
   13.77 -                "update lpit_project set name = ?, node = ?, description = ?, repourl = ?, owner = ? where projectid = ?"
   13.78 -        );
   13.79 -    }
   13.80 -
   13.81 -    private static Project mapColumns(ResultSet result) throws SQLException {
   13.82 -        final var proj = new Project(result.getInt("projectid"));
   13.83 -        proj.setName(result.getString("name"));
   13.84 -        proj.setNode(result.getString("node"));
   13.85 -        proj.setDescription(result.getString("description"));
   13.86 -        proj.setRepoUrl(result.getString("repourl"));
   13.87 -        proj.setOwner(PGUserDao.mapColumns(result));
   13.88 -
   13.89 -        return proj;
   13.90 -    }
   13.91 -
   13.92 -    public IssueSummary getIssueSummary(Project project) throws SQLException {
   13.93 -        issue_summary.setInt(1, project.getId());
   13.94 -        final var result = issue_summary.executeQuery();
   13.95 -        final var summary = new IssueSummary();
   13.96 -        while (result.next()) {
   13.97 -            final var phase = result.getInt("phase");
   13.98 -            final var total = result.getInt("total");
   13.99 -            switch(phase) {
  13.100 -                case 0:
  13.101 -                    summary.setOpen(total);
  13.102 -                    break;
  13.103 -                case 1:
  13.104 -                    summary.setActive(total);
  13.105 -                    break;
  13.106 -                case 2:
  13.107 -                    summary.setDone(total);
  13.108 -                    break;
  13.109 -            }
  13.110 -        }
  13.111 -        return summary;
  13.112 -    }
  13.113 -
  13.114 -    private static int setColumns(PreparedStatement stmt, Project instance) throws SQLException {
  13.115 -        int column = 0;
  13.116 -        stmt.setString(++column, instance.getName());
  13.117 -        stmt.setString(++column, instance.getNode());
  13.118 -        setStringOrNull(stmt, ++column, instance.getDescription());
  13.119 -        setStringOrNull(stmt, ++column, instance.getRepoUrl());
  13.120 -        setForeignKeyOrNull(stmt, ++column, instance.getOwner(), User::getId);
  13.121 -        return column;
  13.122 -    }
  13.123 -
  13.124 -    @Override
  13.125 -    public void save(Project instance) throws SQLException {
  13.126 -        setColumns(insert, instance);
  13.127 -        insert.executeUpdate();
  13.128 -    }
  13.129 -
  13.130 -    @Override
  13.131 -    public boolean update(Project instance) throws SQLException {
  13.132 -        if (instance.getId() < 0) return false;
  13.133 -        int column = setColumns(update, instance);
  13.134 -        update.setInt(++column, instance.getId());
  13.135 -        return update.executeUpdate() > 0;
  13.136 -    }
  13.137 -
  13.138 -    @Override
  13.139 -    public List<Project> list() throws SQLException {
  13.140 -        return Functions.list(list, PGProjectDao::mapColumns);
  13.141 -    }
  13.142 -
  13.143 -    @Override
  13.144 -    public Project find(int id) throws SQLException {
  13.145 -        find.setInt(1, id);
  13.146 -        return Functions.find(find, PGProjectDao::mapColumns);
  13.147 -    }
  13.148 -
  13.149 -    @Override
  13.150 -    public Project findByNode(String node) throws SQLException {
  13.151 -        findByNode.setString(1, node);
  13.152 -        return Functions.find(findByNode, PGProjectDao::mapColumns);
  13.153 -    }
  13.154 -}
    14.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGUserDao.java	Fri Nov 06 10:50:32 2020 +0100
    14.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.3 @@ -1,132 +0,0 @@
    14.4 -/*
    14.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    14.6 - *
    14.7 - * Copyright 2018 Mike Becker. All rights reserved.
    14.8 - *
    14.9 - * Redistribution and use in source and binary forms, with or without
   14.10 - * modification, are permitted provided that the following conditions are met:
   14.11 - *
   14.12 - *   1. Redistributions of source code must retain the above copyright
   14.13 - *      notice, this list of conditions and the following disclaimer.
   14.14 - *
   14.15 - *   2. Redistributions in binary form must reproduce the above copyright
   14.16 - *      notice, this list of conditions and the following disclaimer in the
   14.17 - *      documentation and/or other materials provided with the distribution.
   14.18 - *
   14.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   14.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   14.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   14.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   14.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   14.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   14.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   14.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   14.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   14.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   14.29 - * POSSIBILITY OF SUCH DAMAGE.
   14.30 - *
   14.31 - */
   14.32 -package de.uapcore.lightpit.dao.postgres;
   14.33 -
   14.34 -import de.uapcore.lightpit.dao.UserDao;
   14.35 -import de.uapcore.lightpit.entities.User;
   14.36 -
   14.37 -import java.sql.Connection;
   14.38 -import java.sql.PreparedStatement;
   14.39 -import java.sql.ResultSet;
   14.40 -import java.sql.SQLException;
   14.41 -import java.util.ArrayList;
   14.42 -import java.util.List;
   14.43 -import java.util.Objects;
   14.44 -import java.util.Optional;
   14.45 -
   14.46 -import static de.uapcore.lightpit.dao.Functions.getSafeString;
   14.47 -import static de.uapcore.lightpit.dao.Functions.setStringOrNull;
   14.48 -
   14.49 -public final class PGUserDao implements UserDao {
   14.50 -
   14.51 -    private final PreparedStatement insert, update, list, find, findByUsername;
   14.52 -
   14.53 -    public PGUserDao(Connection connection) throws SQLException {
   14.54 -        list = connection.prepareStatement(
   14.55 -                "select userid, username, lastname, givenname, mail " +
   14.56 -                        "from lpit_user where userid >= 0 " +
   14.57 -                        "order by username");
   14.58 -        find = connection.prepareStatement(
   14.59 -                "select userid, username, lastname, givenname, mail " +
   14.60 -                        "from lpit_user where userid = ? ");
   14.61 -
   14.62 -        findByUsername = connection.prepareStatement(
   14.63 -                "select userid, username, lastname, givenname, mail " +
   14.64 -                        "from lpit_user where lower(username) = lower(?) ");
   14.65 -
   14.66 -        insert = connection.prepareStatement("insert into lpit_user (username, lastname, givenname, mail) values (?, ?, ?, ?)");
   14.67 -        update = connection.prepareStatement("update lpit_user set lastname = ?, givenname = ?, mail = ? where userid = ?");
   14.68 -    }
   14.69 -
   14.70 -    static User mapColumns(ResultSet result) throws SQLException {
   14.71 -        final int id = result.getInt("userid");
   14.72 -        if (id == 0) return null;
   14.73 -        final var user = new User(id);
   14.74 -        user.setUsername(result.getString("username"));
   14.75 -        user.setGivenname(getSafeString(result, "givenname"));
   14.76 -        user.setLastname(getSafeString(result, "lastname"));
   14.77 -        user.setMail(getSafeString(result, "mail"));
   14.78 -        return user;
   14.79 -    }
   14.80 -
   14.81 -    @Override
   14.82 -    public void save(User instance) throws SQLException {
   14.83 -        Objects.requireNonNull(instance.getUsername());
   14.84 -        insert.setString(1, instance.getUsername());
   14.85 -        setStringOrNull(insert, 2, instance.getLastname());
   14.86 -        setStringOrNull(insert, 3, instance.getGivenname());
   14.87 -        setStringOrNull(insert, 4, instance.getMail());
   14.88 -        insert.executeUpdate();
   14.89 -    }
   14.90 -
   14.91 -    @Override
   14.92 -    public boolean update(User instance) throws SQLException {
   14.93 -        if (instance.getId() < 0) return false;
   14.94 -        setStringOrNull(update, 1, instance.getLastname());
   14.95 -        setStringOrNull(update, 2, instance.getGivenname());
   14.96 -        setStringOrNull(update, 3, instance.getMail());
   14.97 -        update.setInt(4, instance.getId());
   14.98 -        return update.executeUpdate() > 0;
   14.99 -    }
  14.100 -
  14.101 -    @Override
  14.102 -    public List<User> list() throws SQLException {
  14.103 -        List<User> users = new ArrayList<>();
  14.104 -        try (var result = list.executeQuery()) {
  14.105 -            while (result.next()) {
  14.106 -                users.add(mapColumns(result));
  14.107 -            }
  14.108 -        }
  14.109 -        return users;
  14.110 -    }
  14.111 -
  14.112 -    @Override
  14.113 -    public User find(int id) throws SQLException {
  14.114 -        find.setInt(1, id);
  14.115 -        try (var result = find.executeQuery()) {
  14.116 -            if (result.next()) {
  14.117 -                return mapColumns(result);
  14.118 -            } else {
  14.119 -                return null;
  14.120 -            }
  14.121 -        }
  14.122 -    }
  14.123 -
  14.124 -    @Override
  14.125 -    public Optional<User> findByUsername(String username) throws SQLException {
  14.126 -        findByUsername.setString(1, username);
  14.127 -        try (var result = findByUsername.executeQuery()) {
  14.128 -            if (result.next()) {
  14.129 -                return Optional.of(mapColumns(result));
  14.130 -            } else {
  14.131 -                return Optional.empty();
  14.132 -            }
  14.133 -        }
  14.134 -    }
  14.135 -}
    15.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java	Fri Nov 06 10:50:32 2020 +0100
    15.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.3 @@ -1,114 +0,0 @@
    15.4 -/*
    15.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    15.6 - *
    15.7 - * Copyright 2018 Mike Becker. All rights reserved.
    15.8 - *
    15.9 - * Redistribution and use in source and binary forms, with or without
   15.10 - * modification, are permitted provided that the following conditions are met:
   15.11 - *
   15.12 - *   1. Redistributions of source code must retain the above copyright
   15.13 - *      notice, this list of conditions and the following disclaimer.
   15.14 - *
   15.15 - *   2. Redistributions in binary form must reproduce the above copyright
   15.16 - *      notice, this list of conditions and the following disclaimer in the
   15.17 - *      documentation and/or other materials provided with the distribution.
   15.18 - *
   15.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   15.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   15.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   15.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   15.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   15.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   15.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   15.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   15.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   15.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   15.29 - * POSSIBILITY OF SUCH DAMAGE.
   15.30 - *
   15.31 - */
   15.32 -package de.uapcore.lightpit.dao.postgres;
   15.33 -
   15.34 -import de.uapcore.lightpit.dao.Functions;
   15.35 -import de.uapcore.lightpit.dao.VersionDao;
   15.36 -import de.uapcore.lightpit.entities.Project;
   15.37 -import de.uapcore.lightpit.entities.Version;
   15.38 -import de.uapcore.lightpit.entities.VersionStatus;
   15.39 -
   15.40 -import java.sql.Connection;
   15.41 -import java.sql.PreparedStatement;
   15.42 -import java.sql.ResultSet;
   15.43 -import java.sql.SQLException;
   15.44 -import java.util.List;
   15.45 -
   15.46 -public final class PGVersionDao implements VersionDao {
   15.47 -
   15.48 -    private final PreparedStatement insert, update, list, find, findByNode;
   15.49 -
   15.50 -    public PGVersionDao(Connection connection) throws SQLException {
   15.51 -        final var query = "select versionid, project, name, node, ordinal, status from lpit_version";
   15.52 -
   15.53 -        list = connection.prepareStatement(query + " where project = ? " +
   15.54 -                        "order by ordinal desc, lower(name) desc");
   15.55 -        find = connection.prepareStatement(query + " where versionid = ?");
   15.56 -        findByNode = connection.prepareStatement(query + " where project = ? and node = ?");
   15.57 -
   15.58 -        insert = connection.prepareStatement(
   15.59 -                "insert into lpit_version (name, node, ordinal, status, project) values (?, ?, ?, ?::version_status, ?)"
   15.60 -        );
   15.61 -        update = connection.prepareStatement(
   15.62 -                "update lpit_version set name = ?, node = ?, ordinal = ?, status = ?::version_status where versionid = ?"
   15.63 -        );
   15.64 -    }
   15.65 -
   15.66 -    private static Version mapColumns(ResultSet result) throws SQLException {
   15.67 -        final var version = new Version(result.getInt("versionid"));
   15.68 -        version.setName(result.getString("name"));
   15.69 -        version.setNode(result.getString("node"));
   15.70 -        version.setOrdinal(result.getInt("ordinal"));
   15.71 -        version.setStatus(VersionStatus.valueOf(result.getString("status")));
   15.72 -        return version;
   15.73 -    }
   15.74 -
   15.75 -    private static int setFields(PreparedStatement stmt, Version instance) throws SQLException {
   15.76 -        int column = 0;
   15.77 -        stmt.setString(++column, instance.getName());
   15.78 -        stmt.setString(++column, instance.getNode());
   15.79 -        stmt.setInt(++column, instance.getOrdinal());
   15.80 -        stmt.setString(++column, instance.getStatus().name());
   15.81 -        return column;
   15.82 -    }
   15.83 -
   15.84 -    @Override
   15.85 -    public void save(Version instance, Project project) throws SQLException {
   15.86 -        int column = setFields(insert, instance);
   15.87 -        insert.setInt(++column, project.getId());
   15.88 -        insert.executeUpdate();
   15.89 -    }
   15.90 -
   15.91 -    @Override
   15.92 -    public boolean update(Version instance) throws SQLException {
   15.93 -        if (instance.getId() < 0) return false;
   15.94 -        int column = setFields(update, instance);
   15.95 -        update.setInt(++column, instance.getId());
   15.96 -        return update.executeUpdate() > 0;
   15.97 -    }
   15.98 -
   15.99 -    @Override
  15.100 -    public List<Version> list(Project project) throws SQLException {
  15.101 -        list.setInt(1, project.getId());
  15.102 -        return Functions.list(list, PGVersionDao::mapColumns);
  15.103 -    }
  15.104 -
  15.105 -    @Override
  15.106 -    public Version find(int id) throws SQLException {
  15.107 -        find.setInt(1, id);
  15.108 -        return Functions.find(find, PGVersionDao::mapColumns);
  15.109 -    }
  15.110 -
  15.111 -    @Override
  15.112 -    public Version findByNode(Project project, String node) throws SQLException {
  15.113 -        findByNode.setInt(1, project.getId());
  15.114 -        findByNode.setString(2, node);;
  15.115 -        return Functions.find(findByNode, PGVersionDao::mapColumns);
  15.116 -    }
  15.117 -}
    16.1 --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java	Fri Nov 06 10:50:32 2020 +0100
    16.2 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java	Thu Nov 19 13:58:54 2020 +0100
    16.3 @@ -30,7 +30,7 @@
    16.4  
    16.5  
    16.6  import de.uapcore.lightpit.*;
    16.7 -import de.uapcore.lightpit.dao.DataAccessObjects;
    16.8 +import de.uapcore.lightpit.dao.DaoProvider;
    16.9  import de.uapcore.lightpit.entities.*;
   16.10  import de.uapcore.lightpit.types.WebColor;
   16.11  import de.uapcore.lightpit.viewmodel.*;
   16.12 @@ -72,7 +72,7 @@
   16.13          }
   16.14      }
   16.15  
   16.16 -    private void populate(ProjectView viewModel, PathParameters pathParameters, DataAccessObjects dao) throws SQLException {
   16.17 +    private void populate(ProjectView viewModel, PathParameters pathParameters, DaoProvider dao) throws SQLException {
   16.18          final var projectDao = dao.getProjectDao();
   16.19          final var versionDao = dao.getVersionDao();
   16.20          final var componentDao = dao.getComponentDao();
   16.21 @@ -97,7 +97,7 @@
   16.22          final var versionNode = pathParameters.get("version");
   16.23          if ("no-version".equals(versionNode)) {
   16.24              viewModel.setVersionFilter(ProjectView.NO_VERSION);
   16.25 -        } else if ("all-versions".equals(versionNode)) {
   16.26 +        } else if ("all-versions".equals(versionNode) || versionNode == null) {
   16.27              viewModel.setVersionFilter(ProjectView.ALL_VERSIONS);
   16.28          } else {
   16.29              viewModel.setVersionFilter(versionDao.findByNode(project, versionNode));
   16.30 @@ -107,7 +107,7 @@
   16.31          final var componentNode = pathParameters.get("component");
   16.32          if ("no-component".equals(componentNode)) {
   16.33              viewModel.setComponentFilter(ProjectView.NO_COMPONENT);
   16.34 -        } else if ("all-components".equals(componentNode)) {
   16.35 +        } else if ("all-components".equals(componentNode) || componentNode == null) {
   16.36              viewModel.setComponentFilter(ProjectView.ALL_COMPONENTS);
   16.37          } else {
   16.38              viewModel.setComponentFilter(componentDao.findByNode(project, componentNode));
   16.39 @@ -133,7 +133,7 @@
   16.40      }
   16.41  
   16.42      @RequestMapping(method = HttpMethod.GET)
   16.43 -    public void index(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, ServletException, IOException {
   16.44 +    public void index(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws SQLException, ServletException, IOException {
   16.45          final var viewModel = new ProjectView();
   16.46          populate(viewModel, null, dao);
   16.47  
   16.48 @@ -148,13 +148,13 @@
   16.49          forwardView(req, resp, viewModel, "projects");
   16.50      }
   16.51  
   16.52 -    private void configureProjectEditor(ProjectEditView viewModel, Project project, DataAccessObjects dao) throws SQLException {
   16.53 +    private void configureProjectEditor(ProjectEditView viewModel, Project project, DaoProvider dao) throws SQLException {
   16.54          viewModel.setProject(project);
   16.55          viewModel.setUsers(dao.getUserDao().list());
   16.56      }
   16.57  
   16.58      @RequestMapping(requestPath = "$project/edit", method = HttpMethod.GET)
   16.59 -    public void edit(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws IOException, SQLException, ServletException {
   16.60 +    public void edit(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DaoProvider dao) throws IOException, SQLException, ServletException {
   16.61          final var viewModel = new ProjectEditView();
   16.62          populate(viewModel, pathParams, dao);
   16.63  
   16.64 @@ -168,7 +168,7 @@
   16.65      }
   16.66  
   16.67      @RequestMapping(requestPath = "create", method = HttpMethod.GET)
   16.68 -    public void create(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, ServletException, IOException {
   16.69 +    public void create(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws SQLException, ServletException, IOException {
   16.70          final var viewModel = new ProjectEditView();
   16.71          populate(viewModel, null, dao);
   16.72          configureProjectEditor(viewModel, new Project(-1), dao);
   16.73 @@ -176,7 +176,7 @@
   16.74      }
   16.75  
   16.76      @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
   16.77 -    public void commit(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, ServletException {
   16.78 +    public void commit(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws IOException, ServletException {
   16.79  
   16.80          try {
   16.81              final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow());
   16.82 @@ -191,21 +191,27 @@
   16.83                      ownerId -> ownerId >= 0 ? new User(ownerId) : null
   16.84              ).ifPresent(project::setOwner);
   16.85  
   16.86 -            dao.getProjectDao().saveOrUpdate(project);
   16.87 +            final var projectDao = dao.getProjectDao();
   16.88 +            if (project.getId() > 0) {
   16.89 +                // TODO: unused return value
   16.90 +                projectDao.update(project);
   16.91 +            } else {
   16.92 +                projectDao.save(project);
   16.93 +            }
   16.94  
   16.95              setRedirectLocation(req, "./projects/");
   16.96              setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   16.97              LOG.debug("Successfully updated project {}", project.getName());
   16.98  
   16.99              renderSite(req, resp);
  16.100 -        } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
  16.101 +        } catch (NoSuchElementException | IllegalArgumentException ex) {
  16.102              resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
  16.103              // TODO: implement - fix issue #21
  16.104          }
  16.105      }
  16.106  
  16.107      @RequestMapping(requestPath = "$project/$component/$version/issues/", method = HttpMethod.GET)
  16.108 -    public void issues(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws SQLException, IOException, ServletException {
  16.109 +    public void issues(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DaoProvider dao) throws SQLException, IOException, ServletException {
  16.110          final var viewModel = new ProjectDetailsView();
  16.111          populate(viewModel, pathParams, dao);
  16.112  
  16.113 @@ -263,7 +269,7 @@
  16.114      }
  16.115  
  16.116      @RequestMapping(requestPath = "$project/versions/", method = HttpMethod.GET)
  16.117 -    public void versions(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
  16.118 +    public void versions(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException {
  16.119          final var viewModel = new VersionsView();
  16.120          populate(viewModel, pathParameters, dao);
  16.121  
  16.122 @@ -282,7 +288,7 @@
  16.123      }
  16.124  
  16.125      @RequestMapping(requestPath = "$project/versions/$version/edit", method = HttpMethod.GET)
  16.126 -    public void editVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
  16.127 +    public void editVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException {
  16.128          final var viewModel = new VersionEditView();
  16.129          populate(viewModel, pathParameters, dao);
  16.130  
  16.131 @@ -297,7 +303,7 @@
  16.132      }
  16.133  
  16.134      @RequestMapping(requestPath = "$project/create-version", method = HttpMethod.GET)
  16.135 -    public void createVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
  16.136 +    public void createVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException {
  16.137          final var viewModel = new VersionEditView();
  16.138          populate(viewModel, pathParameters, dao);
  16.139  
  16.140 @@ -312,7 +318,7 @@
  16.141      }
  16.142  
  16.143      @RequestMapping(requestPath = "commit-version", method = HttpMethod.POST)
  16.144 -    public void commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, ServletException {
  16.145 +    public void commitVersion(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws IOException, ServletException {
  16.146  
  16.147          try {
  16.148              final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow());
  16.149 @@ -329,20 +335,27 @@
  16.150  
  16.151              getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
  16.152              version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
  16.153 -            dao.getVersionDao().saveOrUpdate(version, project);
  16.154 +
  16.155 +            final var versionDao = dao.getVersionDao();
  16.156 +            if (version.getId() > 0) {
  16.157 +                // TODO: use return value
  16.158 +                versionDao.update(version);
  16.159 +            } else {
  16.160 +                versionDao.save(version, project);
  16.161 +            }
  16.162  
  16.163              setRedirectLocation(req, "./projects/" + project.getNode() + "/versions/");
  16.164              setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
  16.165  
  16.166              renderSite(req, resp);
  16.167 -        } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
  16.168 +        } catch (NoSuchElementException | IllegalArgumentException ex) {
  16.169              resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
  16.170              // TODO: implement - fix issue #21
  16.171          }
  16.172      }
  16.173  
  16.174      @RequestMapping(requestPath = "$project/components/", method = HttpMethod.GET)
  16.175 -    public void components(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
  16.176 +    public void components(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException {
  16.177          final var viewModel = new ComponentsView();
  16.178          populate(viewModel, pathParameters, dao);
  16.179  
  16.180 @@ -360,7 +373,7 @@
  16.181      }
  16.182  
  16.183      @RequestMapping(requestPath = "$project/components/$component/edit", method = HttpMethod.GET)
  16.184 -    public void editComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
  16.185 +    public void editComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException {
  16.186          final var viewModel = new ComponentEditView();
  16.187          populate(viewModel, pathParameters, dao);
  16.188  
  16.189 @@ -376,7 +389,7 @@
  16.190      }
  16.191  
  16.192      @RequestMapping(requestPath = "$project/create-component", method = HttpMethod.GET)
  16.193 -    public void createComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
  16.194 +    public void createComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException {
  16.195          final var viewModel = new ComponentEditView();
  16.196          populate(viewModel, pathParameters, dao);
  16.197  
  16.198 @@ -392,7 +405,7 @@
  16.199      }
  16.200  
  16.201      @RequestMapping(requestPath = "commit-component", method = HttpMethod.POST)
  16.202 -    public void commitComponent(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, ServletException {
  16.203 +    public void commitComponent(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws IOException, ServletException {
  16.204  
  16.205          try {
  16.206              final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow());
  16.207 @@ -414,19 +427,25 @@
  16.208              ).ifPresent(component::setLead);
  16.209              getParameter(req, String.class, "description").ifPresent(component::setDescription);
  16.210  
  16.211 -            dao.getComponentDao().saveOrUpdate(component, project);
  16.212 +            final var componentDao = dao.getComponentDao();
  16.213 +            if (component.getId() > 0) {
  16.214 +                // TODO: use return value
  16.215 +                componentDao.update(component);
  16.216 +            } else {
  16.217 +                componentDao.save(component, project);
  16.218 +            }
  16.219  
  16.220              setRedirectLocation(req, "./projects/" + project.getNode() + "/components/");
  16.221              setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
  16.222  
  16.223              renderSite(req, resp);
  16.224 -        } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
  16.225 +        } catch (NoSuchElementException | IllegalArgumentException ex) {
  16.226              resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
  16.227              // TODO: implement - fix issue #21
  16.228          }
  16.229      }
  16.230  
  16.231 -    private void configureIssueEditor(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException {
  16.232 +    private void configureIssueEditor(IssueEditView viewModel, Issue issue, DaoProvider dao) throws SQLException {
  16.233          final var project = viewModel.getProjectInfo().getProject();
  16.234          issue.setProject(project); // automatically set current project for new issues
  16.235          viewModel.setIssue(issue);
  16.236 @@ -436,7 +455,7 @@
  16.237      }
  16.238  
  16.239      @RequestMapping(requestPath = "$project/issues/$issue/view", method = HttpMethod.GET)
  16.240 -    public void viewIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
  16.241 +    public void viewIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException {
  16.242          final var viewModel = new IssueDetailView();
  16.243          populate(viewModel, pathParameters, dao);
  16.244  
  16.245 @@ -462,7 +481,7 @@
  16.246  
  16.247      // TODO: why should the issue editor be child of $project?
  16.248      @RequestMapping(requestPath = "$project/issues/$issue/edit", method = HttpMethod.GET)
  16.249 -    public void editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
  16.250 +    public void editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException {
  16.251          final var viewModel = new IssueEditView();
  16.252          populate(viewModel, pathParameters, dao);
  16.253  
  16.254 @@ -486,7 +505,7 @@
  16.255      }
  16.256  
  16.257      @RequestMapping(requestPath = "$project/create-issue", method = HttpMethod.GET)
  16.258 -    public void createIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
  16.259 +    public void createIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException {
  16.260          final var viewModel = new IssueEditView();
  16.261          populate(viewModel, pathParameters, dao);
  16.262  
  16.263 @@ -504,7 +523,7 @@
  16.264      }
  16.265  
  16.266      @RequestMapping(requestPath = "commit-issue", method = HttpMethod.POST)
  16.267 -    public void commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, ServletException {
  16.268 +    public void commitIssue(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws IOException, ServletException {
  16.269          try {
  16.270              final var issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow());
  16.271              final var componentId = getParameter(req, Integer.class, "component");
  16.272 @@ -549,21 +568,27 @@
  16.273                              stream.map(Version::new).collect(Collectors.toList())
  16.274                      ).ifPresent(issue::setResolvedVersions);
  16.275  
  16.276 -            dao.getIssueDao().saveOrUpdate(issue, issue.getProject());
  16.277 +            final var issueDao = dao.getIssueDao();
  16.278 +            if (issue.getId() > 0) {
  16.279 +                // TODO: use return value
  16.280 +                issueDao.update(issue);
  16.281 +            } else {
  16.282 +                issueDao.save(issue, project);
  16.283 +            }
  16.284  
  16.285              // TODO: fix redirect location
  16.286              setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view");
  16.287              setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
  16.288  
  16.289              renderSite(req, resp);
  16.290 -        } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
  16.291 +        } catch (NoSuchElementException | IllegalArgumentException ex) {
  16.292              resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
  16.293              // TODO: implement - fix issue #21
  16.294          }
  16.295      }
  16.296  
  16.297      @RequestMapping(requestPath = "commit-issue-comment", method = HttpMethod.POST)
  16.298 -    public void commentIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException, ServletException {
  16.299 +    public void commentIssue(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws IOException, ServletException {
  16.300          final var issueIdParam = getParameter(req, Integer.class, "issueid");
  16.301          if (issueIdParam.isEmpty()) {
  16.302              resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Detected manipulated form.");
  16.303 @@ -584,7 +609,7 @@
  16.304  
  16.305              LOG.debug("User {} is commenting on issue #{}", req.getRemoteUser(), issue.getId());
  16.306              if (req.getRemoteUser() != null) {
  16.307 -                dao.getUserDao().findByUsername(req.getRemoteUser()).ifPresent(issueComment::setAuthor);
  16.308 +                Optional.ofNullable(dao.getUserDao().findByUsername(req.getRemoteUser())).ifPresent(issueComment::setAuthor);
  16.309              }
  16.310  
  16.311              dao.getIssueDao().saveComment(issue, issueComment);
  16.312 @@ -594,7 +619,7 @@
  16.313              setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
  16.314  
  16.315              renderSite(req, resp);
  16.316 -        } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
  16.317 +        } catch (NoSuchElementException | IllegalArgumentException ex) {
  16.318              resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
  16.319              // TODO: implement - fix issue #21
  16.320          }
    17.1 --- a/src/main/java/de/uapcore/lightpit/modules/UsersModule.java	Fri Nov 06 10:50:32 2020 +0100
    17.2 +++ b/src/main/java/de/uapcore/lightpit/modules/UsersModule.java	Thu Nov 19 13:58:54 2020 +0100
    17.3 @@ -32,7 +32,7 @@
    17.4  import de.uapcore.lightpit.Constants;
    17.5  import de.uapcore.lightpit.HttpMethod;
    17.6  import de.uapcore.lightpit.RequestMapping;
    17.7 -import de.uapcore.lightpit.dao.DataAccessObjects;
    17.8 +import de.uapcore.lightpit.dao.DaoProvider;
    17.9  import de.uapcore.lightpit.entities.User;
   17.10  import de.uapcore.lightpit.viewmodel.UsersEditView;
   17.11  import de.uapcore.lightpit.viewmodel.UsersView;
   17.12 @@ -61,7 +61,7 @@
   17.13      }
   17.14  
   17.15      @RequestMapping(method = HttpMethod.GET)
   17.16 -    public void index(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, ServletException, IOException {
   17.17 +    public void index(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws SQLException, ServletException, IOException {
   17.18          final var userDao = dao.getUserDao();
   17.19  
   17.20          final var viewModel = new UsersView();
   17.21 @@ -73,7 +73,7 @@
   17.22      }
   17.23  
   17.24      @RequestMapping(requestPath = "edit", method = HttpMethod.GET)
   17.25 -    public void edit(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, ServletException, IOException {
   17.26 +    public void edit(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws SQLException, ServletException, IOException {
   17.27  
   17.28          final var viewModel = new UsersEditView();
   17.29          viewModel.setUser(findByParameter(req, Integer.class, "id",
   17.30 @@ -86,7 +86,7 @@
   17.31      }
   17.32  
   17.33      @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
   17.34 -    public void commit(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws ServletException, IOException {
   17.35 +    public void commit(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws ServletException, IOException {
   17.36  
   17.37          User user = new User(-1);
   17.38          try {
   17.39 @@ -96,13 +96,19 @@
   17.40              getParameter(req, String.class, "lastname").ifPresent(user::setLastname);
   17.41              getParameter(req, String.class, "mail").ifPresent(user::setMail);
   17.42  
   17.43 -            dao.getUserDao().saveOrUpdate(user);
   17.44 +            final var userDao = dao.getUserDao();
   17.45 +            if (user.getId() > 0) {
   17.46 +                // TODO: unused return value
   17.47 +                userDao.update(user);
   17.48 +            } else {
   17.49 +                userDao.save(user);
   17.50 +            }
   17.51  
   17.52              setRedirectLocation(req, "./teams/");
   17.53              setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   17.54  
   17.55              LOG.debug("Successfully updated user {}", user.getUsername());
   17.56 -        } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   17.57 +        } catch (NoSuchElementException | IllegalArgumentException ex) {
   17.58              final var viewModel = new UsersEditView();
   17.59              viewModel.setUser(user);
   17.60              // TODO: viewModel.setErrorText()
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractChildEntityDao.kt	Thu Nov 19 13:58:54 2020 +0100
    18.3 @@ -0,0 +1,64 @@
    18.4 +/*
    18.5 + * Copyright 2020 Mike Becker. All rights reserved.
    18.6 + *
    18.7 + * Redistribution and use in source and binary forms, with or without
    18.8 + * modification, are permitted provided that the following conditions are met:
    18.9 + *
   18.10 + * 1. Redistributions of source code must retain the above copyright
   18.11 + * notice, this list of conditions and the following disclaimer.
   18.12 + *
   18.13 + * 2. Redistributions in binary form must reproduce the above copyright
   18.14 + * notice, this list of conditions and the following disclaimer in the
   18.15 + * documentation and/or other materials provided with the distribution.
   18.16 + *
   18.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   18.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   18.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   18.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   18.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   18.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   18.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   18.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   18.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   18.27 + *
   18.28 + */
   18.29 +
   18.30 +package de.uapcore.lightpit.dao
   18.31 +
   18.32 +
   18.33 +abstract class AbstractChildEntityDao<T, P> : AbstractDao<T>() {
   18.34 +
   18.35 +    /**
   18.36 +     * Lists all entities being a child of the specified parent.
   18.37 +     * @param parent the parent
   18.38 +     * @return the list of child instances
   18.39 +     */
   18.40 +    abstract fun list(parent: P): List<T>
   18.41 +
   18.42 +    /**
   18.43 +     * Finds an entity by its integer ID.
   18.44 +     * It is not guaranteed that referenced entities are automatically joined.
   18.45 +     *
   18.46 +     * @param id the id
   18.47 +     * @return the entity or null if there is no such entity
   18.48 +     */
   18.49 +    abstract fun find(id: Int): T?
   18.50 +
   18.51 +    /**
   18.52 +     * Inserts an instance into database.
   18.53 +     * It is not guaranteed that generated fields will be updated in the instance.
   18.54 +     *
   18.55 +     * @param instance the instance to insert
   18.56 +     * @param parent a reference to the parent
   18.57 +     */
   18.58 +    abstract fun save(instance: T, parent: P)
   18.59 +
   18.60 +    /**
   18.61 +     * Updates an instance in the database.
   18.62 +     *
   18.63 +     * @param instance the instance to update
   18.64 +     * @return true if an instance has been updated, false if the instance is not present in database
   18.65 +     */
   18.66 +    abstract fun update(instance: T): Boolean
   18.67 +}
    19.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractComponentDao.kt	Thu Nov 19 13:58:54 2020 +0100
    19.3 @@ -0,0 +1,34 @@
    19.4 +/*
    19.5 + * Copyright 2020 Mike Becker. All rights reserved.
    19.6 + *
    19.7 + * Redistribution and use in source and binary forms, with or without
    19.8 + * modification, are permitted provided that the following conditions are met:
    19.9 + *
   19.10 + * 1. Redistributions of source code must retain the above copyright
   19.11 + * notice, this list of conditions and the following disclaimer.
   19.12 + *
   19.13 + * 2. Redistributions in binary form must reproduce the above copyright
   19.14 + * notice, this list of conditions and the following disclaimer in the
   19.15 + * documentation and/or other materials provided with the distribution.
   19.16 + *
   19.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   19.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   19.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   19.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   19.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   19.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   19.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   19.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   19.27 + *
   19.28 + */
   19.29 +
   19.30 +package de.uapcore.lightpit.dao
   19.31 +
   19.32 +import de.uapcore.lightpit.entities.Component
   19.33 +import de.uapcore.lightpit.entities.Project
   19.34 +
   19.35 +abstract class AbstractComponentDao : AbstractChildEntityDao<Component, Project>() {
   19.36 +    abstract fun findByNode(parent: Project, node: String): Component?
   19.37 +}
   19.38 \ No newline at end of file
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractDao.kt	Thu Nov 19 13:58:54 2020 +0100
    20.3 @@ -0,0 +1,65 @@
    20.4 +/*
    20.5 + * Copyright 2020 Mike Becker. All rights reserved.
    20.6 + *
    20.7 + * Redistribution and use in source and binary forms, with or without
    20.8 + * modification, are permitted provided that the following conditions are met:
    20.9 + *
   20.10 + * 1. Redistributions of source code must retain the above copyright
   20.11 + * notice, this list of conditions and the following disclaimer.
   20.12 + *
   20.13 + * 2. Redistributions in binary form must reproduce the above copyright
   20.14 + * notice, this list of conditions and the following disclaimer in the
   20.15 + * documentation and/or other materials provided with the distribution.
   20.16 + *
   20.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   20.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   20.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   20.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   20.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   20.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   20.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   20.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   20.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   20.27 + *
   20.28 + */
   20.29 +
   20.30 +package de.uapcore.lightpit.dao
   20.31 +
   20.32 +import java.sql.PreparedStatement
   20.33 +import java.sql.ResultSet
   20.34 +import java.sql.Types
   20.35 +
   20.36 +abstract class AbstractDao<T> {
   20.37 +
   20.38 +    abstract fun mapResult(rs: ResultSet): T
   20.39 +
   20.40 +    protected fun list(stmt: PreparedStatement): List<T> {
   20.41 +        return sequence {
   20.42 +            stmt.executeQuery().use { result ->
   20.43 +                while (result.next()) yield(mapResult(result))
   20.44 +            }
   20.45 +        }.toList()
   20.46 +    }
   20.47 +
   20.48 +    protected fun find(stmt: PreparedStatement): T? {
   20.49 +        stmt.executeQuery().use { result ->
   20.50 +            return if (result.next()) {
   20.51 +                mapResult(result)
   20.52 +            } else {
   20.53 +                null
   20.54 +            }
   20.55 +        }
   20.56 +    }
   20.57 +
   20.58 +    // TODO: create PreparedStatement abstraction that provides some features
   20.59 +
   20.60 +    // TODO: remove the following legacy code helper function
   20.61 +    protected fun <T> setForeignKeyOrNull(stmt: PreparedStatement, index: Int, instance: T?, keyGetter: (obj: T) -> Int) {
   20.62 +        if (instance == null) {
   20.63 +            stmt.setNull(index, Types.INTEGER)
   20.64 +        } else {
   20.65 +            stmt.setInt(index, keyGetter(instance))
   20.66 +        }
   20.67 +    }
   20.68 +}
   20.69 \ No newline at end of file
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractEntityDao.kt	Thu Nov 19 13:58:54 2020 +0100
    21.3 @@ -0,0 +1,61 @@
    21.4 +/*
    21.5 + * Copyright 2020 Mike Becker. All rights reserved.
    21.6 + *
    21.7 + * Redistribution and use in source and binary forms, with or without
    21.8 + * modification, are permitted provided that the following conditions are met:
    21.9 + *
   21.10 + * 1. Redistributions of source code must retain the above copyright
   21.11 + * notice, this list of conditions and the following disclaimer.
   21.12 + *
   21.13 + * 2. Redistributions in binary form must reproduce the above copyright
   21.14 + * notice, this list of conditions and the following disclaimer in the
   21.15 + * documentation and/or other materials provided with the distribution.
   21.16 + *
   21.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   21.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   21.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   21.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   21.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   21.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   21.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   21.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   21.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   21.27 + *
   21.28 + */
   21.29 +
   21.30 +package de.uapcore.lightpit.dao
   21.31 +
   21.32 +abstract class AbstractEntityDao<T> : AbstractDao<T>() {
   21.33 +
   21.34 +    /**
   21.35 +     * Lists all entities.
   21.36 +     * @return a list of all entities
   21.37 +     */
   21.38 +    abstract fun list(): List<T>
   21.39 +
   21.40 +    /**
   21.41 +     * Finds an entity by its integer ID.
   21.42 +     * It is not guaranteed that referenced entities are automatically joined.
   21.43 +     *
   21.44 +     * @param id the id
   21.45 +     * @return the entity or null if there is no such entity
   21.46 +     */
   21.47 +    abstract fun find(id: Int): T?
   21.48 +
   21.49 +    /**
   21.50 +     * Inserts an instance into database.
   21.51 +     * It is not guaranteed that generated fields will be updated in the instance.
   21.52 +     *
   21.53 +     * @param instance the instance to insert
   21.54 +     */
   21.55 +    abstract fun save(instance: T)
   21.56 +
   21.57 +    /**
   21.58 +     * Updates an instance in the database.
   21.59 +     *
   21.60 +     * @param instance the instance to update
   21.61 +     * @return true if an instance has been updated, false if the instance is not present in database
   21.62 +     */
   21.63 +    abstract fun update(instance: T): Boolean
   21.64 +}
   21.65 \ No newline at end of file
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractIssueDao.kt	Thu Nov 19 13:58:54 2020 +0100
    22.3 @@ -0,0 +1,99 @@
    22.4 +/*
    22.5 + * Copyright 2020 Mike Becker. All rights reserved.
    22.6 + *
    22.7 + * Redistribution and use in source and binary forms, with or without
    22.8 + * modification, are permitted provided that the following conditions are met:
    22.9 + *
   22.10 + * 1. Redistributions of source code must retain the above copyright
   22.11 + * notice, this list of conditions and the following disclaimer.
   22.12 + *
   22.13 + * 2. Redistributions in binary form must reproduce the above copyright
   22.14 + * notice, this list of conditions and the following disclaimer in the
   22.15 + * documentation and/or other materials provided with the distribution.
   22.16 + *
   22.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   22.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   22.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   22.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   22.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   22.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   22.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   22.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   22.27 + *
   22.28 + */
   22.29 +
   22.30 +package de.uapcore.lightpit.dao
   22.31 +
   22.32 +import de.uapcore.lightpit.entities.*
   22.33 +import java.sql.SQLException
   22.34 +
   22.35 +abstract class AbstractIssueDao : AbstractChildEntityDao<Issue, Project>() {
   22.36 +
   22.37 +    /**
   22.38 +     * Lists all issues that are related to the specified component and version.
   22.39 +     * If component or version is null, search for issues that are not assigned to any
   22.40 +     * component or version, respectively.
   22.41 +     *
   22.42 +     * @param project the project
   22.43 +     * @param component the component
   22.44 +     * @param version the version
   22.45 +     * @return a list of issues
   22.46 +     */
   22.47 +    abstract fun list(project: Project, component: Component?, version: Version?): List<Issue>
   22.48 +
   22.49 +    /**
   22.50 +     * Lists all issues that are related to the specified version.
   22.51 +     * If the version is null, lists issues that are not assigned to any version.
   22.52 +     *
   22.53 +     * @param project the project
   22.54 +     * @param version the version or null
   22.55 +     * @return a list of issues
   22.56 +     */
   22.57 +    abstract fun list(project: Project, version: Version?): List<Issue>
   22.58 +
   22.59 +    /**
   22.60 +     * Lists all issues that are related to the specified component.
   22.61 +     * If the component is null, lists issues that are not assigned to a component.
   22.62 +     *
   22.63 +     * @param project the project
   22.64 +     * @param component the component or null
   22.65 +     * @return a list of issues
   22.66 +     */
   22.67 +    abstract fun list(project: Project, component: Component?): List<Issue>
   22.68 +
   22.69 +    /**
   22.70 +     * Lists all comments for a specific issue in chronological order.
   22.71 +     *
   22.72 +     * @param issue the issue
   22.73 +     * @return the list of comments
   22.74 +     */
   22.75 +    abstract fun listComments(issue: Issue): List<IssueComment>
   22.76 +
   22.77 +    /**
   22.78 +     * Stores the specified comment in database.
   22.79 +     * This is an update-or-insert operation.
   22.80 +     *
   22.81 +     * @param issue the issue to save the comment for
   22.82 +     * @param comment the comment to save
   22.83 +     */
   22.84 +    abstract fun saveComment(issue: Issue, comment: IssueComment)
   22.85 +
   22.86 +    /**
   22.87 +     * Saves an instances to the database.
   22.88 +     * Implementations of this DAO must guarantee that the generated ID is stored in the instance.
   22.89 +     *
   22.90 +     * @param instance the instance to insert
   22.91 +     * @param parent the parent project
   22.92 +     * @throws SQLException on any kind of SQL error
   22.93 +     */
   22.94 +    abstract override fun save(instance: Issue, parent: Project)
   22.95 +
   22.96 +    /**
   22.97 +     * Retrieves the affected, scheduled and resolved versions for the specified issue.
   22.98 +     *
   22.99 +     * @param issue the issue to join the information for
  22.100 +     */
  22.101 +    abstract fun joinVersionInformation(issue: Issue)
  22.102 +}
  22.103 \ No newline at end of file
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractProjectDao.kt	Thu Nov 19 13:58:54 2020 +0100
    23.3 @@ -0,0 +1,37 @@
    23.4 +/*
    23.5 + * Copyright 2020 Mike Becker. All rights reserved.
    23.6 + *
    23.7 + * Redistribution and use in source and binary forms, with or without
    23.8 + * modification, are permitted provided that the following conditions are met:
    23.9 + *
   23.10 + * 1. Redistributions of source code must retain the above copyright
   23.11 + * notice, this list of conditions and the following disclaimer.
   23.12 + *
   23.13 + * 2. Redistributions in binary form must reproduce the above copyright
   23.14 + * notice, this list of conditions and the following disclaimer in the
   23.15 + * documentation and/or other materials provided with the distribution.
   23.16 + *
   23.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   23.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   23.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   23.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   23.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   23.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   23.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   23.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   23.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   23.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   23.27 + *
   23.28 + */
   23.29 +
   23.30 +package de.uapcore.lightpit.dao
   23.31 +
   23.32 +import de.uapcore.lightpit.entities.IssueSummary
   23.33 +import de.uapcore.lightpit.entities.Project
   23.34 +
   23.35 +abstract class AbstractProjectDao : AbstractEntityDao<Project>() {
   23.36 +
   23.37 +    abstract fun getIssueSummary(project: Project): IssueSummary
   23.38 +
   23.39 +    abstract fun findByNode(node: String): Project?
   23.40 +}
   23.41 \ No newline at end of file
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractUserDao.kt	Thu Nov 19 13:58:54 2020 +0100
    24.3 @@ -0,0 +1,33 @@
    24.4 +/*
    24.5 + * Copyright 2020 Mike Becker. All rights reserved.
    24.6 + *
    24.7 + * Redistribution and use in source and binary forms, with or without
    24.8 + * modification, are permitted provided that the following conditions are met:
    24.9 + *
   24.10 + * 1. Redistributions of source code must retain the above copyright
   24.11 + * notice, this list of conditions and the following disclaimer.
   24.12 + *
   24.13 + * 2. Redistributions in binary form must reproduce the above copyright
   24.14 + * notice, this list of conditions and the following disclaimer in the
   24.15 + * documentation and/or other materials provided with the distribution.
   24.16 + *
   24.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   24.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   24.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   24.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   24.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   24.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   24.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   24.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   24.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   24.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   24.27 + *
   24.28 + */
   24.29 +
   24.30 +package de.uapcore.lightpit.dao
   24.31 +
   24.32 +import de.uapcore.lightpit.entities.User
   24.33 +
   24.34 +abstract class AbstractUserDao : AbstractEntityDao<User>() {
   24.35 +    abstract fun findByUsername(username: String): User?
   24.36 +}
   24.37 \ No newline at end of file
    25.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractVersionDao.kt	Thu Nov 19 13:58:54 2020 +0100
    25.3 @@ -0,0 +1,34 @@
    25.4 +/*
    25.5 + * Copyright 2020 Mike Becker. All rights reserved.
    25.6 + *
    25.7 + * Redistribution and use in source and binary forms, with or without
    25.8 + * modification, are permitted provided that the following conditions are met:
    25.9 + *
   25.10 + * 1. Redistributions of source code must retain the above copyright
   25.11 + * notice, this list of conditions and the following disclaimer.
   25.12 + *
   25.13 + * 2. Redistributions in binary form must reproduce the above copyright
   25.14 + * notice, this list of conditions and the following disclaimer in the
   25.15 + * documentation and/or other materials provided with the distribution.
   25.16 + *
   25.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   25.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   25.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   25.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   25.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   25.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   25.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   25.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   25.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   25.27 + *
   25.28 + */
   25.29 +
   25.30 +package de.uapcore.lightpit.dao
   25.31 +
   25.32 +import de.uapcore.lightpit.entities.Project
   25.33 +import de.uapcore.lightpit.entities.Version
   25.34 +
   25.35 +abstract class AbstractVersionDao : AbstractChildEntityDao<Version, Project>() {
   25.36 +    abstract fun findByNode(parent: Project, node: String): Version?
   25.37 +}
   25.38 \ No newline at end of file
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/DaoProvider.kt	Thu Nov 19 13:58:54 2020 +0100
    26.3 @@ -0,0 +1,35 @@
    26.4 +/*
    26.5 + * Copyright 2020 Mike Becker. All rights reserved.
    26.6 + *
    26.7 + * Redistribution and use in source and binary forms, with or without
    26.8 + * modification, are permitted provided that the following conditions are met:
    26.9 + *
   26.10 + * 1. Redistributions of source code must retain the above copyright
   26.11 + * notice, this list of conditions and the following disclaimer.
   26.12 + *
   26.13 + * 2. Redistributions in binary form must reproduce the above copyright
   26.14 + * notice, this list of conditions and the following disclaimer in the
   26.15 + * documentation and/or other materials provided with the distribution.
   26.16 + *
   26.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   26.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   26.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   26.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   26.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   26.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   26.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   26.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   26.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   26.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   26.27 + *
   26.28 + */
   26.29 +
   26.30 +package de.uapcore.lightpit.dao
   26.31 +
   26.32 +interface DaoProvider {
   26.33 +    val userDao: AbstractUserDao
   26.34 +    val projectDao: AbstractProjectDao
   26.35 +    val componentDao: AbstractComponentDao
   26.36 +    val versionDao: AbstractVersionDao
   26.37 +    val issueDao: AbstractIssueDao
   26.38 +}
   26.39 \ No newline at end of file
    27.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    27.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGComponentDao.kt	Thu Nov 19 13:58:54 2020 +0100
    27.3 @@ -0,0 +1,110 @@
    27.4 +/*
    27.5 + * Copyright 2020 Mike Becker. All rights reserved.
    27.6 + *
    27.7 + * Redistribution and use in source and binary forms, with or without
    27.8 + * modification, are permitted provided that the following conditions are met:
    27.9 + *
   27.10 + * 1. Redistributions of source code must retain the above copyright
   27.11 + * notice, this list of conditions and the following disclaimer.
   27.12 + *
   27.13 + * 2. Redistributions in binary form must reproduce the above copyright
   27.14 + * notice, this list of conditions and the following disclaimer in the
   27.15 + * documentation and/or other materials provided with the distribution.
   27.16 + *
   27.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   27.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   27.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   27.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   27.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   27.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   27.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   27.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   27.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   27.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   27.27 + *
   27.28 + */
   27.29 +
   27.30 +package de.uapcore.lightpit.dao.postgres
   27.31 +
   27.32 +import de.uapcore.lightpit.dao.AbstractComponentDao
   27.33 +import de.uapcore.lightpit.dao.Functions
   27.34 +import de.uapcore.lightpit.entities.Component
   27.35 +import de.uapcore.lightpit.entities.Project
   27.36 +import de.uapcore.lightpit.entities.User
   27.37 +import de.uapcore.lightpit.types.WebColor
   27.38 +import java.sql.Connection
   27.39 +import java.sql.PreparedStatement
   27.40 +import java.sql.ResultSet
   27.41 +
   27.42 +class PGComponentDao(connection: Connection) : AbstractComponentDao() {
   27.43 +
   27.44 +    private val query = "select id, name, node, color, ordinal, description, " +
   27.45 +            "userid, username, givenname, lastname, mail " +
   27.46 +            "from lpit_component " +
   27.47 +            "left join lpit_user on lead = userid"
   27.48 +
   27.49 +    private val listStmt = connection.prepareStatement("$query where project = ? order by ordinal, lower(name)")
   27.50 +    private val findStmt = connection.prepareStatement("$query where id = ? ")
   27.51 +    private val findByNodeStmt = connection.prepareStatement("$query where project = ? and node = ?")
   27.52 +    private val insertStmt = connection.prepareStatement(
   27.53 +            "insert into lpit_component (name, node, color, ordinal, description, lead, project) values (?, ?, ?, ?, ?, ?, ?)"
   27.54 +    )
   27.55 +    private val updateStmt = connection.prepareStatement(
   27.56 +            "update lpit_component set name = ?, node = ?, color = ?, ordinal = ?, description = ?, lead = ? where id = ?"
   27.57 +    )
   27.58 +
   27.59 +    override fun mapResult(rs: ResultSet): Component {
   27.60 +        val component = Component(rs.getInt("id"))
   27.61 +        component.name = rs.getString("name")
   27.62 +        component.node = rs.getString("node")
   27.63 +        component.color = try {
   27.64 +            WebColor(rs.getString("color"))
   27.65 +        } catch (ex: IllegalArgumentException) {
   27.66 +            WebColor("000000")
   27.67 +        }
   27.68 +        component.ordinal = rs.getInt("ordinal")
   27.69 +        component.description = rs.getString("description")
   27.70 +        component.lead = PGUserDao.mapResult(rs).takeUnless { rs.wasNull() }
   27.71 +        return component
   27.72 +    }
   27.73 +
   27.74 +    private fun setColumns(stmt: PreparedStatement, instance: Component): Int {
   27.75 +        var column = 0
   27.76 +        stmt.setString(++column, instance.name)
   27.77 +        stmt.setString(++column, instance.node)
   27.78 +        stmt.setString(++column, instance.color.hex)
   27.79 +        stmt.setInt(++column, instance.ordinal)
   27.80 +        Functions.setStringOrNull(stmt, ++column, instance.description)
   27.81 +        setForeignKeyOrNull(stmt, ++column, instance.lead, User::id)
   27.82 +        return column
   27.83 +    }
   27.84 +
   27.85 +    override fun save(instance: Component, parent: Project) {
   27.86 +        var column = setColumns(insertStmt, instance)
   27.87 +        insertStmt.setInt(++column, parent.id)
   27.88 +        insertStmt.executeUpdate()
   27.89 +    }
   27.90 +
   27.91 +    override fun update(instance: Component): Boolean {
   27.92 +        var column = setColumns(updateStmt, instance)
   27.93 +        updateStmt.setInt(++column, instance.id)
   27.94 +        return updateStmt.executeUpdate() > 0
   27.95 +    }
   27.96 +
   27.97 +
   27.98 +    override fun list(parent: Project): List<Component> {
   27.99 +        listStmt.setInt(1, parent.id)
  27.100 +        return super.list(listStmt)
  27.101 +    }
  27.102 +
  27.103 +    override fun find(id: Int): Component? {
  27.104 +        findStmt.setInt(1, id)
  27.105 +        return super.find(findStmt)
  27.106 +    }
  27.107 +
  27.108 +    override fun findByNode(parent: Project, node: String): Component? {
  27.109 +        findByNodeStmt.setInt(1, parent.id)
  27.110 +        findByNodeStmt.setString(2, node)
  27.111 +        return super.find(findByNodeStmt)
  27.112 +    }
  27.113 +}
  27.114 \ No newline at end of file
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGDaoProvider.kt	Thu Nov 19 13:58:54 2020 +0100
    28.3 @@ -0,0 +1,38 @@
    28.4 +/*
    28.5 + * Copyright 2020 Mike Becker. All rights reserved.
    28.6 + *
    28.7 + * Redistribution and use in source and binary forms, with or without
    28.8 + * modification, are permitted provided that the following conditions are met:
    28.9 + *
   28.10 + * 1. Redistributions of source code must retain the above copyright
   28.11 + * notice, this list of conditions and the following disclaimer.
   28.12 + *
   28.13 + * 2. Redistributions in binary form must reproduce the above copyright
   28.14 + * notice, this list of conditions and the following disclaimer in the
   28.15 + * documentation and/or other materials provided with the distribution.
   28.16 + *
   28.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   28.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   28.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   28.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   28.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   28.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   28.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   28.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   28.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   28.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   28.27 + *
   28.28 + */
   28.29 +
   28.30 +package de.uapcore.lightpit.dao.postgres
   28.31 +
   28.32 +import de.uapcore.lightpit.dao.DaoProvider
   28.33 +import java.sql.Connection
   28.34 +
   28.35 +class PGDaoProvider(connection: Connection) : DaoProvider {
   28.36 +    override val userDao = PGUserDao(connection)
   28.37 +    override val projectDao = PGProjectDao(connection)
   28.38 +    override val componentDao = PGComponentDao(connection)
   28.39 +    override val versionDao = PGVersionDao(connection)
   28.40 +    override val issueDao = PGIssueDao(connection)
   28.41 +}
   28.42 \ No newline at end of file
    29.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGIssueDao.kt	Thu Nov 19 13:58:54 2020 +0100
    29.3 @@ -0,0 +1,249 @@
    29.4 +/*
    29.5 + * Copyright 2020 Mike Becker. All rights reserved.
    29.6 + *
    29.7 + * Redistribution and use in source and binary forms, with or without
    29.8 + * modification, are permitted provided that the following conditions are met:
    29.9 + *
   29.10 + * 1. Redistributions of source code must retain the above copyright
   29.11 + * notice, this list of conditions and the following disclaimer.
   29.12 + *
   29.13 + * 2. Redistributions in binary form must reproduce the above copyright
   29.14 + * notice, this list of conditions and the following disclaimer in the
   29.15 + * documentation and/or other materials provided with the distribution.
   29.16 + *
   29.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   29.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   29.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   29.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   29.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   29.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   29.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   29.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   29.27 + *
   29.28 + */
   29.29 +
   29.30 +package de.uapcore.lightpit.dao.postgres
   29.31 +
   29.32 +import de.uapcore.lightpit.dao.AbstractIssueDao
   29.33 +import de.uapcore.lightpit.dao.Functions
   29.34 +import de.uapcore.lightpit.entities.*
   29.35 +import java.sql.Connection
   29.36 +import java.sql.PreparedStatement
   29.37 +import java.sql.ResultSet
   29.38 +import java.sql.Types
   29.39 +
   29.40 +class PGIssueDao(connection: Connection) : AbstractIssueDao() {
   29.41 +
   29.42 +    private val query = "select issueid, i.project, p.name as projectname, p.node as projectnode, " +
   29.43 +            "component, c.name as componentname, c.node as componentnode, " +
   29.44 +            "status, category, subject, i.description, " +
   29.45 +            "userid, username, givenname, lastname, mail, " +
   29.46 +            "created, updated, eta " +
   29.47 +            "from lpit_issue i " +
   29.48 +            "join lpit_project p on i.project = projectid " +
   29.49 +            "left join lpit_component c on component = c.id " +
   29.50 +            "left join lpit_user on userid = assignee "
   29.51 +    private val list = connection.prepareStatement(query +
   29.52 +            "where i.project = ? and coalesce(component, -1) = coalesce(?, component, -1)")
   29.53 +    private val listForVersion = connection.prepareStatement(
   29.54 +            "with issue_version as ( " +
   29.55 +                    "select issueid, versionid from lpit_issue_affected_version union " +
   29.56 +                    "select issueid, versionid from lpit_issue_resolved_version) " +
   29.57 +                    query +
   29.58 +                    "left join issue_version using (issueid) " +
   29.59 +                    "where i.project = ? " +
   29.60 +                    "and coalesce(versionid,-1) = ? and coalesce(component, -1) = coalesce(?, component, -1)"
   29.61 +    )
   29.62 +    private val find = connection.prepareStatement(query + "where issueid = ? ")
   29.63 +    private val insert = connection.prepareStatement(
   29.64 +            "insert into lpit_issue (project, component, status, category, subject, description, assignee, eta) " +
   29.65 +                    "values (?, ?, ?::issue_status, ?::issue_category, ?, ?, ?, ?) returning issueid"
   29.66 +    )
   29.67 +    private val update = connection.prepareStatement(
   29.68 +            "update lpit_issue set " +
   29.69 +                    "updated = now(), component = ?, status = ?::issue_status, category = ?::issue_category, " +
   29.70 +                    "subject = ?, description = ?, assignee = ?, eta = ? where issueid = ?"
   29.71 +    )
   29.72 +    private val affectedVersions = connection.prepareStatement(
   29.73 +            "select versionid, name, status, ordinal, node " +
   29.74 +                    "from lpit_version join lpit_issue_affected_version using (versionid) " +
   29.75 +                    "where issueid = ? " +
   29.76 +                    "order by ordinal, name"
   29.77 +    )
   29.78 +    private val clearAffected = connection.prepareStatement("delete from lpit_issue_affected_version where issueid = ?")
   29.79 +    private val insertAffected = connection.prepareStatement("insert into lpit_issue_affected_version (issueid, versionid) values (?,?)")
   29.80 +
   29.81 +    private val resolvedVersions = connection.prepareStatement(
   29.82 +            "select versionid, name, status, ordinal, node " +
   29.83 +                    "from lpit_version v join lpit_issue_resolved_version using (versionid) " +
   29.84 +                    "where issueid = ? " +
   29.85 +                    "order by ordinal, name"
   29.86 +    )
   29.87 +    private val clearResolved = connection.prepareStatement("delete from lpit_issue_resolved_version where issueid = ?")
   29.88 +    private val insertResolved = connection.prepareStatement("insert into lpit_issue_resolved_version (issueid, versionid) values (?,?)")
   29.89 +    private val insertComment = connection.prepareStatement(
   29.90 +            "insert into lpit_issue_comment (issueid, comment, userid) values (?, ? ,?)"
   29.91 +    )
   29.92 +    private val updateComment = connection.prepareStatement(
   29.93 +            "update lpit_issue_comment set comment = ?, updated = now(), updatecount = updatecount+1 where commentid = ?"
   29.94 +    )
   29.95 +    private val listComments = connection.prepareStatement(
   29.96 +            "select * from lpit_issue_comment left join lpit_user using (userid) where issueid = ? order by created"
   29.97 +    )
   29.98 +
   29.99 +    override fun mapResult(rs: ResultSet): Issue {
  29.100 +        val project = Project(rs.getInt("project"))
  29.101 +        project.name = rs.getString("projectname")
  29.102 +        project.node = rs.getString("projectnode")
  29.103 +        val issue = Issue(rs.getInt("issueid"))
  29.104 +        issue.project = project
  29.105 +        issue.component = rs.getInt("component").let { id ->
  29.106 +            if (rs.wasNull()) {
  29.107 +                null
  29.108 +            } else {
  29.109 +                val component = Component(id)
  29.110 +                component.name = rs.getString("componentname")
  29.111 +                component.node = rs.getString("componentnode")
  29.112 +                component
  29.113 +            }
  29.114 +        }
  29.115 +        issue.status = IssueStatus.valueOf(rs.getString("status"))
  29.116 +        issue.category = IssueCategory.valueOf(rs.getString("category"))
  29.117 +        issue.subject = rs.getString("subject")
  29.118 +        issue.description = rs.getString("description")
  29.119 +        issue.assignee = PGUserDao.mapResult(rs).takeUnless { rs.wasNull() }
  29.120 +        issue.created = rs.getTimestamp("created")
  29.121 +        issue.updated = rs.getTimestamp("updated")
  29.122 +        issue.eta = rs.getDate("eta")
  29.123 +        return issue
  29.124 +    }
  29.125 +
  29.126 +    private fun updateVersionLists(instance: Issue) {
  29.127 +        clearAffected.setInt(1, instance.id)
  29.128 +        clearResolved.setInt(1, instance.id)
  29.129 +        insertAffected.setInt(1, instance.id)
  29.130 +        insertResolved.setInt(1, instance.id)
  29.131 +        clearAffected.executeUpdate()
  29.132 +        clearResolved.executeUpdate()
  29.133 +        for (v: Version in instance.affectedVersions) {
  29.134 +            insertAffected.setInt(2, v.id)
  29.135 +            insertAffected.executeUpdate()
  29.136 +        }
  29.137 +        for (v: Version in instance.resolvedVersions) {
  29.138 +            insertResolved.setInt(2, v.id)
  29.139 +            insertResolved.executeUpdate()
  29.140 +        }
  29.141 +    }
  29.142 +
  29.143 +    private fun setData(stmt: PreparedStatement, column: Int, instance: Issue): Int {
  29.144 +        var col = column
  29.145 +        setForeignKeyOrNull(stmt, ++col, instance.component, Component::id)
  29.146 +        stmt.setString(++col, instance.status.name)
  29.147 +        stmt.setString(++col, instance.category.name)
  29.148 +        stmt.setString(++col, instance.subject)
  29.149 +        Functions.setStringOrNull(stmt, ++col, instance.description)
  29.150 +        setForeignKeyOrNull(stmt, ++col, instance.assignee, User::id)
  29.151 +        Functions.setDateOrNull(stmt, ++col, instance.eta)
  29.152 +        return col
  29.153 +    }
  29.154 +
  29.155 +    override fun save(instance: Issue, parent: Project) {
  29.156 +        instance.project = parent
  29.157 +        var column = 0
  29.158 +        insert.setInt(++column, parent.id)
  29.159 +        setData(insert, column, instance)
  29.160 +        // insert and retrieve the ID
  29.161 +        val rs = insert.executeQuery()
  29.162 +        rs.next()
  29.163 +        instance.id = rs.getInt(1)
  29.164 +        updateVersionLists(instance)
  29.165 +    }
  29.166 +
  29.167 +    override fun update(instance: Issue): Boolean {
  29.168 +        var column = setData(update, 0, instance)
  29.169 +        update.setInt(++column, instance.id)
  29.170 +        return if (update.executeUpdate() > 0) {
  29.171 +            updateVersionLists(instance)
  29.172 +            true
  29.173 +        } else {
  29.174 +            false
  29.175 +        }
  29.176 +    }
  29.177 +
  29.178 +    override fun list(parent: Project): List<Issue> {
  29.179 +        list.setInt(1, parent.id)
  29.180 +        list.setNull(2, Types.INTEGER)
  29.181 +        return super.list(list)
  29.182 +    }
  29.183 +
  29.184 +    override fun list(project: Project, component: Component?, version: Version?): List<Issue> {
  29.185 +        listForVersion.setInt(1, project.id)
  29.186 +        listForVersion.setInt(2, version?.id ?: -1)
  29.187 +        listForVersion.setInt(3, component?.id ?: -1)
  29.188 +        return super.list(listForVersion)
  29.189 +    }
  29.190 +
  29.191 +    override fun list(project: Project, version: Version?): List<Issue> {
  29.192 +        listForVersion.setInt(1, project.id)
  29.193 +        listForVersion.setInt(2, version?.id ?: -1)
  29.194 +        listForVersion.setNull(3, Types.INTEGER)
  29.195 +        return super.list(listForVersion)
  29.196 +    }
  29.197 +
  29.198 +    override fun list(project: Project, component: Component?): List<Issue> {
  29.199 +        list.setInt(1, project.id)
  29.200 +        list.setInt(2, component?.id ?: -1)
  29.201 +        return super.list(list)
  29.202 +    }
  29.203 +
  29.204 +    override fun find(id: Int): Issue? {
  29.205 +        find.setInt(1, id)
  29.206 +        return super.find(find)
  29.207 +    }
  29.208 +
  29.209 +    private fun listVersions(stmt: PreparedStatement, issue: Issue): List<Version> {
  29.210 +        stmt.setInt(1, issue.id)
  29.211 +        return sequence {
  29.212 +            stmt.executeQuery().use { result ->
  29.213 +                while (result.next()) yield(PGVersionDao.mapResult(result))
  29.214 +            }
  29.215 +        }.toList()
  29.216 +    }
  29.217 +
  29.218 +    override fun joinVersionInformation(issue: Issue) {
  29.219 +        issue.affectedVersions = listVersions(affectedVersions, issue)
  29.220 +        issue.resolvedVersions = listVersions(resolvedVersions, issue)
  29.221 +    }
  29.222 +
  29.223 +    override fun listComments(issue: Issue): List<IssueComment> {
  29.224 +        listComments.setInt(1, issue.id)
  29.225 +        return sequence {
  29.226 +            listComments.executeQuery().use { rs ->
  29.227 +                while (rs.next()) {
  29.228 +                    val comment = IssueComment(rs.getInt("commentid"))
  29.229 +                    comment.created = rs.getTimestamp("created")
  29.230 +                    comment.updated = rs.getTimestamp("updated")
  29.231 +                    comment.updateCount = rs.getInt("updatecount")
  29.232 +                    comment.comment = rs.getString("comment")
  29.233 +                    comment.author = PGUserDao.mapResult(rs).takeUnless { rs.wasNull() }
  29.234 +                    yield(comment)
  29.235 +                }
  29.236 +            }
  29.237 +        }.toList()
  29.238 +    }
  29.239 +
  29.240 +    override fun saveComment(issue: Issue, comment: IssueComment) {
  29.241 +        if (comment.id >= 0) {
  29.242 +            updateComment.setString(1, comment.comment)
  29.243 +            updateComment.setInt(2, comment.id)
  29.244 +            updateComment.execute()
  29.245 +        } else {
  29.246 +            insertComment.setInt(1, issue.id)
  29.247 +            insertComment.setString(2, comment.comment)
  29.248 +            setForeignKeyOrNull(insertComment, 3, comment.author, User::id)
  29.249 +            insertComment.execute()
  29.250 +        }
  29.251 +    }
  29.252 +}
  29.253 \ No newline at end of file
    30.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGProjectDao.kt	Thu Nov 19 13:58:54 2020 +0100
    30.3 @@ -0,0 +1,122 @@
    30.4 +/*
    30.5 + * Copyright 2020 Mike Becker. All rights reserved.
    30.6 + *
    30.7 + * Redistribution and use in source and binary forms, with or without
    30.8 + * modification, are permitted provided that the following conditions are met:
    30.9 + *
   30.10 + * 1. Redistributions of source code must retain the above copyright
   30.11 + * notice, this list of conditions and the following disclaimer.
   30.12 + *
   30.13 + * 2. Redistributions in binary form must reproduce the above copyright
   30.14 + * notice, this list of conditions and the following disclaimer in the
   30.15 + * documentation and/or other materials provided with the distribution.
   30.16 + *
   30.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   30.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   30.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   30.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   30.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   30.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   30.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   30.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   30.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   30.27 + *
   30.28 + */
   30.29 +
   30.30 +package de.uapcore.lightpit.dao.postgres
   30.31 +
   30.32 +import de.uapcore.lightpit.dao.AbstractProjectDao
   30.33 +import de.uapcore.lightpit.dao.Functions
   30.34 +import de.uapcore.lightpit.entities.IssueSummary
   30.35 +import de.uapcore.lightpit.entities.Project
   30.36 +import de.uapcore.lightpit.entities.User
   30.37 +import java.sql.Connection
   30.38 +import java.sql.PreparedStatement
   30.39 +import java.sql.ResultSet
   30.40 +
   30.41 +class PGProjectDao(connection: Connection) : AbstractProjectDao() {
   30.42 +
   30.43 +    private val query = "select projectid, name, node, description, repourl, " +
   30.44 +            "userid, username, lastname, givenname, mail " +
   30.45 +            "from lpit_project " +
   30.46 +            "left join lpit_user owner on lpit_project.owner = owner.userid "
   30.47 +
   30.48 +    private val listStmt = connection.prepareStatement("$query order by name")
   30.49 +    private val findStmt = connection.prepareStatement("$query where projectid = ?")
   30.50 +    private val findByNodeStmt = connection.prepareStatement("$query where node = ?")
   30.51 +    private val issueSummaryStmt = connection.prepareStatement(
   30.52 +            "select phase, count(*) as total " +
   30.53 +                    "from lpit_issue " +
   30.54 +                    "join lpit_issue_phases using(status) " +
   30.55 +                    "where project = ? " +
   30.56 +                    "group by phase "
   30.57 +    )
   30.58 +    private val insertStmt = connection.prepareStatement(
   30.59 +            "insert into lpit_project (name, node, description, repourl, owner) values (?, ?, ?, ?, ?)"
   30.60 +    )
   30.61 +    private val updateStmt = connection.prepareStatement(
   30.62 +            "update lpit_project set name = ?, node = ?, description = ?, repourl = ?, owner = ? where projectid = ?"
   30.63 +    )
   30.64 +
   30.65 +    override fun mapResult(rs: ResultSet): Project {
   30.66 +        val proj = Project(rs.getInt("projectid"))
   30.67 +        proj.name = rs.getString("name")
   30.68 +        proj.node = rs.getString("node")
   30.69 +        proj.description = rs.getString("description")
   30.70 +        proj.repoUrl = rs.getString("repourl")
   30.71 +        proj.owner = PGUserDao.mapResult(rs).takeUnless { rs.wasNull() }
   30.72 +        return proj
   30.73 +    }
   30.74 +
   30.75 +    override fun getIssueSummary(project: Project): IssueSummary {
   30.76 +        issueSummaryStmt.setInt(1, project.id)
   30.77 +        val result = issueSummaryStmt.executeQuery()
   30.78 +        val summary = IssueSummary()
   30.79 +        while (result.next()) {
   30.80 +            val phase = result.getInt("phase")
   30.81 +            val total = result.getInt("total")
   30.82 +            when (phase) {
   30.83 +                0 -> summary.open = total
   30.84 +                1 -> summary.active = total
   30.85 +                2 -> summary.done = total
   30.86 +            }
   30.87 +        }
   30.88 +        return summary
   30.89 +    }
   30.90 +
   30.91 +    private fun setColumns(stmt: PreparedStatement, instance: Project): Int {
   30.92 +        var column = 0
   30.93 +        stmt.setString(++column, instance.name)
   30.94 +        stmt.setString(++column, instance.node)
   30.95 +        Functions.setStringOrNull(stmt, ++column, instance.description)
   30.96 +        Functions.setStringOrNull(stmt, ++column, instance.repoUrl)
   30.97 +        setForeignKeyOrNull(stmt, ++column, instance.owner, User::id)
   30.98 +        return column
   30.99 +    }
  30.100 +
  30.101 +    override fun save(instance: Project) {
  30.102 +        setColumns(insertStmt, instance)
  30.103 +        insertStmt.executeUpdate()
  30.104 +    }
  30.105 +
  30.106 +    override fun update(instance: Project): Boolean {
  30.107 +        var column = setColumns(updateStmt, instance)
  30.108 +        updateStmt.setInt(++column, instance.id)
  30.109 +        return updateStmt.executeUpdate() > 0
  30.110 +    }
  30.111 +
  30.112 +    override fun list(): List<Project> {
  30.113 +        return super.list(listStmt)
  30.114 +    }
  30.115 +
  30.116 +    override fun find(id: Int): Project? {
  30.117 +        findStmt.setInt(1, id)
  30.118 +        return super.find(findStmt)
  30.119 +    }
  30.120 +
  30.121 +    override fun findByNode(node: String): Project? {
  30.122 +        findByNodeStmt.setString(1, node)
  30.123 +        return super.find(findByNodeStmt)
  30.124 +    }
  30.125 +}
  30.126 \ No newline at end of file
    31.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGUserDao.kt	Thu Nov 19 13:58:54 2020 +0100
    31.3 @@ -0,0 +1,95 @@
    31.4 +/*
    31.5 + * Copyright 2020 Mike Becker. All rights reserved.
    31.6 + *
    31.7 + * Redistribution and use in source and binary forms, with or without
    31.8 + * modification, are permitted provided that the following conditions are met:
    31.9 + *
   31.10 + * 1. Redistributions of source code must retain the above copyright
   31.11 + * notice, this list of conditions and the following disclaimer.
   31.12 + *
   31.13 + * 2. Redistributions in binary form must reproduce the above copyright
   31.14 + * notice, this list of conditions and the following disclaimer in the
   31.15 + * documentation and/or other materials provided with the distribution.
   31.16 + *
   31.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   31.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   31.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   31.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   31.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   31.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   31.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   31.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   31.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   31.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   31.27 + *
   31.28 + */
   31.29 +
   31.30 +package de.uapcore.lightpit.dao.postgres
   31.31 +
   31.32 +import de.uapcore.lightpit.dao.AbstractUserDao
   31.33 +import de.uapcore.lightpit.dao.Functions
   31.34 +import de.uapcore.lightpit.entities.User
   31.35 +import java.sql.Connection
   31.36 +import java.sql.ResultSet
   31.37 +
   31.38 +class PGUserDao(connection: Connection) : AbstractUserDao() {
   31.39 +
   31.40 +    companion object {
   31.41 +        fun mapResult(rs: ResultSet): User {
   31.42 +            val id = rs.getInt("userid")
   31.43 +            return if (rs.wasNull()) {
   31.44 +                User(-1)
   31.45 +            } else {
   31.46 +                val user = User(id)
   31.47 +                user.username = rs.getString("username")
   31.48 +                user.givenname = Functions.getSafeString(rs, "givenname")
   31.49 +                user.lastname = Functions.getSafeString(rs, "lastname")
   31.50 +                user.mail = Functions.getSafeString(rs, "mail")
   31.51 +                user
   31.52 +            }
   31.53 +        }
   31.54 +    }
   31.55 +
   31.56 +    private val listStmt = connection.prepareStatement(
   31.57 +            "select userid, username, lastname, givenname, mail " +
   31.58 +                    "from lpit_user where userid >= 0 " +
   31.59 +                    "order by username")
   31.60 +    private val findStmt = connection.prepareStatement(
   31.61 +            "select userid, username, lastname, givenname, mail " +
   31.62 +                    "from lpit_user where userid = ? ")
   31.63 +    private val findByUsernameStmt = connection.prepareStatement(
   31.64 +            "select userid, username, lastname, givenname, mail " +
   31.65 +                    "from lpit_user where lower(username) = lower(?) ")
   31.66 +    private val insertStmt = connection.prepareStatement("insert into lpit_user (username, lastname, givenname, mail) values (?, ?, ?, ?)")
   31.67 +    private val updateStmt = connection.prepareStatement("update lpit_user set lastname = ?, givenname = ?, mail = ? where userid = ?")
   31.68 +
   31.69 +    override fun mapResult(rs: ResultSet): User = Companion.mapResult(rs)
   31.70 +
   31.71 +    override fun save(instance: User) {
   31.72 +        insertStmt.setString(1, instance.username)
   31.73 +        Functions.setStringOrNull(insertStmt, 2, instance.lastname)
   31.74 +        Functions.setStringOrNull(insertStmt, 3, instance.givenname)
   31.75 +        Functions.setStringOrNull(insertStmt, 4, instance.mail)
   31.76 +        insertStmt.executeUpdate()
   31.77 +    }
   31.78 +
   31.79 +    override fun update(instance: User): Boolean {
   31.80 +        Functions.setStringOrNull(updateStmt, 1, instance.lastname)
   31.81 +        Functions.setStringOrNull(updateStmt, 2, instance.givenname)
   31.82 +        Functions.setStringOrNull(updateStmt, 3, instance.mail)
   31.83 +        updateStmt.setInt(4, instance.id)
   31.84 +        return updateStmt.executeUpdate() > 0
   31.85 +    }
   31.86 +
   31.87 +    override fun list(): List<User> = super.list(listStmt)
   31.88 +
   31.89 +    override fun find(id: Int): User? {
   31.90 +        findStmt.setInt(1, id)
   31.91 +        return super.find(findStmt)
   31.92 +    }
   31.93 +
   31.94 +    override fun findByUsername(username: String): User? {
   31.95 +        findByUsernameStmt.setString(1, username)
   31.96 +        return super.find(findByUsernameStmt)
   31.97 +    }
   31.98 +}
   31.99 \ No newline at end of file
    32.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    32.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGVersionDao.kt	Thu Nov 19 13:58:54 2020 +0100
    32.3 @@ -0,0 +1,105 @@
    32.4 +/*
    32.5 + * Copyright 2020 Mike Becker. All rights reserved.
    32.6 + *
    32.7 + * Redistribution and use in source and binary forms, with or without
    32.8 + * modification, are permitted provided that the following conditions are met:
    32.9 + *
   32.10 + * 1. Redistributions of source code must retain the above copyright
   32.11 + * notice, this list of conditions and the following disclaimer.
   32.12 + *
   32.13 + * 2. Redistributions in binary form must reproduce the above copyright
   32.14 + * notice, this list of conditions and the following disclaimer in the
   32.15 + * documentation and/or other materials provided with the distribution.
   32.16 + *
   32.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   32.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   32.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   32.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   32.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   32.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   32.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   32.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   32.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   32.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   32.27 + *
   32.28 + */
   32.29 +
   32.30 +package de.uapcore.lightpit.dao.postgres
   32.31 +
   32.32 +import de.uapcore.lightpit.dao.AbstractVersionDao
   32.33 +import de.uapcore.lightpit.entities.Project
   32.34 +import de.uapcore.lightpit.entities.Version
   32.35 +import de.uapcore.lightpit.entities.VersionStatus
   32.36 +import java.sql.Connection
   32.37 +import java.sql.PreparedStatement
   32.38 +import java.sql.ResultSet
   32.39 +
   32.40 +class PGVersionDao(connection: Connection) : AbstractVersionDao() {
   32.41 +
   32.42 +    companion object {
   32.43 +        fun mapResult(rs: ResultSet): Version {
   32.44 +            val id = rs.getInt("versionid")
   32.45 +            return if (rs.wasNull()) {
   32.46 +                Version(-1)
   32.47 +            } else {
   32.48 +                val version = Version(id)
   32.49 +                version.name = rs.getString("name")
   32.50 +                version.node = rs.getString("node")
   32.51 +                version.ordinal = rs.getInt("ordinal")
   32.52 +                version.status = VersionStatus.valueOf(rs.getString("status"))
   32.53 +                version
   32.54 +            }
   32.55 +        }
   32.56 +    }
   32.57 +
   32.58 +    private val query = "select versionid, project, name, node, ordinal, status from lpit_version"
   32.59 +    private val listStmt = connection.prepareStatement(query + " where project = ? " +
   32.60 +            "order by ordinal desc, lower(name) desc")
   32.61 +    private val findStmt = connection.prepareStatement("$query where versionid = ?")
   32.62 +    private val findByNodeStmt = connection.prepareStatement("$query where project = ? and node = ?")
   32.63 +    private val insertStmt = connection.prepareStatement(
   32.64 +            "insert into lpit_version (name, node, ordinal, status, project) values (?, ?, ?, ?::version_status, ?)"
   32.65 +    )
   32.66 +    private val updateStmt = connection.prepareStatement(
   32.67 +            "update lpit_version set name = ?, node = ?, ordinal = ?, status = ?::version_status where versionid = ?"
   32.68 +    )
   32.69 +
   32.70 +    override fun mapResult(rs: ResultSet): Version = Companion.mapResult(rs)
   32.71 +
   32.72 +    private fun setFields(stmt: PreparedStatement, instance: Version): Int {
   32.73 +        var column = 0
   32.74 +        stmt.setString(++column, instance.name)
   32.75 +        stmt.setString(++column, instance.node)
   32.76 +        stmt.setInt(++column, instance.ordinal)
   32.77 +        stmt.setString(++column, instance.status.name)
   32.78 +        return column
   32.79 +    }
   32.80 +
   32.81 +    override fun save(instance: Version, parent: Project) {
   32.82 +        var column = setFields(insertStmt, instance)
   32.83 +        insertStmt.setInt(++column, parent.id)
   32.84 +        insertStmt.executeUpdate()
   32.85 +    }
   32.86 +
   32.87 +    override fun update(instance: Version): Boolean {
   32.88 +        var column = setFields(updateStmt, instance)
   32.89 +        updateStmt.setInt(++column, instance.id)
   32.90 +        return updateStmt.executeUpdate() > 0
   32.91 +    }
   32.92 +
   32.93 +    override fun list(parent: Project): List<Version> {
   32.94 +        listStmt.setInt(1, parent.id)
   32.95 +        return super.list(listStmt)
   32.96 +    }
   32.97 +
   32.98 +    override fun find(id: Int): Version? {
   32.99 +        findStmt.setInt(1, id)
  32.100 +        return super.find(findStmt)
  32.101 +    }
  32.102 +
  32.103 +    override fun findByNode(parent: Project, node: String): Version? {
  32.104 +        findByNodeStmt.setInt(1, parent.id)
  32.105 +        findByNodeStmt.setString(2, node)
  32.106 +        return super.find(findByNodeStmt)
  32.107 +    }
  32.108 +}
  32.109 \ No newline at end of file
    33.1 --- a/src/main/kotlin/de/uapcore/lightpit/entities/User.kt	Fri Nov 06 10:50:32 2020 +0100
    33.2 +++ b/src/main/kotlin/de/uapcore/lightpit/entities/User.kt	Thu Nov 19 13:58:54 2020 +0100
    33.3 @@ -26,7 +26,7 @@
    33.4  package de.uapcore.lightpit.entities
    33.5  
    33.6  data class User(val id: Int) {
    33.7 -    var username = "anonymous"
    33.8 +    var username = ""
    33.9      var mail = ""
   33.10      var givenname = ""
   33.11      var lastname = ""

mercurial