# HG changeset patch # User mike@uapl01.localdomain # Date 1605790734 -3600 # Node ID 86b5d8a1662f7f2199f0d7cb03c2d1f8c22d4b0f # Parent 4f912cd42876a5f5f0205dfa67b7e09b95f9a3b8 migrates DAO classes diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java --- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Fri Nov 06 10:50:32 2020 +0100 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Thu Nov 19 13:58:54 2020 +0100 @@ -28,8 +28,8 @@ */ package de.uapcore.lightpit; -import de.uapcore.lightpit.dao.DataAccessObjects; -import de.uapcore.lightpit.dao.postgres.PGDataAccessObjects; +import de.uapcore.lightpit.dao.DaoProvider; +import de.uapcore.lightpit.dao.postgres.PGDaoProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -100,15 +100,15 @@ * @param connection the SQL connection * @return a set of data access objects */ - private DataAccessObjects createDataAccessObjects(Connection connection) throws SQLException { + private DaoProvider createDataAccessObjects(Connection connection) throws SQLException { final var df = (DataSourceProvider) getServletContext().getAttribute(DataSourceProvider.Companion.getSC_ATTR_NAME()); if (df.getDialect() == DataSourceProvider.Dialect.Postgres) { - return new PGDataAccessObjects(connection); + return new PGDaoProvider(connection); } throw new UnsupportedOperationException("Non-exhaustive if-else - this is a bug."); } - private void invokeMapping(Map.Entry mapping, HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException { + private void invokeMapping(Map.Entry mapping, HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws IOException { final var pathPattern = mapping.getKey(); final var method = mapping.getValue(); try { @@ -121,7 +121,7 @@ } else if (paramTypes[i].isAssignableFrom(HttpServletResponse.class)) { paramValues[i] = resp; } - if (paramTypes[i].isAssignableFrom(DataAccessObjects.class)) { + if (paramTypes[i].isAssignableFrom(DaoProvider.class)) { paramValues[i] = dao; } if (paramTypes[i].isAssignableFrom(PathParameters.class)) { @@ -180,7 +180,7 @@ paramsInjectible &= HttpServletRequest.class.isAssignableFrom(param) || HttpServletResponse.class.isAssignableFrom(param) || PathParameters.class.isAssignableFrom(param) - || DataAccessObjects.class.isAssignableFrom(param); + || DaoProvider.class.isAssignableFrom(param); } if (paramsInjectible) { try { diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/dao/ChildEntityDao.java --- a/src/main/java/de/uapcore/lightpit/dao/ChildEntityDao.java Fri Nov 06 10:50:32 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.dao; - -import java.sql.SQLException; -import java.util.List; - -public interface ChildEntityDao { - - /** - * Lists all entities being a child of the specified parent. - * @param parent the parent - * @return the list of child instances - * @throws SQLException on any kind of SQL errors - */ - List list(P parent) throws SQLException; - - /** - * Finds an entity by its integer ID. - * It is not guaranteed that referenced entities are automatically joined. - * - * @param id the id - * @return the enity or null if there is no such entity - * @throws SQLException on any kind of SQL errors - */ - T find(int id) throws SQLException; - - /** - * Inserts an instance into database. - * It is not guaranteed that generated fields will be updated in the instance. - * - * @param instance the instance to insert - * @param parent a reference to the parent - * @throws SQLException on any kind of SQL errors - */ - void save(T instance, P parent) throws SQLException; - - /** - * Updates an instance in the database. - * - * @param instance the instance to insert - * @return true if an instance has been updated, false if no instance with the specified ID was found - * @throws SQLException on any kind of SQL errors - */ - boolean update(T instance) throws SQLException; - - /** - * Updates an instance in the database changing the parent. - * This operation is not supported by default. - * - * @param instance the instance to insert - * @param newParent a reference to the new parent - * @return true if an instance has been updated, false if no instance with the specified ID was found - * @throws SQLException on any kind of SQL errors - * @see #isChangingParentSupported() - */ - default boolean update(T instance, P newParent) throws SQLException { - throw new UnsupportedOperationException(); - } - - /** - * Returns true if changing the parent is supported by this DAO. - * This method must return true, if {@link #update(Object, Object)} is implemented. - * @return true, if changing the parent is supported - */ - default boolean isChangingParentSupported() { - return false; - } - - /** - * Inserts or updates an instance in the database. - * Tries an update first and if that fails, performs a save. - * If changing a parent is not supported by this DAO, - * specifying an alternate parent for an existing instance has no effect. - * - * @param instance the instance to insert or update - * @param parent a reference to the parent - * @throws SQLException on any kind of SQL errors - * @see #update(Object) - * @see #update(Object, Object) - * @see #save(Object, Object) - */ - default void saveOrUpdate(T instance, P parent) throws SQLException { - if (isChangingParentSupported()) { - if (!update(instance, parent)) save(instance, parent); - } else { - if (!update(instance)) save(instance, parent); - } - } -} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/dao/ComponentDao.java --- a/src/main/java/de/uapcore/lightpit/dao/ComponentDao.java Fri Nov 06 10:50:32 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.dao; - -import de.uapcore.lightpit.entities.Component; -import de.uapcore.lightpit.entities.Project; - -import java.sql.SQLException; - -public interface ComponentDao extends ChildEntityDao { - Component findByNode(Project parent, String node) throws SQLException; -} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/dao/DataAccessObjects.java --- a/src/main/java/de/uapcore/lightpit/dao/DataAccessObjects.java Fri Nov 06 10:50:32 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.dao; - -public interface DataAccessObjects { - UserDao getUserDao(); - - ProjectDao getProjectDao(); - - VersionDao getVersionDao(); - - ComponentDao getComponentDao(); - - IssueDao getIssueDao(); -} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/dao/IssueDao.java --- a/src/main/java/de/uapcore/lightpit/dao/IssueDao.java Fri Nov 06 10:50:32 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.dao; - -import de.uapcore.lightpit.entities.*; - -import java.sql.SQLException; -import java.util.List; - -public interface IssueDao extends ChildEntityDao { - - /** - * Lists all issues that are related to the specified component and version. - * If component or version is null, search for issues that are not assigned to any - * component or version, respectively. - * - * @param project the project - * @param component the component or null - * @param version the version or null - * @return a list of issues - * @throws SQLException on any kind of SQL error - */ - List list(Project project, Component component, Version version) throws SQLException; - - /** - * Lists all issues that are related to the specified version. - * If the version is null, lists issues that are not assigned to any version. - * - * @param project the project (mandatory) - * @param version the version or null - * @return a list of issues - * @throws SQLException on any kind of SQL error - */ - List list(Project project, Version version) throws SQLException; - - /** - * Lists all issues that are related to the specified component. - * If the component is null, lists issues that are not assigned to a component. - * - * @param project the project (mandatory) - * @param component the component or null - * @return a list of issues - * @throws SQLException on any kind of SQL error - */ - List list(Project project, Component component) throws SQLException; - - /** - * Lists all comments for a specific issue in chronological order. - * - * @param issue the issue - * @return the list of comments - * @throws SQLException on any kind of SQL error - */ - List listComments(Issue issue) throws SQLException; - - /** - * Stores the specified comment in database. - * This is an update-or-insert operation. - * - * @param issue the issue to save the comment for - * @param comment the comment to save - * @throws SQLException on any kind of SQL error - */ - void saveComment(Issue issue, IssueComment comment) throws SQLException; - - /** - * Saves an instances to the database. - * Implementations of this DAO must guarantee that the generated ID is stored in the instance. - * - * @param instance the instance to insert - * @param project the parent project - * @throws SQLException on any kind of SQL error - * @see Issue#setId(int) - */ - @Override - void save(Issue instance, Project project) throws SQLException; - - /** - * Retrieves the affected, scheduled and resolved versions for the specified issue. - * - * @param issue the issue to join the information for - * @throws SQLException on any kind of SQL error - */ - void joinVersionInformation(Issue issue) throws SQLException; -} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/dao/ProjectDao.java --- a/src/main/java/de/uapcore/lightpit/dao/ProjectDao.java Fri Nov 06 10:50:32 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.dao; - -import de.uapcore.lightpit.entities.IssueSummary; -import de.uapcore.lightpit.entities.Project; - -import java.sql.SQLException; - -public interface ProjectDao extends RootEntityDao { - IssueSummary getIssueSummary(Project project) throws SQLException; - - Project findByNode(String node) throws SQLException; -} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/dao/RootEntityDao.java --- a/src/main/java/de/uapcore/lightpit/dao/RootEntityDao.java Fri Nov 06 10:50:32 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.dao; - -import java.sql.SQLException; -import java.util.List; - -public interface RootEntityDao { - - /** - * Lists all entities. - * @return a list of all entities - * @throws SQLException on any kind of SQL errors - */ - List list() throws SQLException; - - /** - * Finds an entity by its integer ID. - * It is not guaranteed that referenced entities are automatically joined. - * - * @param id the id - * @return the enity or null if there is no such entity - * @throws SQLException on any kind of SQL errors - */ - T find(int id) throws SQLException; - - /** - * Inserts an instance into database. - * It is not guaranteed that generated fields will be updated in the instance. - * - * @param instance the instance to insert - * @throws SQLException on any kind of SQL errors - */ - void save(T instance) throws SQLException; - - /** - * Updates an instance in the database. - * - * @param instance the instance to insert - * @return true if an instance has been updated, false if no instance with the specified ID was found - * @throws SQLException on any kind of SQL errors - */ - boolean update(T instance) throws SQLException; - - /** - * Inserts or updates an instance in the database. - * Tries an update first and if that fails, performs a save. - * - * @param instance the instance to insert or update - * @throws SQLException on any kind of SQL errors - * @see #update(Object) - * @see #save(Object) - */ - default void saveOrUpdate(T instance) throws SQLException { - if (!update(instance)) save(instance); - } -} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/dao/UserDao.java --- a/src/main/java/de/uapcore/lightpit/dao/UserDao.java Fri Nov 06 10:50:32 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.dao; - -import de.uapcore.lightpit.entities.User; - -import java.sql.SQLException; -import java.util.Optional; - -public interface UserDao extends RootEntityDao { - - /** - * Tries to find a user by their username. - * The search is case-insensitive. - * - * @param username the username - * @return the user object or an empty optional if no such user exists - * @throws SQLException - */ - Optional findByUsername(String username) throws SQLException; -} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/dao/VersionDao.java --- a/src/main/java/de/uapcore/lightpit/dao/VersionDao.java Fri Nov 06 10:50:32 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.dao; - -import de.uapcore.lightpit.entities.Project; -import de.uapcore.lightpit.entities.Version; - -import java.sql.SQLException; - -public interface VersionDao extends ChildEntityDao { - Version findByNode(Project parent, String node) throws SQLException; -} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/dao/postgres/PGComponentDao.java --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGComponentDao.java Fri Nov 06 10:50:32 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,130 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.dao.postgres; - -import de.uapcore.lightpit.dao.ComponentDao; -import de.uapcore.lightpit.dao.Functions; -import de.uapcore.lightpit.entities.Component; -import de.uapcore.lightpit.entities.Project; -import de.uapcore.lightpit.entities.User; -import de.uapcore.lightpit.types.WebColor; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; - -public final class PGComponentDao implements ComponentDao { - - private final PreparedStatement insert, update, list, find, findByNode; - - public PGComponentDao(Connection connection) throws SQLException { - final var query = "select id, name, node, color, ordinal, description, " + - "userid, username, givenname, lastname, mail " + - "from lpit_component " + - "left join lpit_user on lead = userid"; - - list = connection.prepareStatement(query + " where project = ? order by ordinal, lower(name)"); - - find = connection.prepareStatement(query + " where id = ? "); - - findByNode = connection.prepareStatement(query + " where project = ? and node = ?"); - - insert = connection.prepareStatement( - "insert into lpit_component (name, node, color, ordinal, description, lead, project) values (?, ?, ?, ?, ?, ?, ?)" - ); - - update = connection.prepareStatement( - "update lpit_component set name = ?, node = ?, color = ?, ordinal = ?, description = ?, lead = ? where id = ?" - ); - } - - private static Component mapColumns(ResultSet result) throws SQLException { - final var component = new Component(result.getInt("id")); - component.setName(result.getString("name")); - component.setNode(result.getString("node")); - try { - component.setColor(new WebColor(result.getString("color"))); - } catch (IllegalArgumentException ex) { - // if someone tempered with the database we default the color to black - component.setColor(new WebColor("000000")); - } - component.setOrdinal(result.getInt("ordinal")); - component.setDescription(result.getString("description")); - component.setLead(PGUserDao.mapColumns(result)); - return component; - } - - private static int setColumns(PreparedStatement stmt, Component instance) throws SQLException { - int column = 0; - stmt.setString(++column, instance.getName()); - stmt.setString(++column, instance.getNode()); - stmt.setString(++column, instance.getColor().getHex()); - stmt.setInt(++column, instance.getOrdinal()); - Functions.setStringOrNull(stmt, ++column, instance.getDescription()); - Functions.setForeignKeyOrNull(stmt, ++column, instance.getLead(), User::getId); - return column; - } - - @Override - public void save(Component instance, Project project) throws SQLException { - int column = setColumns(insert, instance); - insert.setInt(++column, project.getId()); - insert.executeUpdate(); - } - - @Override - public boolean update(Component instance) throws SQLException { - if (instance.getId() < 0) return false; - int column = setColumns(update, instance); - update.setInt(++column, instance.getId()); - return update.executeUpdate() > 0; - } - - - @Override - public List list(Project project) throws SQLException { - list.setInt(1, project.getId()); - return Functions.list(list, PGComponentDao::mapColumns); - } - - @Override - public Component find(int id) throws SQLException { - find.setInt(1, id); - return Functions.find(find, PGComponentDao::mapColumns); - } - - @Override - public Component findByNode(Project project, String node) throws SQLException { - findByNode.setInt(1, project.getId()); - findByNode.setString(2, node);; - return Functions.find(findByNode, PGComponentDao::mapColumns); - } -} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/dao/postgres/PGDataAccessObjects.java --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGDataAccessObjects.java Fri Nov 06 10:50:32 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.dao.postgres; - -import de.uapcore.lightpit.dao.*; - -import java.sql.Connection; -import java.sql.SQLException; - -public class PGDataAccessObjects implements DataAccessObjects { - - private final UserDao userDao; - private final ProjectDao projectDao; - private final VersionDao versionDao; - private final ComponentDao componentDao; - private final IssueDao issueDao; - - public PGDataAccessObjects(Connection connection) throws SQLException { - userDao = new PGUserDao(connection); - projectDao = new PGProjectDao(connection); - versionDao = new PGVersionDao(connection); - componentDao = new PGComponentDao(connection); - issueDao = new PGIssueDao(connection); - } - - @Override - public UserDao getUserDao() { - return userDao; - } - - @Override - public ProjectDao getProjectDao() { - return projectDao; - } - - @Override - public ComponentDao getComponentDao() { - return componentDao; - } - - @Override - public VersionDao getVersionDao() { - return versionDao; - } - - @Override - public IssueDao getIssueDao() { - return issueDao; - } -} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java Fri Nov 06 10:50:32 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,306 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.dao.postgres; - -import de.uapcore.lightpit.dao.IssueDao; -import de.uapcore.lightpit.entities.*; - -import java.sql.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -import static de.uapcore.lightpit.dao.Functions.*; - -public final class PGIssueDao implements IssueDao { - - private final PreparedStatement insert, update, list, listForVersion, find; - private final PreparedStatement affectedVersions, resolvedVersions; - private final PreparedStatement clearAffected, clearResolved; - private final PreparedStatement insertAffected, insertResolved; - private final PreparedStatement insertComment, updateComment, listComments; - - public PGIssueDao(Connection connection) throws SQLException { - final var query = "select issueid, i.project, p.name as projectname, p.node as projectnode, "+ - "component, c.name as componentname, c.node as componentnode, " + - "status, category, subject, i.description, " + - "userid, username, givenname, lastname, mail, " + - "created, updated, eta " + - "from lpit_issue i " + - "join lpit_project p on i.project = projectid " + - "left join lpit_component c on component = c.id " + - "left join lpit_user on userid = assignee "; - - list = connection.prepareStatement(query + - "where i.project = ? and coalesce(component, -1) = coalesce(?, component, -1)"); - - listForVersion = connection.prepareStatement( - "with issue_version as ( "+ - "select issueid, versionid from lpit_issue_affected_version union "+ - "select issueid, versionid from lpit_issue_resolved_version) "+ - query + - "left join issue_version using (issueid) "+ - "where i.project = ? "+ - "and coalesce(versionid,-1) = ? and coalesce(component, -1) = coalesce(?, component, -1)" - ); - - find = connection.prepareStatement(query + "where issueid = ? "); - - insert = connection.prepareStatement( - "insert into lpit_issue (project, component, status, category, subject, description, assignee, eta) " + - "values (?, ?, ?::issue_status, ?::issue_category, ?, ?, ?, ?) returning issueid" - ); - update = connection.prepareStatement( - "update lpit_issue set " + - "updated = now(), component = ?, status = ?::issue_status, category = ?::issue_category, " + - "subject = ?, description = ?, assignee = ?, eta = ? where issueid = ?" - ); - - affectedVersions = connection.prepareStatement( - "select versionid, name, status, ordinal " + - "from lpit_version join lpit_issue_affected_version using (versionid) " + - "where issueid = ? " + - "order by ordinal, name" - ); - clearAffected = connection.prepareStatement("delete from lpit_issue_affected_version where issueid = ?"); - insertAffected = connection.prepareStatement("insert into lpit_issue_affected_version (issueid, versionid) values (?,?)"); - - resolvedVersions = connection.prepareStatement( - "select versionid, name, status, ordinal " + - "from lpit_version v join lpit_issue_resolved_version using (versionid) " + - "where issueid = ? " + - "order by ordinal, name" - ); - clearResolved = connection.prepareStatement("delete from lpit_issue_resolved_version where issueid = ?"); - insertResolved = connection.prepareStatement("insert into lpit_issue_resolved_version (issueid, versionid) values (?,?)"); - - insertComment = connection.prepareStatement( - "insert into lpit_issue_comment (issueid, comment, userid) values (?, ? ,?)" - ); - updateComment = connection.prepareStatement( - "update lpit_issue_comment set comment = ?, updated = now(), updatecount = updatecount+1 where commentid = ?" - ); - listComments = connection.prepareStatement( - "select * from lpit_issue_comment left join lpit_user using (userid) where issueid = ? order by created" - ); - } - - private Issue mapColumns(ResultSet result) throws SQLException { - final var project = new Project(result.getInt("project")); - project.setName(result.getString("projectname")); - project.setNode(result.getString("projectnode")); - var component = new Component(result.getInt("component")); - if (result.wasNull()) { - component = null; - } else { - component.setName(result.getString("componentname")); - component.setNode(result.getString("componentnode")); - } - final var issue = new Issue(result.getInt("issueid")); - issue.setProject(project); - issue.setComponent(component); - issue.setStatus(IssueStatus.valueOf(result.getString("status"))); - issue.setCategory(IssueCategory.valueOf(result.getString("category"))); - issue.setSubject(result.getString("subject")); - issue.setDescription(result.getString("description")); - issue.setAssignee(PGUserDao.mapColumns(result)); - issue.setCreated(result.getTimestamp("created")); - issue.setUpdated(result.getTimestamp("updated")); - issue.setEta(result.getDate("eta")); - return issue; - } - - private Version mapVersion(ResultSet result) throws SQLException { - final var version = new Version(result.getInt("versionid")); - version.setName(result.getString("name")); - version.setOrdinal(result.getInt("ordinal")); - version.setStatus(VersionStatus.valueOf(result.getString("status"))); - return version; - } - - private void updateVersionLists(Issue instance) throws SQLException { - clearAffected.setInt(1, instance.getId()); - clearResolved.setInt(1, instance.getId()); - insertAffected.setInt(1, instance.getId()); - insertResolved.setInt(1, instance.getId()); - clearAffected.executeUpdate(); - clearResolved.executeUpdate(); - for (Version v : instance.getAffectedVersions()) { - insertAffected.setInt(2, v.getId()); - insertAffected.executeUpdate(); - } - for (Version v : instance.getResolvedVersions()) { - insertResolved.setInt(2, v.getId()); - insertResolved.executeUpdate(); - } - } - - private int setData(PreparedStatement stmt, int column, Issue instance) throws SQLException { - setForeignKeyOrNull(stmt, ++column, instance.getComponent(), Component::getId); - stmt.setString(++column, instance.getStatus().name()); - stmt.setString(++column, instance.getCategory().name()); - stmt.setString(++column, instance.getSubject()); - setStringOrNull(stmt, ++column, instance.getDescription()); - setForeignKeyOrNull(stmt, ++column, instance.getAssignee(), User::getId); - setDateOrNull(stmt, ++column, instance.getEta()); - return column; - } - - @Override - public void save(Issue instance, Project project) throws SQLException { - Objects.requireNonNull(instance.getSubject()); - instance.setProject(project); - int column = 0; - insert.setInt(++column, instance.getProject().getId()); - setData(insert, column, instance); - // insert and retrieve the ID - final var rs = insert.executeQuery(); - rs.next(); - instance.setId(rs.getInt(1)); - updateVersionLists(instance); - } - - @Override - public boolean update(Issue instance) throws SQLException { - if (instance.getId() < 0) return false; - Objects.requireNonNull(instance.getSubject()); - int column = setData(update, 0, instance); - update.setInt(++column, instance.getId()); - boolean success = update.executeUpdate() > 0; - if (success) { - updateVersionLists(instance); - return true; - } else { - return false; - } - } - - private List executeQuery(PreparedStatement query) throws SQLException { - List issues = new ArrayList<>(); - try (var result = query.executeQuery()) { - while (result.next()) { - issues.add(mapColumns(result)); - } - } - return issues; - } - - @Override - public List list(Project project) throws SQLException { - list.setInt(1, project.getId()); - list.setNull(2, Types.INTEGER); - return executeQuery(list); - } - - @Override - public List list(Project project, Component component, Version version) throws SQLException { - listForVersion.setInt(1, project.getId()); - listForVersion.setInt(2, Optional.ofNullable(version).map(Version::getId).orElse(-1)); - listForVersion.setInt(3, Optional.ofNullable(component).map(Component::getId).orElse(-1)); - return executeQuery(listForVersion); - } - - @Override - public List list(Project project, Version version) throws SQLException { - listForVersion.setInt(1, project.getId()); - listForVersion.setInt(2, Optional.ofNullable(version).map(Version::getId).orElse(-1)); - listForVersion.setNull(3, Types.INTEGER); - return executeQuery(listForVersion); - } - - @Override - public List list(Project project, Component component) throws SQLException { - list.setInt(1, project.getId()); - list.setInt(2, Optional.ofNullable(component).map(Component::getId).orElse(-1)); - return executeQuery(list); - } - - @Override - public Issue find(int id) throws SQLException { - find.setInt(1, id); - try (var result = find.executeQuery()) { - if (result.next()) { - return mapColumns(result); - } else { - return null; - } - } - } - - private List listVersions(PreparedStatement stmt, Issue issue) throws SQLException { - stmt.setInt(1, issue.getId()); - List versions = new ArrayList<>(); - try (var result = stmt.executeQuery()) { - while (result.next()) { - versions.add(mapVersion(result)); - } - } - return versions; - } - - @Override - public void joinVersionInformation(Issue issue) throws SQLException { - Objects.requireNonNull(issue.getProject()); - issue.setAffectedVersions(listVersions(affectedVersions, issue)); - issue.setResolvedVersions(listVersions(resolvedVersions, issue)); - } - - @Override - public List listComments(Issue issue) throws SQLException { - listComments.setInt(1, issue.getId()); - List comments = new ArrayList<>(); - try (var result = listComments.executeQuery()) { - while (result.next()) { - final var comment = new IssueComment(result.getInt("commentid")); - comment.setCreated(result.getTimestamp("created")); - comment.setUpdated(result.getTimestamp("updated")); - comment.setUpdateCount(result.getInt("updatecount")); - comment.setComment(result.getString("comment")); - comment.setAuthor(PGUserDao.mapColumns(result)); - comments.add(comment); - } - } - return comments; - } - - @Override - public void saveComment(Issue issue, IssueComment comment) throws SQLException { - if (comment.getId() >= 0) { - updateComment.setString(1, comment.getComment()); - updateComment.setInt(2, comment.getId()); - updateComment.execute(); - } else { - insertComment.setInt(1, issue.getId()); - insertComment.setString(2, comment.getComment()); - setForeignKeyOrNull(insertComment, 3, comment.getAuthor(), User::getId); - insertComment.execute(); - } - } -} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java Fri Nov 06 10:50:32 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,151 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.dao.postgres; - -import de.uapcore.lightpit.dao.Functions; -import de.uapcore.lightpit.dao.ProjectDao; -import de.uapcore.lightpit.entities.IssueSummary; -import de.uapcore.lightpit.entities.Project; -import de.uapcore.lightpit.entities.User; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; - -import static de.uapcore.lightpit.dao.Functions.setForeignKeyOrNull; -import static de.uapcore.lightpit.dao.Functions.setStringOrNull; - -public final class PGProjectDao implements ProjectDao { - - private final PreparedStatement insert, update, list, find, findByNode; - private final PreparedStatement issue_summary; - - public PGProjectDao(Connection connection) throws SQLException { - final var query = "select projectid, name, node, description, repourl, " + - "userid, username, lastname, givenname, mail " + - "from lpit_project " + - "left join lpit_user owner on lpit_project.owner = owner.userid "; - - list = connection.prepareStatement(query + " order by name"); - - find = connection.prepareStatement(query + " where projectid = ?"); - findByNode = connection.prepareStatement(query + " where node = ?"); - - issue_summary = connection.prepareStatement( - "select phase, count(*) as total "+ - "from lpit_issue " + - "join lpit_issue_phases using(status) " + - "where project = ? "+ - "group by phase " - ); - - insert = connection.prepareStatement( - "insert into lpit_project (name, node, description, repourl, owner) values (?, ?, ?, ?, ?)" - ); - update = connection.prepareStatement( - "update lpit_project set name = ?, node = ?, description = ?, repourl = ?, owner = ? where projectid = ?" - ); - } - - private static Project mapColumns(ResultSet result) throws SQLException { - final var proj = new Project(result.getInt("projectid")); - proj.setName(result.getString("name")); - proj.setNode(result.getString("node")); - proj.setDescription(result.getString("description")); - proj.setRepoUrl(result.getString("repourl")); - proj.setOwner(PGUserDao.mapColumns(result)); - - return proj; - } - - public IssueSummary getIssueSummary(Project project) throws SQLException { - issue_summary.setInt(1, project.getId()); - final var result = issue_summary.executeQuery(); - final var summary = new IssueSummary(); - while (result.next()) { - final var phase = result.getInt("phase"); - final var total = result.getInt("total"); - switch(phase) { - case 0: - summary.setOpen(total); - break; - case 1: - summary.setActive(total); - break; - case 2: - summary.setDone(total); - break; - } - } - return summary; - } - - private static int setColumns(PreparedStatement stmt, Project instance) throws SQLException { - int column = 0; - stmt.setString(++column, instance.getName()); - stmt.setString(++column, instance.getNode()); - setStringOrNull(stmt, ++column, instance.getDescription()); - setStringOrNull(stmt, ++column, instance.getRepoUrl()); - setForeignKeyOrNull(stmt, ++column, instance.getOwner(), User::getId); - return column; - } - - @Override - public void save(Project instance) throws SQLException { - setColumns(insert, instance); - insert.executeUpdate(); - } - - @Override - public boolean update(Project instance) throws SQLException { - if (instance.getId() < 0) return false; - int column = setColumns(update, instance); - update.setInt(++column, instance.getId()); - return update.executeUpdate() > 0; - } - - @Override - public List list() throws SQLException { - return Functions.list(list, PGProjectDao::mapColumns); - } - - @Override - public Project find(int id) throws SQLException { - find.setInt(1, id); - return Functions.find(find, PGProjectDao::mapColumns); - } - - @Override - public Project findByNode(String node) throws SQLException { - findByNode.setString(1, node); - return Functions.find(findByNode, PGProjectDao::mapColumns); - } -} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/dao/postgres/PGUserDao.java --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGUserDao.java Fri Nov 06 10:50:32 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,132 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.dao.postgres; - -import de.uapcore.lightpit.dao.UserDao; -import de.uapcore.lightpit.entities.User; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -import static de.uapcore.lightpit.dao.Functions.getSafeString; -import static de.uapcore.lightpit.dao.Functions.setStringOrNull; - -public final class PGUserDao implements UserDao { - - private final PreparedStatement insert, update, list, find, findByUsername; - - public PGUserDao(Connection connection) throws SQLException { - list = connection.prepareStatement( - "select userid, username, lastname, givenname, mail " + - "from lpit_user where userid >= 0 " + - "order by username"); - find = connection.prepareStatement( - "select userid, username, lastname, givenname, mail " + - "from lpit_user where userid = ? "); - - findByUsername = connection.prepareStatement( - "select userid, username, lastname, givenname, mail " + - "from lpit_user where lower(username) = lower(?) "); - - insert = connection.prepareStatement("insert into lpit_user (username, lastname, givenname, mail) values (?, ?, ?, ?)"); - update = connection.prepareStatement("update lpit_user set lastname = ?, givenname = ?, mail = ? where userid = ?"); - } - - static User mapColumns(ResultSet result) throws SQLException { - final int id = result.getInt("userid"); - if (id == 0) return null; - final var user = new User(id); - user.setUsername(result.getString("username")); - user.setGivenname(getSafeString(result, "givenname")); - user.setLastname(getSafeString(result, "lastname")); - user.setMail(getSafeString(result, "mail")); - return user; - } - - @Override - public void save(User instance) throws SQLException { - Objects.requireNonNull(instance.getUsername()); - insert.setString(1, instance.getUsername()); - setStringOrNull(insert, 2, instance.getLastname()); - setStringOrNull(insert, 3, instance.getGivenname()); - setStringOrNull(insert, 4, instance.getMail()); - insert.executeUpdate(); - } - - @Override - public boolean update(User instance) throws SQLException { - if (instance.getId() < 0) return false; - setStringOrNull(update, 1, instance.getLastname()); - setStringOrNull(update, 2, instance.getGivenname()); - setStringOrNull(update, 3, instance.getMail()); - update.setInt(4, instance.getId()); - return update.executeUpdate() > 0; - } - - @Override - public List list() throws SQLException { - List users = new ArrayList<>(); - try (var result = list.executeQuery()) { - while (result.next()) { - users.add(mapColumns(result)); - } - } - return users; - } - - @Override - public User find(int id) throws SQLException { - find.setInt(1, id); - try (var result = find.executeQuery()) { - if (result.next()) { - return mapColumns(result); - } else { - return null; - } - } - } - - @Override - public Optional findByUsername(String username) throws SQLException { - findByUsername.setString(1, username); - try (var result = findByUsername.executeQuery()) { - if (result.next()) { - return Optional.of(mapColumns(result)); - } else { - return Optional.empty(); - } - } - } -} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java Fri Nov 06 10:50:32 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2018 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -package de.uapcore.lightpit.dao.postgres; - -import de.uapcore.lightpit.dao.Functions; -import de.uapcore.lightpit.dao.VersionDao; -import de.uapcore.lightpit.entities.Project; -import de.uapcore.lightpit.entities.Version; -import de.uapcore.lightpit.entities.VersionStatus; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; - -public final class PGVersionDao implements VersionDao { - - private final PreparedStatement insert, update, list, find, findByNode; - - public PGVersionDao(Connection connection) throws SQLException { - final var query = "select versionid, project, name, node, ordinal, status from lpit_version"; - - list = connection.prepareStatement(query + " where project = ? " + - "order by ordinal desc, lower(name) desc"); - find = connection.prepareStatement(query + " where versionid = ?"); - findByNode = connection.prepareStatement(query + " where project = ? and node = ?"); - - insert = connection.prepareStatement( - "insert into lpit_version (name, node, ordinal, status, project) values (?, ?, ?, ?::version_status, ?)" - ); - update = connection.prepareStatement( - "update lpit_version set name = ?, node = ?, ordinal = ?, status = ?::version_status where versionid = ?" - ); - } - - private static Version mapColumns(ResultSet result) throws SQLException { - final var version = new Version(result.getInt("versionid")); - version.setName(result.getString("name")); - version.setNode(result.getString("node")); - version.setOrdinal(result.getInt("ordinal")); - version.setStatus(VersionStatus.valueOf(result.getString("status"))); - return version; - } - - private static int setFields(PreparedStatement stmt, Version instance) throws SQLException { - int column = 0; - stmt.setString(++column, instance.getName()); - stmt.setString(++column, instance.getNode()); - stmt.setInt(++column, instance.getOrdinal()); - stmt.setString(++column, instance.getStatus().name()); - return column; - } - - @Override - public void save(Version instance, Project project) throws SQLException { - int column = setFields(insert, instance); - insert.setInt(++column, project.getId()); - insert.executeUpdate(); - } - - @Override - public boolean update(Version instance) throws SQLException { - if (instance.getId() < 0) return false; - int column = setFields(update, instance); - update.setInt(++column, instance.getId()); - return update.executeUpdate() > 0; - } - - @Override - public List list(Project project) throws SQLException { - list.setInt(1, project.getId()); - return Functions.list(list, PGVersionDao::mapColumns); - } - - @Override - public Version find(int id) throws SQLException { - find.setInt(1, id); - return Functions.find(find, PGVersionDao::mapColumns); - } - - @Override - public Version findByNode(Project project, String node) throws SQLException { - findByNode.setInt(1, project.getId()); - findByNode.setString(2, node);; - return Functions.find(findByNode, PGVersionDao::mapColumns); - } -} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Fri Nov 06 10:50:32 2020 +0100 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Thu Nov 19 13:58:54 2020 +0100 @@ -30,7 +30,7 @@ import de.uapcore.lightpit.*; -import de.uapcore.lightpit.dao.DataAccessObjects; +import de.uapcore.lightpit.dao.DaoProvider; import de.uapcore.lightpit.entities.*; import de.uapcore.lightpit.types.WebColor; import de.uapcore.lightpit.viewmodel.*; @@ -72,7 +72,7 @@ } } - private void populate(ProjectView viewModel, PathParameters pathParameters, DataAccessObjects dao) throws SQLException { + private void populate(ProjectView viewModel, PathParameters pathParameters, DaoProvider dao) throws SQLException { final var projectDao = dao.getProjectDao(); final var versionDao = dao.getVersionDao(); final var componentDao = dao.getComponentDao(); @@ -97,7 +97,7 @@ final var versionNode = pathParameters.get("version"); if ("no-version".equals(versionNode)) { viewModel.setVersionFilter(ProjectView.NO_VERSION); - } else if ("all-versions".equals(versionNode)) { + } else if ("all-versions".equals(versionNode) || versionNode == null) { viewModel.setVersionFilter(ProjectView.ALL_VERSIONS); } else { viewModel.setVersionFilter(versionDao.findByNode(project, versionNode)); @@ -107,7 +107,7 @@ final var componentNode = pathParameters.get("component"); if ("no-component".equals(componentNode)) { viewModel.setComponentFilter(ProjectView.NO_COMPONENT); - } else if ("all-components".equals(componentNode)) { + } else if ("all-components".equals(componentNode) || componentNode == null) { viewModel.setComponentFilter(ProjectView.ALL_COMPONENTS); } else { viewModel.setComponentFilter(componentDao.findByNode(project, componentNode)); @@ -133,7 +133,7 @@ } @RequestMapping(method = HttpMethod.GET) - public void index(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, ServletException, IOException { + public void index(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws SQLException, ServletException, IOException { final var viewModel = new ProjectView(); populate(viewModel, null, dao); @@ -148,13 +148,13 @@ forwardView(req, resp, viewModel, "projects"); } - private void configureProjectEditor(ProjectEditView viewModel, Project project, DataAccessObjects dao) throws SQLException { + private void configureProjectEditor(ProjectEditView viewModel, Project project, DaoProvider dao) throws SQLException { viewModel.setProject(project); viewModel.setUsers(dao.getUserDao().list()); } @RequestMapping(requestPath = "$project/edit", method = HttpMethod.GET) - public void edit(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws IOException, SQLException, ServletException { + public void edit(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DaoProvider dao) throws IOException, SQLException, ServletException { final var viewModel = new ProjectEditView(); populate(viewModel, pathParams, dao); @@ -168,7 +168,7 @@ } @RequestMapping(requestPath = "create", method = HttpMethod.GET) - public void create(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, ServletException, IOException { + public void create(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws SQLException, ServletException, IOException { final var viewModel = new ProjectEditView(); populate(viewModel, null, dao); configureProjectEditor(viewModel, new Project(-1), dao); @@ -176,7 +176,7 @@ } @RequestMapping(requestPath = "commit", method = HttpMethod.POST) - public void commit(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, ServletException { + public void commit(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws IOException, ServletException { try { final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow()); @@ -191,21 +191,27 @@ ownerId -> ownerId >= 0 ? new User(ownerId) : null ).ifPresent(project::setOwner); - dao.getProjectDao().saveOrUpdate(project); + final var projectDao = dao.getProjectDao(); + if (project.getId() > 0) { + // TODO: unused return value + projectDao.update(project); + } else { + projectDao.save(project); + } setRedirectLocation(req, "./projects/"); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); LOG.debug("Successfully updated project {}", project.getName()); renderSite(req, resp); - } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { + } catch (NoSuchElementException | IllegalArgumentException ex) { resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); // TODO: implement - fix issue #21 } } @RequestMapping(requestPath = "$project/$component/$version/issues/", method = HttpMethod.GET) - public void issues(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws SQLException, IOException, ServletException { + public void issues(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DaoProvider dao) throws SQLException, IOException, ServletException { final var viewModel = new ProjectDetailsView(); populate(viewModel, pathParams, dao); @@ -263,7 +269,7 @@ } @RequestMapping(requestPath = "$project/versions/", method = HttpMethod.GET) - public void versions(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException { + public void versions(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException { final var viewModel = new VersionsView(); populate(viewModel, pathParameters, dao); @@ -282,7 +288,7 @@ } @RequestMapping(requestPath = "$project/versions/$version/edit", method = HttpMethod.GET) - public void editVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException { + public void editVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException { final var viewModel = new VersionEditView(); populate(viewModel, pathParameters, dao); @@ -297,7 +303,7 @@ } @RequestMapping(requestPath = "$project/create-version", method = HttpMethod.GET) - public void createVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException { + public void createVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException { final var viewModel = new VersionEditView(); populate(viewModel, pathParameters, dao); @@ -312,7 +318,7 @@ } @RequestMapping(requestPath = "commit-version", method = HttpMethod.POST) - public void commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, ServletException { + public void commitVersion(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws IOException, ServletException { try { final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow()); @@ -329,20 +335,27 @@ getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal); version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow())); - dao.getVersionDao().saveOrUpdate(version, project); + + final var versionDao = dao.getVersionDao(); + if (version.getId() > 0) { + // TODO: use return value + versionDao.update(version); + } else { + versionDao.save(version, project); + } setRedirectLocation(req, "./projects/" + project.getNode() + "/versions/"); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); renderSite(req, resp); - } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { + } catch (NoSuchElementException | IllegalArgumentException ex) { resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); // TODO: implement - fix issue #21 } } @RequestMapping(requestPath = "$project/components/", method = HttpMethod.GET) - public void components(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException { + public void components(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException { final var viewModel = new ComponentsView(); populate(viewModel, pathParameters, dao); @@ -360,7 +373,7 @@ } @RequestMapping(requestPath = "$project/components/$component/edit", method = HttpMethod.GET) - public void editComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException { + public void editComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException { final var viewModel = new ComponentEditView(); populate(viewModel, pathParameters, dao); @@ -376,7 +389,7 @@ } @RequestMapping(requestPath = "$project/create-component", method = HttpMethod.GET) - public void createComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException { + public void createComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException { final var viewModel = new ComponentEditView(); populate(viewModel, pathParameters, dao); @@ -392,7 +405,7 @@ } @RequestMapping(requestPath = "commit-component", method = HttpMethod.POST) - public void commitComponent(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, ServletException { + public void commitComponent(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws IOException, ServletException { try { final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow()); @@ -414,19 +427,25 @@ ).ifPresent(component::setLead); getParameter(req, String.class, "description").ifPresent(component::setDescription); - dao.getComponentDao().saveOrUpdate(component, project); + final var componentDao = dao.getComponentDao(); + if (component.getId() > 0) { + // TODO: use return value + componentDao.update(component); + } else { + componentDao.save(component, project); + } setRedirectLocation(req, "./projects/" + project.getNode() + "/components/"); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); renderSite(req, resp); - } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { + } catch (NoSuchElementException | IllegalArgumentException ex) { resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); // TODO: implement - fix issue #21 } } - private void configureIssueEditor(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException { + private void configureIssueEditor(IssueEditView viewModel, Issue issue, DaoProvider dao) throws SQLException { final var project = viewModel.getProjectInfo().getProject(); issue.setProject(project); // automatically set current project for new issues viewModel.setIssue(issue); @@ -436,7 +455,7 @@ } @RequestMapping(requestPath = "$project/issues/$issue/view", method = HttpMethod.GET) - public void viewIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException { + public void viewIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException { final var viewModel = new IssueDetailView(); populate(viewModel, pathParameters, dao); @@ -462,7 +481,7 @@ // TODO: why should the issue editor be child of $project? @RequestMapping(requestPath = "$project/issues/$issue/edit", method = HttpMethod.GET) - public void editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException { + public void editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException { final var viewModel = new IssueEditView(); populate(viewModel, pathParameters, dao); @@ -486,7 +505,7 @@ } @RequestMapping(requestPath = "$project/create-issue", method = HttpMethod.GET) - public void createIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException { + public void createIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DaoProvider dao) throws IOException, SQLException, ServletException { final var viewModel = new IssueEditView(); populate(viewModel, pathParameters, dao); @@ -504,7 +523,7 @@ } @RequestMapping(requestPath = "commit-issue", method = HttpMethod.POST) - public void commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, ServletException { + public void commitIssue(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws IOException, ServletException { try { final var issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow()); final var componentId = getParameter(req, Integer.class, "component"); @@ -549,21 +568,27 @@ stream.map(Version::new).collect(Collectors.toList()) ).ifPresent(issue::setResolvedVersions); - dao.getIssueDao().saveOrUpdate(issue, issue.getProject()); + final var issueDao = dao.getIssueDao(); + if (issue.getId() > 0) { + // TODO: use return value + issueDao.update(issue); + } else { + issueDao.save(issue, project); + } // TODO: fix redirect location setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view"); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); renderSite(req, resp); - } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { + } catch (NoSuchElementException | IllegalArgumentException ex) { resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); // TODO: implement - fix issue #21 } } @RequestMapping(requestPath = "commit-issue-comment", method = HttpMethod.POST) - public void commentIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException, ServletException { + public void commentIssue(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws IOException, ServletException { final var issueIdParam = getParameter(req, Integer.class, "issueid"); if (issueIdParam.isEmpty()) { resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Detected manipulated form."); @@ -584,7 +609,7 @@ LOG.debug("User {} is commenting on issue #{}", req.getRemoteUser(), issue.getId()); if (req.getRemoteUser() != null) { - dao.getUserDao().findByUsername(req.getRemoteUser()).ifPresent(issueComment::setAuthor); + Optional.ofNullable(dao.getUserDao().findByUsername(req.getRemoteUser())).ifPresent(issueComment::setAuthor); } dao.getIssueDao().saveComment(issue, issueComment); @@ -594,7 +619,7 @@ setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); renderSite(req, resp); - } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { + } catch (NoSuchElementException | IllegalArgumentException ex) { resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); // TODO: implement - fix issue #21 } diff -r 4f912cd42876 -r 86b5d8a1662f src/main/java/de/uapcore/lightpit/modules/UsersModule.java --- a/src/main/java/de/uapcore/lightpit/modules/UsersModule.java Fri Nov 06 10:50:32 2020 +0100 +++ b/src/main/java/de/uapcore/lightpit/modules/UsersModule.java Thu Nov 19 13:58:54 2020 +0100 @@ -32,7 +32,7 @@ import de.uapcore.lightpit.Constants; import de.uapcore.lightpit.HttpMethod; import de.uapcore.lightpit.RequestMapping; -import de.uapcore.lightpit.dao.DataAccessObjects; +import de.uapcore.lightpit.dao.DaoProvider; import de.uapcore.lightpit.entities.User; import de.uapcore.lightpit.viewmodel.UsersEditView; import de.uapcore.lightpit.viewmodel.UsersView; @@ -61,7 +61,7 @@ } @RequestMapping(method = HttpMethod.GET) - public void index(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, ServletException, IOException { + public void index(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws SQLException, ServletException, IOException { final var userDao = dao.getUserDao(); final var viewModel = new UsersView(); @@ -73,7 +73,7 @@ } @RequestMapping(requestPath = "edit", method = HttpMethod.GET) - public void edit(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, ServletException, IOException { + public void edit(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws SQLException, ServletException, IOException { final var viewModel = new UsersEditView(); viewModel.setUser(findByParameter(req, Integer.class, "id", @@ -86,7 +86,7 @@ } @RequestMapping(requestPath = "commit", method = HttpMethod.POST) - public void commit(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws ServletException, IOException { + public void commit(HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws ServletException, IOException { User user = new User(-1); try { @@ -96,13 +96,19 @@ getParameter(req, String.class, "lastname").ifPresent(user::setLastname); getParameter(req, String.class, "mail").ifPresent(user::setMail); - dao.getUserDao().saveOrUpdate(user); + final var userDao = dao.getUserDao(); + if (user.getId() > 0) { + // TODO: unused return value + userDao.update(user); + } else { + userDao.save(user); + } setRedirectLocation(req, "./teams/"); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); LOG.debug("Successfully updated user {}", user.getUsername()); - } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { + } catch (NoSuchElementException | IllegalArgumentException ex) { final var viewModel = new UsersEditView(); viewModel.setUser(user); // TODO: viewModel.setErrorText() diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/AbstractChildEntityDao.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractChildEntityDao.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,64 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao + + +abstract class AbstractChildEntityDao : AbstractDao() { + + /** + * Lists all entities being a child of the specified parent. + * @param parent the parent + * @return the list of child instances + */ + abstract fun list(parent: P): List + + /** + * Finds an entity by its integer ID. + * It is not guaranteed that referenced entities are automatically joined. + * + * @param id the id + * @return the entity or null if there is no such entity + */ + abstract fun find(id: Int): T? + + /** + * Inserts an instance into database. + * It is not guaranteed that generated fields will be updated in the instance. + * + * @param instance the instance to insert + * @param parent a reference to the parent + */ + abstract fun save(instance: T, parent: P) + + /** + * Updates an instance in the database. + * + * @param instance the instance to update + * @return true if an instance has been updated, false if the instance is not present in database + */ + abstract fun update(instance: T): Boolean +} diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/AbstractComponentDao.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractComponentDao.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,34 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao + +import de.uapcore.lightpit.entities.Component +import de.uapcore.lightpit.entities.Project + +abstract class AbstractComponentDao : AbstractChildEntityDao() { + abstract fun findByNode(parent: Project, node: String): Component? +} \ No newline at end of file diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/AbstractDao.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractDao.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,65 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao + +import java.sql.PreparedStatement +import java.sql.ResultSet +import java.sql.Types + +abstract class AbstractDao { + + abstract fun mapResult(rs: ResultSet): T + + protected fun list(stmt: PreparedStatement): List { + return sequence { + stmt.executeQuery().use { result -> + while (result.next()) yield(mapResult(result)) + } + }.toList() + } + + protected fun find(stmt: PreparedStatement): T? { + stmt.executeQuery().use { result -> + return if (result.next()) { + mapResult(result) + } else { + null + } + } + } + + // TODO: create PreparedStatement abstraction that provides some features + + // TODO: remove the following legacy code helper function + protected fun setForeignKeyOrNull(stmt: PreparedStatement, index: Int, instance: T?, keyGetter: (obj: T) -> Int) { + if (instance == null) { + stmt.setNull(index, Types.INTEGER) + } else { + stmt.setInt(index, keyGetter(instance)) + } + } +} \ No newline at end of file diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/AbstractEntityDao.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractEntityDao.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,61 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao + +abstract class AbstractEntityDao : AbstractDao() { + + /** + * Lists all entities. + * @return a list of all entities + */ + abstract fun list(): List + + /** + * Finds an entity by its integer ID. + * It is not guaranteed that referenced entities are automatically joined. + * + * @param id the id + * @return the entity or null if there is no such entity + */ + abstract fun find(id: Int): T? + + /** + * Inserts an instance into database. + * It is not guaranteed that generated fields will be updated in the instance. + * + * @param instance the instance to insert + */ + abstract fun save(instance: T) + + /** + * Updates an instance in the database. + * + * @param instance the instance to update + * @return true if an instance has been updated, false if the instance is not present in database + */ + abstract fun update(instance: T): Boolean +} \ No newline at end of file diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/AbstractIssueDao.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractIssueDao.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,99 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao + +import de.uapcore.lightpit.entities.* +import java.sql.SQLException + +abstract class AbstractIssueDao : AbstractChildEntityDao() { + + /** + * Lists all issues that are related to the specified component and version. + * If component or version is null, search for issues that are not assigned to any + * component or version, respectively. + * + * @param project the project + * @param component the component + * @param version the version + * @return a list of issues + */ + abstract fun list(project: Project, component: Component?, version: Version?): List + + /** + * Lists all issues that are related to the specified version. + * If the version is null, lists issues that are not assigned to any version. + * + * @param project the project + * @param version the version or null + * @return a list of issues + */ + abstract fun list(project: Project, version: Version?): List + + /** + * Lists all issues that are related to the specified component. + * If the component is null, lists issues that are not assigned to a component. + * + * @param project the project + * @param component the component or null + * @return a list of issues + */ + abstract fun list(project: Project, component: Component?): List + + /** + * Lists all comments for a specific issue in chronological order. + * + * @param issue the issue + * @return the list of comments + */ + abstract fun listComments(issue: Issue): List + + /** + * Stores the specified comment in database. + * This is an update-or-insert operation. + * + * @param issue the issue to save the comment for + * @param comment the comment to save + */ + abstract fun saveComment(issue: Issue, comment: IssueComment) + + /** + * Saves an instances to the database. + * Implementations of this DAO must guarantee that the generated ID is stored in the instance. + * + * @param instance the instance to insert + * @param parent the parent project + * @throws SQLException on any kind of SQL error + */ + abstract override fun save(instance: Issue, parent: Project) + + /** + * Retrieves the affected, scheduled and resolved versions for the specified issue. + * + * @param issue the issue to join the information for + */ + abstract fun joinVersionInformation(issue: Issue) +} \ No newline at end of file diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/AbstractProjectDao.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractProjectDao.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,37 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao + +import de.uapcore.lightpit.entities.IssueSummary +import de.uapcore.lightpit.entities.Project + +abstract class AbstractProjectDao : AbstractEntityDao() { + + abstract fun getIssueSummary(project: Project): IssueSummary + + abstract fun findByNode(node: String): Project? +} \ No newline at end of file diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/AbstractUserDao.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractUserDao.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,33 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao + +import de.uapcore.lightpit.entities.User + +abstract class AbstractUserDao : AbstractEntityDao() { + abstract fun findByUsername(username: String): User? +} \ No newline at end of file diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/AbstractVersionDao.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/AbstractVersionDao.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,34 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao + +import de.uapcore.lightpit.entities.Project +import de.uapcore.lightpit.entities.Version + +abstract class AbstractVersionDao : AbstractChildEntityDao() { + abstract fun findByNode(parent: Project, node: String): Version? +} \ No newline at end of file diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/DaoProvider.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/DaoProvider.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,35 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao + +interface DaoProvider { + val userDao: AbstractUserDao + val projectDao: AbstractProjectDao + val componentDao: AbstractComponentDao + val versionDao: AbstractVersionDao + val issueDao: AbstractIssueDao +} \ No newline at end of file diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGComponentDao.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGComponentDao.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,110 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao.postgres + +import de.uapcore.lightpit.dao.AbstractComponentDao +import de.uapcore.lightpit.dao.Functions +import de.uapcore.lightpit.entities.Component +import de.uapcore.lightpit.entities.Project +import de.uapcore.lightpit.entities.User +import de.uapcore.lightpit.types.WebColor +import java.sql.Connection +import java.sql.PreparedStatement +import java.sql.ResultSet + +class PGComponentDao(connection: Connection) : AbstractComponentDao() { + + private val query = "select id, name, node, color, ordinal, description, " + + "userid, username, givenname, lastname, mail " + + "from lpit_component " + + "left join lpit_user on lead = userid" + + private val listStmt = connection.prepareStatement("$query where project = ? order by ordinal, lower(name)") + private val findStmt = connection.prepareStatement("$query where id = ? ") + private val findByNodeStmt = connection.prepareStatement("$query where project = ? and node = ?") + private val insertStmt = connection.prepareStatement( + "insert into lpit_component (name, node, color, ordinal, description, lead, project) values (?, ?, ?, ?, ?, ?, ?)" + ) + private val updateStmt = connection.prepareStatement( + "update lpit_component set name = ?, node = ?, color = ?, ordinal = ?, description = ?, lead = ? where id = ?" + ) + + override fun mapResult(rs: ResultSet): Component { + val component = Component(rs.getInt("id")) + component.name = rs.getString("name") + component.node = rs.getString("node") + component.color = try { + WebColor(rs.getString("color")) + } catch (ex: IllegalArgumentException) { + WebColor("000000") + } + component.ordinal = rs.getInt("ordinal") + component.description = rs.getString("description") + component.lead = PGUserDao.mapResult(rs).takeUnless { rs.wasNull() } + return component + } + + private fun setColumns(stmt: PreparedStatement, instance: Component): Int { + var column = 0 + stmt.setString(++column, instance.name) + stmt.setString(++column, instance.node) + stmt.setString(++column, instance.color.hex) + stmt.setInt(++column, instance.ordinal) + Functions.setStringOrNull(stmt, ++column, instance.description) + setForeignKeyOrNull(stmt, ++column, instance.lead, User::id) + return column + } + + override fun save(instance: Component, parent: Project) { + var column = setColumns(insertStmt, instance) + insertStmt.setInt(++column, parent.id) + insertStmt.executeUpdate() + } + + override fun update(instance: Component): Boolean { + var column = setColumns(updateStmt, instance) + updateStmt.setInt(++column, instance.id) + return updateStmt.executeUpdate() > 0 + } + + + override fun list(parent: Project): List { + listStmt.setInt(1, parent.id) + return super.list(listStmt) + } + + override fun find(id: Int): Component? { + findStmt.setInt(1, id) + return super.find(findStmt) + } + + override fun findByNode(parent: Project, node: String): Component? { + findByNodeStmt.setInt(1, parent.id) + findByNodeStmt.setString(2, node) + return super.find(findByNodeStmt) + } +} \ No newline at end of file diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGDaoProvider.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGDaoProvider.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,38 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao.postgres + +import de.uapcore.lightpit.dao.DaoProvider +import java.sql.Connection + +class PGDaoProvider(connection: Connection) : DaoProvider { + override val userDao = PGUserDao(connection) + override val projectDao = PGProjectDao(connection) + override val componentDao = PGComponentDao(connection) + override val versionDao = PGVersionDao(connection) + override val issueDao = PGIssueDao(connection) +} \ No newline at end of file diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGIssueDao.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGIssueDao.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,249 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao.postgres + +import de.uapcore.lightpit.dao.AbstractIssueDao +import de.uapcore.lightpit.dao.Functions +import de.uapcore.lightpit.entities.* +import java.sql.Connection +import java.sql.PreparedStatement +import java.sql.ResultSet +import java.sql.Types + +class PGIssueDao(connection: Connection) : AbstractIssueDao() { + + private val query = "select issueid, i.project, p.name as projectname, p.node as projectnode, " + + "component, c.name as componentname, c.node as componentnode, " + + "status, category, subject, i.description, " + + "userid, username, givenname, lastname, mail, " + + "created, updated, eta " + + "from lpit_issue i " + + "join lpit_project p on i.project = projectid " + + "left join lpit_component c on component = c.id " + + "left join lpit_user on userid = assignee " + private val list = connection.prepareStatement(query + + "where i.project = ? and coalesce(component, -1) = coalesce(?, component, -1)") + private val listForVersion = connection.prepareStatement( + "with issue_version as ( " + + "select issueid, versionid from lpit_issue_affected_version union " + + "select issueid, versionid from lpit_issue_resolved_version) " + + query + + "left join issue_version using (issueid) " + + "where i.project = ? " + + "and coalesce(versionid,-1) = ? and coalesce(component, -1) = coalesce(?, component, -1)" + ) + private val find = connection.prepareStatement(query + "where issueid = ? ") + private val insert = connection.prepareStatement( + "insert into lpit_issue (project, component, status, category, subject, description, assignee, eta) " + + "values (?, ?, ?::issue_status, ?::issue_category, ?, ?, ?, ?) returning issueid" + ) + private val update = connection.prepareStatement( + "update lpit_issue set " + + "updated = now(), component = ?, status = ?::issue_status, category = ?::issue_category, " + + "subject = ?, description = ?, assignee = ?, eta = ? where issueid = ?" + ) + private val affectedVersions = connection.prepareStatement( + "select versionid, name, status, ordinal, node " + + "from lpit_version join lpit_issue_affected_version using (versionid) " + + "where issueid = ? " + + "order by ordinal, name" + ) + private val clearAffected = connection.prepareStatement("delete from lpit_issue_affected_version where issueid = ?") + private val insertAffected = connection.prepareStatement("insert into lpit_issue_affected_version (issueid, versionid) values (?,?)") + + private val resolvedVersions = connection.prepareStatement( + "select versionid, name, status, ordinal, node " + + "from lpit_version v join lpit_issue_resolved_version using (versionid) " + + "where issueid = ? " + + "order by ordinal, name" + ) + private val clearResolved = connection.prepareStatement("delete from lpit_issue_resolved_version where issueid = ?") + private val insertResolved = connection.prepareStatement("insert into lpit_issue_resolved_version (issueid, versionid) values (?,?)") + private val insertComment = connection.prepareStatement( + "insert into lpit_issue_comment (issueid, comment, userid) values (?, ? ,?)" + ) + private val updateComment = connection.prepareStatement( + "update lpit_issue_comment set comment = ?, updated = now(), updatecount = updatecount+1 where commentid = ?" + ) + private val listComments = connection.prepareStatement( + "select * from lpit_issue_comment left join lpit_user using (userid) where issueid = ? order by created" + ) + + override fun mapResult(rs: ResultSet): Issue { + val project = Project(rs.getInt("project")) + project.name = rs.getString("projectname") + project.node = rs.getString("projectnode") + val issue = Issue(rs.getInt("issueid")) + issue.project = project + issue.component = rs.getInt("component").let { id -> + if (rs.wasNull()) { + null + } else { + val component = Component(id) + component.name = rs.getString("componentname") + component.node = rs.getString("componentnode") + component + } + } + issue.status = IssueStatus.valueOf(rs.getString("status")) + issue.category = IssueCategory.valueOf(rs.getString("category")) + issue.subject = rs.getString("subject") + issue.description = rs.getString("description") + issue.assignee = PGUserDao.mapResult(rs).takeUnless { rs.wasNull() } + issue.created = rs.getTimestamp("created") + issue.updated = rs.getTimestamp("updated") + issue.eta = rs.getDate("eta") + return issue + } + + private fun updateVersionLists(instance: Issue) { + clearAffected.setInt(1, instance.id) + clearResolved.setInt(1, instance.id) + insertAffected.setInt(1, instance.id) + insertResolved.setInt(1, instance.id) + clearAffected.executeUpdate() + clearResolved.executeUpdate() + for (v: Version in instance.affectedVersions) { + insertAffected.setInt(2, v.id) + insertAffected.executeUpdate() + } + for (v: Version in instance.resolvedVersions) { + insertResolved.setInt(2, v.id) + insertResolved.executeUpdate() + } + } + + private fun setData(stmt: PreparedStatement, column: Int, instance: Issue): Int { + var col = column + setForeignKeyOrNull(stmt, ++col, instance.component, Component::id) + stmt.setString(++col, instance.status.name) + stmt.setString(++col, instance.category.name) + stmt.setString(++col, instance.subject) + Functions.setStringOrNull(stmt, ++col, instance.description) + setForeignKeyOrNull(stmt, ++col, instance.assignee, User::id) + Functions.setDateOrNull(stmt, ++col, instance.eta) + return col + } + + override fun save(instance: Issue, parent: Project) { + instance.project = parent + var column = 0 + insert.setInt(++column, parent.id) + setData(insert, column, instance) + // insert and retrieve the ID + val rs = insert.executeQuery() + rs.next() + instance.id = rs.getInt(1) + updateVersionLists(instance) + } + + override fun update(instance: Issue): Boolean { + var column = setData(update, 0, instance) + update.setInt(++column, instance.id) + return if (update.executeUpdate() > 0) { + updateVersionLists(instance) + true + } else { + false + } + } + + override fun list(parent: Project): List { + list.setInt(1, parent.id) + list.setNull(2, Types.INTEGER) + return super.list(list) + } + + override fun list(project: Project, component: Component?, version: Version?): List { + listForVersion.setInt(1, project.id) + listForVersion.setInt(2, version?.id ?: -1) + listForVersion.setInt(3, component?.id ?: -1) + return super.list(listForVersion) + } + + override fun list(project: Project, version: Version?): List { + listForVersion.setInt(1, project.id) + listForVersion.setInt(2, version?.id ?: -1) + listForVersion.setNull(3, Types.INTEGER) + return super.list(listForVersion) + } + + override fun list(project: Project, component: Component?): List { + list.setInt(1, project.id) + list.setInt(2, component?.id ?: -1) + return super.list(list) + } + + override fun find(id: Int): Issue? { + find.setInt(1, id) + return super.find(find) + } + + private fun listVersions(stmt: PreparedStatement, issue: Issue): List { + stmt.setInt(1, issue.id) + return sequence { + stmt.executeQuery().use { result -> + while (result.next()) yield(PGVersionDao.mapResult(result)) + } + }.toList() + } + + override fun joinVersionInformation(issue: Issue) { + issue.affectedVersions = listVersions(affectedVersions, issue) + issue.resolvedVersions = listVersions(resolvedVersions, issue) + } + + override fun listComments(issue: Issue): List { + listComments.setInt(1, issue.id) + return sequence { + listComments.executeQuery().use { rs -> + while (rs.next()) { + val comment = IssueComment(rs.getInt("commentid")) + comment.created = rs.getTimestamp("created") + comment.updated = rs.getTimestamp("updated") + comment.updateCount = rs.getInt("updatecount") + comment.comment = rs.getString("comment") + comment.author = PGUserDao.mapResult(rs).takeUnless { rs.wasNull() } + yield(comment) + } + } + }.toList() + } + + override fun saveComment(issue: Issue, comment: IssueComment) { + if (comment.id >= 0) { + updateComment.setString(1, comment.comment) + updateComment.setInt(2, comment.id) + updateComment.execute() + } else { + insertComment.setInt(1, issue.id) + insertComment.setString(2, comment.comment) + setForeignKeyOrNull(insertComment, 3, comment.author, User::id) + insertComment.execute() + } + } +} \ No newline at end of file diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGProjectDao.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGProjectDao.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,122 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao.postgres + +import de.uapcore.lightpit.dao.AbstractProjectDao +import de.uapcore.lightpit.dao.Functions +import de.uapcore.lightpit.entities.IssueSummary +import de.uapcore.lightpit.entities.Project +import de.uapcore.lightpit.entities.User +import java.sql.Connection +import java.sql.PreparedStatement +import java.sql.ResultSet + +class PGProjectDao(connection: Connection) : AbstractProjectDao() { + + private val query = "select projectid, name, node, description, repourl, " + + "userid, username, lastname, givenname, mail " + + "from lpit_project " + + "left join lpit_user owner on lpit_project.owner = owner.userid " + + private val listStmt = connection.prepareStatement("$query order by name") + private val findStmt = connection.prepareStatement("$query where projectid = ?") + private val findByNodeStmt = connection.prepareStatement("$query where node = ?") + private val issueSummaryStmt = connection.prepareStatement( + "select phase, count(*) as total " + + "from lpit_issue " + + "join lpit_issue_phases using(status) " + + "where project = ? " + + "group by phase " + ) + private val insertStmt = connection.prepareStatement( + "insert into lpit_project (name, node, description, repourl, owner) values (?, ?, ?, ?, ?)" + ) + private val updateStmt = connection.prepareStatement( + "update lpit_project set name = ?, node = ?, description = ?, repourl = ?, owner = ? where projectid = ?" + ) + + override fun mapResult(rs: ResultSet): Project { + val proj = Project(rs.getInt("projectid")) + proj.name = rs.getString("name") + proj.node = rs.getString("node") + proj.description = rs.getString("description") + proj.repoUrl = rs.getString("repourl") + proj.owner = PGUserDao.mapResult(rs).takeUnless { rs.wasNull() } + return proj + } + + override fun getIssueSummary(project: Project): IssueSummary { + issueSummaryStmt.setInt(1, project.id) + val result = issueSummaryStmt.executeQuery() + val summary = IssueSummary() + while (result.next()) { + val phase = result.getInt("phase") + val total = result.getInt("total") + when (phase) { + 0 -> summary.open = total + 1 -> summary.active = total + 2 -> summary.done = total + } + } + return summary + } + + private fun setColumns(stmt: PreparedStatement, instance: Project): Int { + var column = 0 + stmt.setString(++column, instance.name) + stmt.setString(++column, instance.node) + Functions.setStringOrNull(stmt, ++column, instance.description) + Functions.setStringOrNull(stmt, ++column, instance.repoUrl) + setForeignKeyOrNull(stmt, ++column, instance.owner, User::id) + return column + } + + override fun save(instance: Project) { + setColumns(insertStmt, instance) + insertStmt.executeUpdate() + } + + override fun update(instance: Project): Boolean { + var column = setColumns(updateStmt, instance) + updateStmt.setInt(++column, instance.id) + return updateStmt.executeUpdate() > 0 + } + + override fun list(): List { + return super.list(listStmt) + } + + override fun find(id: Int): Project? { + findStmt.setInt(1, id) + return super.find(findStmt) + } + + override fun findByNode(node: String): Project? { + findByNodeStmt.setString(1, node) + return super.find(findByNodeStmt) + } +} \ No newline at end of file diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGUserDao.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGUserDao.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,95 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao.postgres + +import de.uapcore.lightpit.dao.AbstractUserDao +import de.uapcore.lightpit.dao.Functions +import de.uapcore.lightpit.entities.User +import java.sql.Connection +import java.sql.ResultSet + +class PGUserDao(connection: Connection) : AbstractUserDao() { + + companion object { + fun mapResult(rs: ResultSet): User { + val id = rs.getInt("userid") + return if (rs.wasNull()) { + User(-1) + } else { + val user = User(id) + user.username = rs.getString("username") + user.givenname = Functions.getSafeString(rs, "givenname") + user.lastname = Functions.getSafeString(rs, "lastname") + user.mail = Functions.getSafeString(rs, "mail") + user + } + } + } + + private val listStmt = connection.prepareStatement( + "select userid, username, lastname, givenname, mail " + + "from lpit_user where userid >= 0 " + + "order by username") + private val findStmt = connection.prepareStatement( + "select userid, username, lastname, givenname, mail " + + "from lpit_user where userid = ? ") + private val findByUsernameStmt = connection.prepareStatement( + "select userid, username, lastname, givenname, mail " + + "from lpit_user where lower(username) = lower(?) ") + private val insertStmt = connection.prepareStatement("insert into lpit_user (username, lastname, givenname, mail) values (?, ?, ?, ?)") + private val updateStmt = connection.prepareStatement("update lpit_user set lastname = ?, givenname = ?, mail = ? where userid = ?") + + override fun mapResult(rs: ResultSet): User = Companion.mapResult(rs) + + override fun save(instance: User) { + insertStmt.setString(1, instance.username) + Functions.setStringOrNull(insertStmt, 2, instance.lastname) + Functions.setStringOrNull(insertStmt, 3, instance.givenname) + Functions.setStringOrNull(insertStmt, 4, instance.mail) + insertStmt.executeUpdate() + } + + override fun update(instance: User): Boolean { + Functions.setStringOrNull(updateStmt, 1, instance.lastname) + Functions.setStringOrNull(updateStmt, 2, instance.givenname) + Functions.setStringOrNull(updateStmt, 3, instance.mail) + updateStmt.setInt(4, instance.id) + return updateStmt.executeUpdate() > 0 + } + + override fun list(): List = super.list(listStmt) + + override fun find(id: Int): User? { + findStmt.setInt(1, id) + return super.find(findStmt) + } + + override fun findByUsername(username: String): User? { + findByUsernameStmt.setString(1, username) + return super.find(findByUsernameStmt) + } +} \ No newline at end of file diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGVersionDao.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGVersionDao.kt Thu Nov 19 13:58:54 2020 +0100 @@ -0,0 +1,105 @@ +/* + * Copyright 2020 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package de.uapcore.lightpit.dao.postgres + +import de.uapcore.lightpit.dao.AbstractVersionDao +import de.uapcore.lightpit.entities.Project +import de.uapcore.lightpit.entities.Version +import de.uapcore.lightpit.entities.VersionStatus +import java.sql.Connection +import java.sql.PreparedStatement +import java.sql.ResultSet + +class PGVersionDao(connection: Connection) : AbstractVersionDao() { + + companion object { + fun mapResult(rs: ResultSet): Version { + val id = rs.getInt("versionid") + return if (rs.wasNull()) { + Version(-1) + } else { + val version = Version(id) + version.name = rs.getString("name") + version.node = rs.getString("node") + version.ordinal = rs.getInt("ordinal") + version.status = VersionStatus.valueOf(rs.getString("status")) + version + } + } + } + + private val query = "select versionid, project, name, node, ordinal, status from lpit_version" + private val listStmt = connection.prepareStatement(query + " where project = ? " + + "order by ordinal desc, lower(name) desc") + private val findStmt = connection.prepareStatement("$query where versionid = ?") + private val findByNodeStmt = connection.prepareStatement("$query where project = ? and node = ?") + private val insertStmt = connection.prepareStatement( + "insert into lpit_version (name, node, ordinal, status, project) values (?, ?, ?, ?::version_status, ?)" + ) + private val updateStmt = connection.prepareStatement( + "update lpit_version set name = ?, node = ?, ordinal = ?, status = ?::version_status where versionid = ?" + ) + + override fun mapResult(rs: ResultSet): Version = Companion.mapResult(rs) + + private fun setFields(stmt: PreparedStatement, instance: Version): Int { + var column = 0 + stmt.setString(++column, instance.name) + stmt.setString(++column, instance.node) + stmt.setInt(++column, instance.ordinal) + stmt.setString(++column, instance.status.name) + return column + } + + override fun save(instance: Version, parent: Project) { + var column = setFields(insertStmt, instance) + insertStmt.setInt(++column, parent.id) + insertStmt.executeUpdate() + } + + override fun update(instance: Version): Boolean { + var column = setFields(updateStmt, instance) + updateStmt.setInt(++column, instance.id) + return updateStmt.executeUpdate() > 0 + } + + override fun list(parent: Project): List { + listStmt.setInt(1, parent.id) + return super.list(listStmt) + } + + override fun find(id: Int): Version? { + findStmt.setInt(1, id) + return super.find(findStmt) + } + + override fun findByNode(parent: Project, node: String): Version? { + findByNodeStmt.setInt(1, parent.id) + findByNodeStmt.setString(2, node) + return super.find(findByNodeStmt) + } +} \ No newline at end of file diff -r 4f912cd42876 -r 86b5d8a1662f src/main/kotlin/de/uapcore/lightpit/entities/User.kt --- a/src/main/kotlin/de/uapcore/lightpit/entities/User.kt Fri Nov 06 10:50:32 2020 +0100 +++ b/src/main/kotlin/de/uapcore/lightpit/entities/User.kt Thu Nov 19 13:58:54 2020 +0100 @@ -26,7 +26,7 @@ package de.uapcore.lightpit.entities data class User(val id: Int) { - var username = "anonymous" + var username = "" var mail = "" var givenname = "" var lastname = ""