# HG changeset patch # User Mike Becker # Date 1603364606 -7200 # Node ID e2aa673dd473a6ac6dd74036618d9bb1d7f079dc # Parent a7e543ab0c5f267580934018ab16bc4ac56bff87 adds custom node names - fixes #27 diff -r a7e543ab0c5f -r e2aa673dd473 setup/postgres/psql_create_tables.sql --- a/setup/postgres/psql_create_tables.sql Thu Oct 22 12:00:34 2020 +0200 +++ b/setup/postgres/psql_create_tables.sql Thu Oct 22 13:03:26 2020 +0200 @@ -12,6 +12,7 @@ create table lpit_project ( projectid serial primary key, name varchar(20) not null unique, + node varchar(20) not null unique, description varchar(200), repoUrl varchar(50), owner integer references lpit_user(userid) @@ -29,21 +30,26 @@ versionid serial primary key, project integer not null references lpit_project(projectid), name varchar(20) not null, + node varchar(20) not null, ordinal integer not null default 0, status version_status not null default 'Future' ); +create unique index lpit_version_node_unique on lpit_version(project, node); create table lpit_component ( id serial primary key, project integer not null references lpit_project(projectid), name varchar(20) not null, + node varchar(20) not null, color char(6) not null default '000000', ordinal integer not null default 0, description text, lead integer references lpit_user(userid) ); +create unique index lpit_component_node_unique on lpit_component(project, node); + create type issue_status as enum ( 'InSpecification', 'ToDo', diff -r a7e543ab0c5f -r e2aa673dd473 src/main/java/de/uapcore/lightpit/dao/ComponentDao.java --- a/src/main/java/de/uapcore/lightpit/dao/ComponentDao.java Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/ComponentDao.java Thu Oct 22 13:03:26 2020 +0200 @@ -31,6 +31,8 @@ import de.uapcore.lightpit.entities.Component; import de.uapcore.lightpit.entities.Project; -public interface ComponentDao extends ChildEntityDao { +import java.sql.SQLException; +public interface ComponentDao extends ChildEntityDao { + Component findByNode(Project parent, String node) throws SQLException; } diff -r a7e543ab0c5f -r e2aa673dd473 src/main/java/de/uapcore/lightpit/dao/Functions.java --- a/src/main/java/de/uapcore/lightpit/dao/Functions.java Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/Functions.java Thu Oct 22 13:03:26 2020 +0200 @@ -28,10 +28,9 @@ */ package de.uapcore.lightpit.dao; -import java.sql.Date; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.Types; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import java.util.function.Function; @@ -65,6 +64,33 @@ } } + @FunctionalInterface + public interface ResultSetMapper { + T apply(ResultSet rs) throws SQLException; + } + + public static List list(PreparedStatement stmt, ResultSetMapper mapper) throws SQLException { + List results = new ArrayList<>(); + try (var result = stmt.executeQuery()) { + while (result.next()) { + final var project = mapper.apply(result); + results.add(project); + } + } + return results; + } + + public static T find(PreparedStatement stmt, ResultSetMapper mapper) throws SQLException { + try (var result = stmt.executeQuery()) { + if (result.next()) { + final var ent = mapper.apply(result); + return ent; + } else { + return null; + } + } + } + private Functions() { } diff -r a7e543ab0c5f -r e2aa673dd473 src/main/java/de/uapcore/lightpit/dao/ProjectDao.java --- a/src/main/java/de/uapcore/lightpit/dao/ProjectDao.java Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/ProjectDao.java Thu Oct 22 13:03:26 2020 +0200 @@ -35,4 +35,6 @@ public interface ProjectDao extends RootEntityDao { IssueSummary getIssueSummary(Project project) throws SQLException; + + Project findByNode(String node) throws SQLException; } diff -r a7e543ab0c5f -r e2aa673dd473 src/main/java/de/uapcore/lightpit/dao/VersionDao.java --- a/src/main/java/de/uapcore/lightpit/dao/VersionDao.java Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/VersionDao.java Thu Oct 22 13:03:26 2020 +0200 @@ -31,5 +31,8 @@ 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 a7e543ab0c5f -r e2aa673dd473 src/main/java/de/uapcore/lightpit/dao/postgres/PGComponentDao.java --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGComponentDao.java Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGComponentDao.java Thu Oct 22 13:03:26 2020 +0200 @@ -39,42 +39,38 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; import java.util.List; -import java.util.Objects; public final class PGComponentDao implements ComponentDao { - private final PreparedStatement insert, update, list, find; + private final PreparedStatement insert, update, list, find, findByNode; public PGComponentDao(Connection connection) throws SQLException { - list = connection.prepareStatement( - "select id, name, color, ordinal, description, " + - "userid, username, givenname, lastname, mail " + - "from lpit_component " + - "left join lpit_user on lead = userid " + - "where project = ? " + + 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 desc, lower(name) desc"); - find = connection.prepareStatement( - "select id, name, color, ordinal, description, " + - "userid, username, givenname, lastname, mail " + - "from lpit_component " + - "left join lpit_user on lead = userid " + - "where id = ? "); + find = connection.prepareStatement(query + " where id = ? "); + + findByNode = connection.prepareStatement(query + " where project = ? and node = ?"); insert = connection.prepareStatement( - "insert into lpit_component (project, name, color, ordinal, description, lead) values (?, ?, ?, ?, ?, ?)" + "insert into lpit_component (name, node, color, ordinal, description, lead, project) values (?, ?, ?, ?, ?, ?, ?)" ); update = connection.prepareStatement( - "update lpit_component set name = ?, color = ?, ordinal = ?, description = ?, lead = ? where id = ?" + "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) { @@ -87,53 +83,49 @@ 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 { - Objects.requireNonNull(instance.getName()); - insert.setInt(1, project.getId()); - insert.setString(2, instance.getName()); - insert.setString(3, instance.getColor().getHex()); - insert.setInt(4, instance.getOrdinal()); - Functions.setStringOrNull(insert, 5, instance.getDescription()); - Functions.setForeignKeyOrNull(insert, 6, instance.getLead(), User::getId); + 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; - Objects.requireNonNull(instance.getName()); - Objects.requireNonNull(instance.getColor()); - update.setString(1, instance.getName()); - update.setString(2, instance.getColor().getHex()); - update.setInt(3, instance.getOrdinal()); - Functions.setStringOrNull(update, 4, instance.getDescription()); - Functions.setForeignKeyOrNull(update, 5, instance.getLead(), User::getId); - update.setInt(6, instance.getId()); + 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()); - List components = new ArrayList<>(); - try (var result = list.executeQuery()) { - while (result.next()) { - components.add(mapColumns(result)); - } - } - return components; + return Functions.list(list, PGComponentDao::mapColumns); } @Override public Component find(int id) throws SQLException { find.setInt(1, id); - try (var result = find.executeQuery()) { - if (result.next()) { - return mapColumns(result); - } else { - return null; - } - } + 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 a7e543ab0c5f -r e2aa673dd473 src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java Thu Oct 22 13:03:26 2020 +0200 @@ -48,43 +48,29 @@ private final PreparedStatement insertComment, updateComment, listComments; public PGIssueDao(Connection connection) throws SQLException { - list = connection.prepareStatement( - "select issueid, i.project, p.name as projectname, component, c.name as componentname, " + + 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 " + + "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) "+ - "select issueid, i.project, p.name as projectname, component, c.name as componentname, " + - "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 " + + query + "left join issue_version using (issueid) "+ - "left join lpit_user on userid = assignee " + "where coalesce(versionid,-1) = ? and coalesce(component, -1) = coalesce(?, component, -1)" ); - find = connection.prepareStatement( - "select issueid, i.project, p.name as projectname, component, c.name as componentname, " + - "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 " + - "where issueid = ? "); + find = connection.prepareStatement(query + "where issueid = ? "); insert = connection.prepareStatement( "insert into lpit_issue (project, component, status, category, subject, description, assignee, eta) " + @@ -128,11 +114,13 @@ 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); diff -r a7e543ab0c5f -r e2aa673dd473 src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java Thu Oct 22 13:03:26 2020 +0200 @@ -28,6 +28,7 @@ */ 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; @@ -37,32 +38,26 @@ 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 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; + private final PreparedStatement insert, update, list, find, findByNode; private final PreparedStatement issue_summary; public PGProjectDao(Connection connection) throws SQLException { - list = connection.prepareStatement( - "select projectid, name, description, repourl, " + - "userid, username, lastname, givenname, mail " + - "from lpit_project " + - "left join lpit_user owner on lpit_project.owner = owner.userid " + - "order by name"); + 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 "; - find = connection.prepareStatement( - "select projectid, name, description, repourl, " + - "userid, username, lastname, givenname, mail " + - "from lpit_project " + - "left join lpit_user owner on lpit_project.owner = owner.userid " + - "where projectid = ?"); + 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 "+ @@ -73,16 +68,17 @@ ); insert = connection.prepareStatement( - "insert into lpit_project (name, description, repourl, owner) values (?, ?, ?, ?)" + "insert into lpit_project (name, node, description, repourl, owner) values (?, ?, ?, ?, ?)" ); update = connection.prepareStatement( - "update lpit_project set name = ?, description = ?, repourl = ?, owner = ? where projectid = ?" + "update lpit_project set name = ?, node = ?, description = ?, repourl = ?, owner = ? where projectid = ?" ); } - public Project mapColumns(ResultSet result) throws SQLException { + 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)); @@ -112,50 +108,44 @@ 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 { - Objects.requireNonNull(instance.getName()); - insert.setString(1, instance.getName()); - setStringOrNull(insert, 2, instance.getDescription()); - setStringOrNull(insert, 3, instance.getRepoUrl()); - setForeignKeyOrNull(insert, 4, instance.getOwner(), User::getId); + setColumns(insert, instance); insert.executeUpdate(); } @Override public boolean update(Project instance) throws SQLException { if (instance.getId() < 0) return false; - Objects.requireNonNull(instance.getName()); - update.setString(1, instance.getName()); - setStringOrNull(update, 2, instance.getDescription()); - setStringOrNull(update, 3, instance.getRepoUrl()); - setForeignKeyOrNull(update, 4, instance.getOwner(), User::getId); - update.setInt(5, instance.getId()); + int column = setColumns(update, instance); + update.setInt(++column, instance.getId()); return update.executeUpdate() > 0; } @Override public List list() throws SQLException { - List projects = new ArrayList<>(); - try (var result = list.executeQuery()) { - while (result.next()) { - final var project = mapColumns(result); - projects.add(project); - } - } - return projects; + return Functions.list(list, PGProjectDao::mapColumns); } @Override public Project find(int id) throws SQLException { find.setInt(1, id); - try (var result = find.executeQuery()) { - if (result.next()) { - final var project = mapColumns(result); - return project; - } else { - return null; - } - } + 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 a7e543ab0c5f -r e2aa673dd473 src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java Thu Oct 22 13:03:26 2020 +0200 @@ -28,6 +28,7 @@ */ 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; @@ -37,84 +38,77 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; import java.util.List; -import java.util.Objects; public final class PGVersionDao implements VersionDao { - private final PreparedStatement insert, update, list, find; + private final PreparedStatement insert, update, list, find, findByNode; public PGVersionDao(Connection connection) throws SQLException { - list = connection.prepareStatement( - "select versionid, project, name, ordinal, status " + - "from lpit_version " + - "where project = ? " + + 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( - "select versionid, project, name, ordinal, status " + - "from lpit_version " + - "where versionid = ?"); + find = connection.prepareStatement(query + " where versionid = ?"); + findByNode = connection.prepareStatement(query + " where project = ? and node = ?"); insert = connection.prepareStatement( - "insert into lpit_version (project, name, ordinal, status) values (?, ?, ?, ?::version_status)" + "insert into lpit_version (name, node, ordinal, status, project) values (?, ?, ?, ?::version_status, ?)" ); update = connection.prepareStatement( - "update lpit_version set name = ?, ordinal = ?, status = ?::version_status where versionid = ?" + "update lpit_version set name = ?, node = ?, ordinal = ?, status = ?::version_status where versionid = ?" ); } - private Version mapColumns(ResultSet result) throws SQLException { + 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 { - Objects.requireNonNull(instance.getName()); - insert.setInt(1, project.getId()); - insert.setString(2, instance.getName()); - insert.setInt(3, instance.getOrdinal()); - insert.setString(4, instance.getStatus().name()); + 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; - Objects.requireNonNull(instance.getName()); - update.setString(1, instance.getName()); - update.setInt(2, instance.getOrdinal()); - update.setString(3, instance.getStatus().name()); - update.setInt(4, instance.getId()); + 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()); - List versions = new ArrayList<>(); - try (var result = list.executeQuery()) { - while (result.next()) { - versions.add(mapColumns(result)); - } - } - return versions; + return Functions.list(list, PGVersionDao::mapColumns); } @Override public Version find(int id) throws SQLException { find.setInt(1, id); - try (var result = find.executeQuery()) { - if (result.next()) { - return mapColumns(result); - } else { - return null; - } - } + 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 a7e543ab0c5f -r e2aa673dd473 src/main/java/de/uapcore/lightpit/entities/Component.java --- a/src/main/java/de/uapcore/lightpit/entities/Component.java Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/entities/Component.java Thu Oct 22 13:03:26 2020 +0200 @@ -69,7 +69,7 @@ } public String getNode() { - return node == null ? String.valueOf(id) : node; + return node; } public void setNode(String node) { diff -r a7e543ab0c5f -r e2aa673dd473 src/main/java/de/uapcore/lightpit/entities/Project.java --- a/src/main/java/de/uapcore/lightpit/entities/Project.java Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/entities/Project.java Thu Oct 22 13:03:26 2020 +0200 @@ -56,7 +56,7 @@ } public String getNode() { - return node == null ? String.valueOf(id) : node; + return node; } public void setNode(String node) { diff -r a7e543ab0c5f -r e2aa673dd473 src/main/java/de/uapcore/lightpit/entities/Version.java --- a/src/main/java/de/uapcore/lightpit/entities/Version.java Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/entities/Version.java Thu Oct 22 13:03:26 2020 +0200 @@ -58,7 +58,7 @@ } public String getNode() { - return node == null ? String.valueOf(id) : node; + return node; } public void setNode(String node) { diff -r a7e543ab0c5f -r e2aa673dd473 src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Thu Oct 22 13:03:26 2020 +0200 @@ -74,42 +74,44 @@ return; // Select Project - final int pid = Functions.parseIntOrZero(pathParameters.get("project")); - if (pid > 0) { - final var project = projectDao.find(pid); - if (project != null) { - final var info = new ProjectInfo(project); - info.setVersions(versionDao.list(project)); - info.setComponents(componentDao.list(project)); - info.setIssueSummary(projectDao.getIssueSummary(project)); - viewModel.setProjectInfo(info); - } - } + final var project = projectDao.findByNode(pathParameters.get("project")); + if (project == null) + return; + + final var info = new ProjectInfo(project); + info.setVersions(versionDao.list(project)); + info.setComponents(componentDao.list(project)); + info.setIssueSummary(projectDao.getIssueSummary(project)); + viewModel.setProjectInfo(info); // Select Version - final var pathParamVersion = pathParameters.get("version"); - if ("no-version".equals(pathParamVersion)) { + final var versionNode = pathParameters.get("version"); + if ("no-version".equals(versionNode)) { viewModel.setVersionFilter(ProjectView.NO_VERSION); - } else if ("all-versions".equals(pathParamVersion)) { + } else if ("all-versions".equals(versionNode)) { viewModel.setVersionFilter(ProjectView.ALL_VERSIONS); } else { - final int vid = Functions.parseIntOrZero(pathParamVersion); - if (vid > 0) { - viewModel.setVersionFilter(versionDao.find(vid)); - } + viewModel.setVersionFilter(versionDao.findByNode(project, versionNode)); } // Select Component - final var pathParamComponent = pathParameters.get("component"); - if ("no-component".equals(pathParamComponent)) { + final var componentNode = pathParameters.get("component"); + if ("no-component".equals(componentNode)) { viewModel.setComponentFilter(ProjectView.NO_COMPONENT); - } else if ("all-components".equals(pathParamComponent)) { + } else if ("all-components".equals(componentNode)) { viewModel.setComponentFilter(ProjectView.ALL_COMPONENTS); } else { - final int cid = Functions.parseIntOrZero(pathParamComponent); - if (cid > 0) { - viewModel.setComponentFilter(componentDao.find(cid)); - } + viewModel.setComponentFilter(componentDao.findByNode(project, componentNode)); + } + } + + private static String sanitizeNode(String node, String defaultValue) { + String result = node == null || node.isBlank() ? defaultValue : node; + result = result.replace('/', '-'); + if (result.equals(".") || result.equals("..")) { + return "_"+result; + } else { + return result; } } @@ -170,6 +172,10 @@ try { final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow()); project.setName(getParameter(req, String.class, "name").orElseThrow()); + + final var node = getParameter(req, String.class, "node").orElse(null); + project.setNode(sanitizeNode(node, project.getName())); + getParameter(req, String.class, "description").ifPresent(project::setDescription); getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl); getParameter(req, Integer.class, "owner").map( @@ -301,14 +307,23 @@ public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException { try { - final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow()); + final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow()); + if (project == null) { + // TODO: improve error handling, because not found is not correct for this POST request + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + return ResponseType.NONE; + } final var version = new Version(getParameter(req, Integer.class, "id").orElseThrow()); version.setName(getParameter(req, String.class, "name").orElseThrow()); + + final var node = getParameter(req, String.class, "node").orElse(null); + version.setNode(sanitizeNode(node, version.getName())); + getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal); version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow())); dao.getVersionDao().saveOrUpdate(version, project); - setRedirectLocation(req, "./projects/" + project.getId() + "/versions/"); + setRedirectLocation(req, "./projects/" + project.getNode() + "/versions/"); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); @@ -373,9 +388,18 @@ public ResponseType commitComponent(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException { try { - final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow()); + final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow()); + if (project == null) { + // TODO: improve error handling, because not found is not correct for this POST request + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + return ResponseType.NONE; + } final var component = new Component(getParameter(req, Integer.class, "id").orElseThrow()); component.setName(getParameter(req, String.class, "name").orElseThrow()); + + final var node = getParameter(req, String.class, "node").orElse(null); + component.setNode(sanitizeNode(node, component.getName())); + component.setColor(getParameter(req, WebColor.class, "color").orElseThrow()); getParameter(req, Integer.class, "ordinal").ifPresent(component::setOrdinal); getParameter(req, Integer.class, "lead").map( @@ -385,7 +409,7 @@ dao.getComponentDao().saveOrUpdate(component, project); - setRedirectLocation(req, "./projects/" + project.getId() + "/components/"); + setRedirectLocation(req, "./projects/" + project.getNode() + "/components/"); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); @@ -461,7 +485,13 @@ } else { component = null; } - issue.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow())); + final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow()); + if (project == null) { + // TODO: improve error handling, because not found is not correct for this POST request + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + return ResponseType.NONE; + } + issue.setProject(project); getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory); getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus); issue.setSubject(getParameter(req, String.class, "subject").orElseThrow()); @@ -493,7 +523,7 @@ dao.getIssueDao().saveOrUpdate(issue, issue.getProject()); // TODO: fix issue #14 - setRedirectLocation(req, "./projects/" + issue.getProject().getId() + "/all-components/all-versions/issues/"); + setRedirectLocation(req, "./projects/" + issue.getProject().getNode() + "/all-components/all-versions/issues/"); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); return ResponseType.HTML; @@ -532,7 +562,7 @@ dao.getIssueDao().saveComment(issueComment); // TODO: fix redirect location (e.g. after fixing #24) - setRedirectLocation(req, "./projects/" + issue.getProject().getId()+"/issues/"+issue.getId()+"/edit"); + setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/edit"); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); return ResponseType.HTML; diff -r a7e543ab0c5f -r e2aa673dd473 src/main/resources/localization/projects.properties --- a/src/main/resources/localization/projects.properties Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/resources/localization/projects.properties Thu Oct 22 13:03:26 2020 +0200 @@ -40,6 +40,8 @@ navmenu.all=all name=Name +node=Node +node.tooltip=Name of the path node that will be used in URL construction. description=Description repoUrl=Repository owner=Project Lead diff -r a7e543ab0c5f -r e2aa673dd473 src/main/resources/localization/projects_de.properties --- a/src/main/resources/localization/projects_de.properties Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/resources/localization/projects_de.properties Thu Oct 22 13:03:26 2020 +0200 @@ -40,6 +40,8 @@ navmenu.all=Alle name=Name +node=Pfadname +node.tooltip=Name, der zur Konstruktion der URL genutzt werden soll. description=Beschreibung repoUrl=Repository owner=Projektleitung diff -r a7e543ab0c5f -r e2aa673dd473 src/main/webapp/WEB-INF/jsp/component-form.jsp --- a/src/main/webapp/WEB-INF/jsp/component-form.jsp Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/component-form.jsp Thu Oct 22 13:03:26 2020 +0200 @@ -50,6 +50,10 @@ " /> + "> + + " /> + diff -r a7e543ab0c5f -r e2aa673dd473 src/main/webapp/WEB-INF/jsp/project-form.jsp --- a/src/main/webapp/WEB-INF/jsp/project-form.jsp Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/project-form.jsp Thu Oct 22 13:03:26 2020 +0200 @@ -42,6 +42,10 @@ " /> + "> + + " /> + " /> diff -r a7e543ab0c5f -r e2aa673dd473 src/main/webapp/WEB-INF/jsp/version-form.jsp --- a/src/main/webapp/WEB-INF/jsp/version-form.jsp Thu Oct 22 12:00:34 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/version-form.jsp Thu Oct 22 13:03:26 2020 +0200 @@ -50,6 +50,10 @@ " /> + "> + + " /> +