src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java

changeset 184
e8eecee6aadf
parent 183
61669abf277f
child 185
5ec9fcfbdf9c
     1.1 --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java	Sat Jan 23 14:47:59 2021 +0100
     1.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.3 @@ -1,660 +0,0 @@
     1.4 -/*
     1.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     1.6 - *
     1.7 - * Copyright 2021 Mike Becker. All rights reserved.
     1.8 - *
     1.9 - * Redistribution and use in source and binary forms, with or without
    1.10 - * modification, are permitted provided that the following conditions are met:
    1.11 - *
    1.12 - *   1. Redistributions of source code must retain the above copyright
    1.13 - *      notice, this list of conditions and the following disclaimer.
    1.14 - *
    1.15 - *   2. Redistributions in binary form must reproduce the above copyright
    1.16 - *      notice, this list of conditions and the following disclaimer in the
    1.17 - *      documentation and/or other materials provided with the distribution.
    1.18 - *
    1.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    1.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    1.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    1.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    1.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    1.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    1.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    1.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    1.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    1.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    1.29 - * POSSIBILITY OF SUCH DAMAGE.
    1.30 - *
    1.31 - */
    1.32 -package de.uapcore.lightpit.modules;
    1.33 -
    1.34 -
    1.35 -import de.uapcore.lightpit.*;
    1.36 -import de.uapcore.lightpit.dao.DataAccessObject;
    1.37 -import de.uapcore.lightpit.entities.*;
    1.38 -import de.uapcore.lightpit.filter.AllFilter;
    1.39 -import de.uapcore.lightpit.filter.IssueFilter;
    1.40 -import de.uapcore.lightpit.filter.NoneFilter;
    1.41 -import de.uapcore.lightpit.filter.SpecificFilter;
    1.42 -import de.uapcore.lightpit.types.IssueCategory;
    1.43 -import de.uapcore.lightpit.types.IssueStatus;
    1.44 -import de.uapcore.lightpit.types.VersionStatus;
    1.45 -import de.uapcore.lightpit.types.WebColor;
    1.46 -import de.uapcore.lightpit.viewmodel.*;
    1.47 -import de.uapcore.lightpit.viewmodel.util.IssueSorter;
    1.48 -import org.slf4j.Logger;
    1.49 -import org.slf4j.LoggerFactory;
    1.50 -
    1.51 -import javax.servlet.ServletException;
    1.52 -import javax.servlet.annotation.WebServlet;
    1.53 -import javax.servlet.http.HttpServletRequest;
    1.54 -import javax.servlet.http.HttpServletResponse;
    1.55 -import java.io.IOException;
    1.56 -import java.sql.Date;
    1.57 -import java.sql.SQLException;
    1.58 -import java.util.NoSuchElementException;
    1.59 -import java.util.Optional;
    1.60 -import java.util.stream.Collectors;
    1.61 -import java.util.stream.Stream;
    1.62 -
    1.63 -@WebServlet(
    1.64 -        name = "ProjectsModule",
    1.65 -        urlPatterns = "/projects/*"
    1.66 -)
    1.67 -public final class ProjectsModule extends AbstractServlet {
    1.68 -
    1.69 -    private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class);
    1.70 -
    1.71 -    private static int parseIntOrZero(String str) {
    1.72 -        try {
    1.73 -            return Integer.parseInt(str);
    1.74 -        } catch (NumberFormatException ex) {
    1.75 -            return 0;
    1.76 -        }
    1.77 -    }
    1.78 -
    1.79 -    private void populate(ProjectView viewModel, PathParameters pathParameters, DataAccessObject dao) {
    1.80 -        dao.listProjects().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add);
    1.81 -
    1.82 -        if (pathParameters == null)
    1.83 -            return;
    1.84 -
    1.85 -        // Select Project
    1.86 -        final var project = dao.findProjectByNode(pathParameters.get("project"));
    1.87 -        if (project == null)
    1.88 -            return;
    1.89 -
    1.90 -        final var info = new ProjectInfo(project);
    1.91 -        info.setVersions(dao.listVersions(project));
    1.92 -        info.setComponents(dao.listComponents(project));
    1.93 -        info.setIssueSummary(dao.collectIssueSummary(project));
    1.94 -        viewModel.setProjectInfo(info);
    1.95 -
    1.96 -        // Select Version
    1.97 -        final var versionNode = pathParameters.get("version");
    1.98 -        if (versionNode != null) {
    1.99 -            if ("no-version".equals(versionNode)) {
   1.100 -                viewModel.setVersionFilter(ProjectView.NO_VERSION);
   1.101 -            } else if ("all-versions".equals(versionNode)) {
   1.102 -                viewModel.setVersionFilter(ProjectView.ALL_VERSIONS);
   1.103 -            } else {
   1.104 -                viewModel.setVersionFilter(dao.findVersionByNode(project, versionNode));
   1.105 -            }
   1.106 -        }
   1.107 -
   1.108 -        // Select Component
   1.109 -        final var componentNode = pathParameters.get("component");
   1.110 -        if (componentNode != null) {
   1.111 -            if ("no-component".equals(componentNode)) {
   1.112 -                viewModel.setComponentFilter(ProjectView.NO_COMPONENT);
   1.113 -            } else if ("all-components".equals(componentNode)) {
   1.114 -                viewModel.setComponentFilter(ProjectView.ALL_COMPONENTS);
   1.115 -            } else {
   1.116 -                viewModel.setComponentFilter(dao.findComponentByNode(project, componentNode));
   1.117 -            }
   1.118 -        }
   1.119 -    }
   1.120 -
   1.121 -    private static String sanitizeNode(String node, String defaultValue) {
   1.122 -        String result = node == null || node.isBlank() ? defaultValue : node;
   1.123 -        result = result.replace('/', '-');
   1.124 -        if (result.equals(".") || result.equals("..")) {
   1.125 -            return "_"+result;
   1.126 -        } else {
   1.127 -            return result;
   1.128 -        }
   1.129 -    }
   1.130 -
   1.131 -    private void forwardView(HttpServletRequest req, HttpServletResponse resp, ProjectView viewModel, String name) throws ServletException, IOException {
   1.132 -        setViewModel(req, viewModel);
   1.133 -        setContentPage(req, name);
   1.134 -        setStylesheet(req, "projects");
   1.135 -        setNavigationMenu(req, "project-navmenu");
   1.136 -        renderSite(req, resp);
   1.137 -    }
   1.138 -
   1.139 -    @RequestMapping(method = HttpMethod.GET)
   1.140 -    public void index(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws ServletException, IOException {
   1.141 -        final var viewModel = new ProjectView();
   1.142 -        populate(viewModel, null, dao);
   1.143 -
   1.144 -        for (var info : viewModel.getProjectList()) {
   1.145 -            info.setVersions(dao.listVersions(info.getProject()));
   1.146 -            info.setIssueSummary(dao.collectIssueSummary(info.getProject()));
   1.147 -        }
   1.148 -
   1.149 -        forwardView(req, resp, viewModel, "projects");
   1.150 -    }
   1.151 -
   1.152 -    private void configureProjectEditor(ProjectEditView viewModel, Project project, DataAccessObject dao) {
   1.153 -        viewModel.setProject(project);
   1.154 -        viewModel.setUsers(dao.listUsers());
   1.155 -    }
   1.156 -
   1.157 -    @RequestMapping(requestPath = "$project/edit", method = HttpMethod.GET)
   1.158 -    public void edit(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObject dao) throws IOException, SQLException, ServletException {
   1.159 -        final var viewModel = new ProjectEditView();
   1.160 -        populate(viewModel, pathParams, dao);
   1.161 -
   1.162 -        if (!viewModel.isProjectInfoPresent()) {
   1.163 -            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.164 -            return;
   1.165 -        }
   1.166 -
   1.167 -        configureProjectEditor(viewModel, viewModel.getProjectInfo().getProject(), dao);
   1.168 -        forwardView(req, resp, viewModel, "project-form");
   1.169 -    }
   1.170 -
   1.171 -    @RequestMapping(requestPath = "create", method = HttpMethod.GET)
   1.172 -    public void create(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws SQLException, ServletException, IOException {
   1.173 -        final var viewModel = new ProjectEditView();
   1.174 -        populate(viewModel, null, dao);
   1.175 -        configureProjectEditor(viewModel, new Project(-1), dao);
   1.176 -        forwardView(req, resp, viewModel, "project-form");
   1.177 -    }
   1.178 -
   1.179 -    @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
   1.180 -    public void commit(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
   1.181 -
   1.182 -        try {
   1.183 -            final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow());
   1.184 -            project.setName(getParameter(req, String.class, "name").orElseThrow());
   1.185 -
   1.186 -            final var node = getParameter(req, String.class, "node").orElse(null);
   1.187 -            project.setNode(sanitizeNode(node, project.getName()));
   1.188 -            getParameter(req, Integer.class, "ordinal").ifPresent(project::setOrdinal);
   1.189 -
   1.190 -            getParameter(req, String.class, "description").ifPresent(project::setDescription);
   1.191 -            getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl);
   1.192 -            getParameter(req, Integer.class, "owner").map(
   1.193 -                    ownerId -> ownerId >= 0 ? new User(ownerId) : null
   1.194 -            ).ifPresent(project::setOwner);
   1.195 -
   1.196 -            if (project.getId() > 0) {
   1.197 -                dao.updateProject(project);
   1.198 -            } else {
   1.199 -                dao.insertProject(project);
   1.200 -            }
   1.201 -
   1.202 -            setRedirectLocation(req, "./projects/");
   1.203 -            setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   1.204 -            LOG.debug("Successfully updated project {}", project.getName());
   1.205 -
   1.206 -            renderSite(req, resp);
   1.207 -        } catch (NoSuchElementException | IllegalArgumentException ex) {
   1.208 -            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   1.209 -            // TODO: implement - fix issue #21
   1.210 -        }
   1.211 -    }
   1.212 -
   1.213 -    @RequestMapping(requestPath = "$project/$component/$version/issues/", method = HttpMethod.GET)
   1.214 -    public void issues(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObject dao) throws SQLException, IOException, ServletException {
   1.215 -        final var viewModel = new ProjectDetailsView();
   1.216 -        populate(viewModel, pathParams, dao);
   1.217 -
   1.218 -        if (!viewModel.isEveryFilterValid()) {
   1.219 -            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.220 -            return;
   1.221 -        }
   1.222 -
   1.223 -        final var project = viewModel.getProjectInfo().getProject();
   1.224 -        final var version = viewModel.getVersionFilter();
   1.225 -        final var component = viewModel.getComponentFilter();
   1.226 -
   1.227 -        // TODO: use new IssueFilter class for the ViewModel
   1.228 -
   1.229 -        final var projectFilter = new SpecificFilter<>(project);
   1.230 -        final IssueFilter filter;
   1.231 -        if (version.equals(ProjectView.NO_VERSION)) {
   1.232 -            if (component.equals(ProjectView.ALL_COMPONENTS)) {
   1.233 -                filter = new IssueFilter(projectFilter,
   1.234 -                        new NoneFilter<>(),
   1.235 -                        new AllFilter<>()
   1.236 -                );
   1.237 -            } else if (component.equals(ProjectView.NO_COMPONENT)) {
   1.238 -                filter = new IssueFilter(projectFilter,
   1.239 -                        new NoneFilter<>(),
   1.240 -                        new NoneFilter<>()
   1.241 -                );
   1.242 -            } else {
   1.243 -                filter = new IssueFilter(projectFilter,
   1.244 -                        new NoneFilter<>(),
   1.245 -                        new SpecificFilter<>(component)
   1.246 -                );
   1.247 -            }
   1.248 -        } else if (version.equals(ProjectView.ALL_VERSIONS)) {
   1.249 -            if (component.equals(ProjectView.ALL_COMPONENTS)) {
   1.250 -                filter = new IssueFilter(projectFilter,
   1.251 -                        new AllFilter<>(),
   1.252 -                        new AllFilter<>()
   1.253 -                );
   1.254 -            } else if (component.equals(ProjectView.NO_COMPONENT)) {
   1.255 -                filter = new IssueFilter(projectFilter,
   1.256 -                        new AllFilter<>(),
   1.257 -                        new NoneFilter<>()
   1.258 -                );
   1.259 -            } else {
   1.260 -                filter = new IssueFilter(projectFilter,
   1.261 -                        new AllFilter<>(),
   1.262 -                        new SpecificFilter<>(component)
   1.263 -                );
   1.264 -            }
   1.265 -        } else {
   1.266 -            if (component.equals(ProjectView.ALL_COMPONENTS)) {
   1.267 -                filter = new IssueFilter(projectFilter,
   1.268 -                        new SpecificFilter<>(version),
   1.269 -                        new AllFilter<>()
   1.270 -                );
   1.271 -            } else if (component.equals(ProjectView.NO_COMPONENT)) {
   1.272 -                filter = new IssueFilter(projectFilter,
   1.273 -                        new SpecificFilter<>(version),
   1.274 -                        new NoneFilter<>()
   1.275 -                );
   1.276 -            } else {
   1.277 -                filter = new IssueFilter(projectFilter,
   1.278 -                        new SpecificFilter<>(version),
   1.279 -                        new SpecificFilter<>(component)
   1.280 -                );
   1.281 -            }
   1.282 -        }
   1.283 -
   1.284 -        final var issues = dao.listIssues(filter);
   1.285 -        issues.sort(new IssueSorter(
   1.286 -                new IssueSorter.Criteria(IssueSorter.Field.DONE, true),
   1.287 -                new IssueSorter.Criteria(IssueSorter.Field.ETA, true),
   1.288 -                new IssueSorter.Criteria(IssueSorter.Field.UPDATED, false)
   1.289 -        ));
   1.290 -
   1.291 -
   1.292 -        viewModel.getProjectDetails().updateDetails(issues);
   1.293 -        if (version.getId() > 0)
   1.294 -            viewModel.getProjectDetails().updateVersionInfo(version);
   1.295 -
   1.296 -        forwardView(req, resp, viewModel, "project-details");
   1.297 -    }
   1.298 -
   1.299 -    @RequestMapping(requestPath = "$project/versions/", method = HttpMethod.GET)
   1.300 -    public void versions(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException {
   1.301 -        final var viewModel = new VersionsView();
   1.302 -        populate(viewModel, pathParameters, dao);
   1.303 -
   1.304 -        final var projectInfo = viewModel.getProjectInfo();
   1.305 -        if (projectInfo == null) {
   1.306 -            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.307 -            return;
   1.308 -        }
   1.309 -
   1.310 -        final var issues = dao.listIssues(
   1.311 -                new IssueFilter(
   1.312 -                        new SpecificFilter<>(projectInfo.getProject()),
   1.313 -                        new AllFilter<>(),
   1.314 -                        new AllFilter<>()
   1.315 -                )
   1.316 -        );
   1.317 -        viewModel.update(projectInfo.getVersions(), issues);
   1.318 -
   1.319 -        forwardView(req, resp, viewModel, "versions");
   1.320 -    }
   1.321 -
   1.322 -    @RequestMapping(requestPath = "$project/versions/$version/edit", method = HttpMethod.GET)
   1.323 -    public void editVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
   1.324 -        final var viewModel = new VersionEditView();
   1.325 -        populate(viewModel, pathParameters, dao);
   1.326 -
   1.327 -        if (viewModel.getProjectInfo() == null || viewModel.getVersionFilter() == null) {
   1.328 -            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.329 -            return;
   1.330 -        }
   1.331 -
   1.332 -        viewModel.setVersion(viewModel.getVersionFilter());
   1.333 -
   1.334 -        forwardView(req, resp, viewModel, "version-form");
   1.335 -    }
   1.336 -
   1.337 -    @RequestMapping(requestPath = "$project/create-version", method = HttpMethod.GET)
   1.338 -    public void createVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
   1.339 -        final var viewModel = new VersionEditView();
   1.340 -        populate(viewModel, pathParameters, dao);
   1.341 -
   1.342 -        if (viewModel.getProjectInfo() == null) {
   1.343 -            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.344 -            return;
   1.345 -        }
   1.346 -
   1.347 -        viewModel.setVersion(new Version(-1, viewModel.getProjectInfo().getProject().getId()));
   1.348 -
   1.349 -        forwardView(req, resp, viewModel, "version-form");
   1.350 -    }
   1.351 -
   1.352 -    @RequestMapping(requestPath = "commit-version", method = HttpMethod.POST)
   1.353 -    public void commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
   1.354 -
   1.355 -        try {
   1.356 -            final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow());
   1.357 -            if (project == null) {
   1.358 -                // TODO: improve error handling, because not found is not correct for this POST request
   1.359 -                resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.360 -                return;
   1.361 -            }
   1.362 -            final var version = new Version(getParameter(req, Integer.class, "id").orElseThrow(), project.getId());
   1.363 -            version.setName(getParameter(req, String.class, "name").orElseThrow());
   1.364 -
   1.365 -            final var node = getParameter(req, String.class, "node").orElse(null);
   1.366 -            version.setNode(sanitizeNode(node, version.getName()));
   1.367 -
   1.368 -            getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
   1.369 -            version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
   1.370 -
   1.371 -            if (version.getId() > 0) {
   1.372 -                dao.updateVersion(version);
   1.373 -            } else {
   1.374 -                dao.insertVersion(version);
   1.375 -            }
   1.376 -
   1.377 -            setRedirectLocation(req, "./projects/" + project.getNode() + "/versions/");
   1.378 -            setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   1.379 -
   1.380 -            renderSite(req, resp);
   1.381 -        } catch (NoSuchElementException | IllegalArgumentException ex) {
   1.382 -            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   1.383 -            // TODO: implement - fix issue #21
   1.384 -        }
   1.385 -    }
   1.386 -
   1.387 -    @RequestMapping(requestPath = "$project/components/", method = HttpMethod.GET)
   1.388 -    public void components(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException {
   1.389 -        final var viewModel = new ComponentsView();
   1.390 -        populate(viewModel, pathParameters, dao);
   1.391 -
   1.392 -        final var projectInfo = viewModel.getProjectInfo();
   1.393 -        if (projectInfo == null) {
   1.394 -            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.395 -            return;
   1.396 -        }
   1.397 -
   1.398 -        final var issues = dao.listIssues(
   1.399 -                new IssueFilter(
   1.400 -                        new SpecificFilter<>(projectInfo.getProject()),
   1.401 -                        new AllFilter<>(),
   1.402 -                        new AllFilter<>()
   1.403 -                )
   1.404 -        );
   1.405 -        viewModel.update(projectInfo.getComponents(), issues);
   1.406 -
   1.407 -        forwardView(req, resp, viewModel, "components");
   1.408 -    }
   1.409 -
   1.410 -    @RequestMapping(requestPath = "$project/components/$component/edit", method = HttpMethod.GET)
   1.411 -    public void editComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
   1.412 -        final var viewModel = new ComponentEditView();
   1.413 -        populate(viewModel, pathParameters, dao);
   1.414 -
   1.415 -        if (viewModel.getProjectInfo() == null || viewModel.getComponentFilter() == null) {
   1.416 -            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.417 -            return;
   1.418 -        }
   1.419 -
   1.420 -        viewModel.setComponent(viewModel.getComponentFilter());
   1.421 -        viewModel.setUsers(dao.listUsers());
   1.422 -
   1.423 -        forwardView(req, resp, viewModel, "component-form");
   1.424 -    }
   1.425 -
   1.426 -    @RequestMapping(requestPath = "$project/create-component", method = HttpMethod.GET)
   1.427 -    public void createComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
   1.428 -        final var viewModel = new ComponentEditView();
   1.429 -        populate(viewModel, pathParameters, dao);
   1.430 -
   1.431 -        if (viewModel.getProjectInfo() == null) {
   1.432 -            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.433 -            return;
   1.434 -        }
   1.435 -
   1.436 -        viewModel.setComponent(new Component(-1, viewModel.getProjectInfo().getProject().getId()));
   1.437 -        viewModel.setUsers(dao.listUsers());
   1.438 -
   1.439 -        forwardView(req, resp, viewModel, "component-form");
   1.440 -    }
   1.441 -
   1.442 -    @RequestMapping(requestPath = "commit-component", method = HttpMethod.POST)
   1.443 -    public void commitComponent(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
   1.444 -
   1.445 -        try {
   1.446 -            final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow());
   1.447 -            if (project == null) {
   1.448 -                // TODO: improve error handling, because not found is not correct for this POST request
   1.449 -                resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.450 -                return;
   1.451 -            }
   1.452 -            final var component = new Component(getParameter(req, Integer.class, "id").orElseThrow(), project.getId());
   1.453 -            component.setName(getParameter(req, String.class, "name").orElseThrow());
   1.454 -
   1.455 -            final var node = getParameter(req, String.class, "node").orElse(null);
   1.456 -            component.setNode(sanitizeNode(node, component.getName()));
   1.457 -
   1.458 -            component.setColor(getParameter(req, WebColor.class, "color").orElseThrow());
   1.459 -            getParameter(req, Integer.class, "ordinal").ifPresent(component::setOrdinal);
   1.460 -            getParameter(req, Integer.class, "lead").map(
   1.461 -                    userid -> userid >= 0 ? new User(userid) : null
   1.462 -            ).ifPresent(component::setLead);
   1.463 -            getParameter(req, String.class, "description").ifPresent(component::setDescription);
   1.464 -
   1.465 -            if (component.getId() > 0) {
   1.466 -                dao.updateComponent(component);
   1.467 -            } else {
   1.468 -                dao.insertComponent(component);
   1.469 -            }
   1.470 -
   1.471 -            setRedirectLocation(req, "./projects/" + project.getNode() + "/components/");
   1.472 -            setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   1.473 -
   1.474 -            renderSite(req, resp);
   1.475 -        } catch (NoSuchElementException | IllegalArgumentException ex) {
   1.476 -            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   1.477 -            // TODO: implement - fix issue #21
   1.478 -        }
   1.479 -    }
   1.480 -
   1.481 -    private void configureIssueEditor(IssueEditView viewModel, Issue issue, DataAccessObject dao) {
   1.482 -        final var project = viewModel.getProjectInfo().getProject();
   1.483 -        issue.setProject(project); // automatically set current project for new issues
   1.484 -        viewModel.setIssue(issue);
   1.485 -        viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions());
   1.486 -        viewModel.setUsers(dao.listUsers());
   1.487 -        viewModel.setComponents(dao.listComponents(project));
   1.488 -    }
   1.489 -
   1.490 -    @RequestMapping(requestPath = "$project/issues/$issue/view", method = HttpMethod.GET)
   1.491 -    public void viewIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
   1.492 -        final var viewModel = new IssueDetailView();
   1.493 -        populate(viewModel, pathParameters, dao);
   1.494 -
   1.495 -        final var projectInfo = viewModel.getProjectInfo();
   1.496 -        if (projectInfo == null) {
   1.497 -            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.498 -            return;
   1.499 -        }
   1.500 -
   1.501 -        final var issue = dao.findIssue(parseIntOrZero(pathParameters.get("issue")));
   1.502 -        if (issue == null) {
   1.503 -            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.504 -            return;
   1.505 -        }
   1.506 -
   1.507 -        viewModel.setIssue(issue);
   1.508 -        viewModel.setComments(dao.listComments(issue));
   1.509 -
   1.510 -        viewModel.processMarkdown();
   1.511 -
   1.512 -        forwardView(req, resp, viewModel, "issue-view");
   1.513 -    }
   1.514 -
   1.515 -    // TODO: why should the issue editor be child of $project?
   1.516 -    @RequestMapping(requestPath = "$project/issues/$issue/edit", method = HttpMethod.GET)
   1.517 -    public void editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException {
   1.518 -        final var viewModel = new IssueEditView();
   1.519 -        populate(viewModel, pathParameters, dao);
   1.520 -
   1.521 -        final var projectInfo = viewModel.getProjectInfo();
   1.522 -        if (projectInfo == null) {
   1.523 -            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.524 -            return;
   1.525 -        }
   1.526 -
   1.527 -        final var issue = dao.findIssue(parseIntOrZero(pathParameters.get("issue")));
   1.528 -        if (issue == null) {
   1.529 -            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.530 -            return;
   1.531 -        }
   1.532 -
   1.533 -        configureIssueEditor(viewModel, issue, dao);
   1.534 -
   1.535 -        forwardView(req, resp, viewModel, "issue-form");
   1.536 -    }
   1.537 -
   1.538 -    @RequestMapping(requestPath = "$project/create-issue", method = HttpMethod.GET)
   1.539 -    public void createIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
   1.540 -        final var viewModel = new IssueEditView();
   1.541 -        populate(viewModel, pathParameters, dao);
   1.542 -
   1.543 -        final var projectInfo = viewModel.getProjectInfo();
   1.544 -        if (projectInfo == null) {
   1.545 -            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.546 -            return;
   1.547 -        }
   1.548 -
   1.549 -        setAttributeFromParameter(req, "more");
   1.550 -        setAttributeFromParameter(req, "cid");
   1.551 -        setAttributeFromParameter(req, "vid");
   1.552 -
   1.553 -        final var issue = new Issue(-1, projectInfo.getProject(), null);
   1.554 -        issue.setProject(projectInfo.getProject());
   1.555 -        configureIssueEditor(viewModel, issue, dao);
   1.556 -
   1.557 -        forwardView(req, resp, viewModel, "issue-form");
   1.558 -    }
   1.559 -
   1.560 -    @RequestMapping(requestPath = "commit-issue", method = HttpMethod.POST)
   1.561 -    public void commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
   1.562 -        try {
   1.563 -            final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow());
   1.564 -            if (project == null) {
   1.565 -                // TODO: improve error handling, because not found is not correct for this POST request
   1.566 -                resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.567 -                return;
   1.568 -            }
   1.569 -            final var componentId = getParameter(req, Integer.class, "component");
   1.570 -            final Component component;
   1.571 -            if (componentId.isPresent()) {
   1.572 -                component = dao.findComponent(componentId.get());
   1.573 -            } else {
   1.574 -                component = null;
   1.575 -            }
   1.576 -            final var issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow(), project, component);
   1.577 -            getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory);
   1.578 -            getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus);
   1.579 -            issue.setSubject(getParameter(req, String.class, "subject").orElseThrow());
   1.580 -            getParameter(req, Integer.class, "assignee").map(userid -> {
   1.581 -                if (userid >= 0) {
   1.582 -                    return new User(userid);
   1.583 -                } else if (userid == -2) {
   1.584 -                    return Optional.ofNullable(component).map(Component::getLead).orElse(null);
   1.585 -                } else {
   1.586 -                    return null;
   1.587 -                }
   1.588 -            }
   1.589 -            ).ifPresent(issue::setAssignee);
   1.590 -            getParameter(req, String.class, "description").ifPresent(issue::setDescription);
   1.591 -            getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
   1.592 -
   1.593 -            getParameter(req, Integer[].class, "affected")
   1.594 -                    .map(Stream::of)
   1.595 -                    .map(stream ->
   1.596 -                            stream.map(id -> new Version(id, project.getId()))
   1.597 -                                    .collect(Collectors.toList())
   1.598 -                    ).ifPresent(issue::setAffectedVersions);
   1.599 -            getParameter(req, Integer[].class, "resolved")
   1.600 -                    .map(Stream::of)
   1.601 -                    .map(stream ->
   1.602 -                            stream.map(id -> new Version(id, project.getId()))
   1.603 -                                    .collect(Collectors.toList())
   1.604 -                    ).ifPresent(issue::setResolvedVersions);
   1.605 -
   1.606 -            if (issue.getId() > 0) {
   1.607 -                dao.updateIssue(issue);
   1.608 -            } else {
   1.609 -                dao.insertIssue(issue);
   1.610 -            }
   1.611 -
   1.612 -            if (getParameter(req, Boolean.class, "create-another").orElse(false)) {
   1.613 -                // TODO: fix #38 - automatically select component (and version)
   1.614 -                setRedirectLocation(req, "./projects/" + issue.getProject().getNode() + "/create-issue?more=true");
   1.615 -            } else{
   1.616 -                setRedirectLocation(req, "./projects/" + issue.getProject().getNode() + "/issues/" + issue.getId() + "/view");
   1.617 -            }
   1.618 -            setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   1.619 -
   1.620 -            renderSite(req, resp);
   1.621 -        } catch (NoSuchElementException | IllegalArgumentException ex) {
   1.622 -            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   1.623 -            // TODO: implement - fix issue #21
   1.624 -        }
   1.625 -    }
   1.626 -
   1.627 -    @RequestMapping(requestPath = "commit-issue-comment", method = HttpMethod.POST)
   1.628 -    public void commentIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
   1.629 -        final var issueIdParam = getParameter(req, Integer.class, "issueid");
   1.630 -        if (issueIdParam.isEmpty()) {
   1.631 -            resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Detected manipulated form.");
   1.632 -            return;
   1.633 -        }
   1.634 -        final var issue = dao.findIssue(issueIdParam.get());
   1.635 -        if (issue == null) {
   1.636 -            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.637 -            return;
   1.638 -        }
   1.639 -        try {
   1.640 -            final var issueComment = new IssueComment(getParameter(req, Integer.class, "commentid").orElse(-1), issue.getId());
   1.641 -            issueComment.setComment(getParameter(req, String.class, "comment").orElse(""));
   1.642 -
   1.643 -            if (issueComment.getComment().isBlank()) {
   1.644 -                throw new IllegalArgumentException("comment.null");
   1.645 -            }
   1.646 -
   1.647 -            LOG.debug("User {} is commenting on issue #{}", req.getRemoteUser(), issue.getId());
   1.648 -            if (req.getRemoteUser() != null) {
   1.649 -                Optional.ofNullable(dao.findUserByName(req.getRemoteUser())).ifPresent(issueComment::setAuthor);
   1.650 -            }
   1.651 -
   1.652 -            dao.insertComment(issueComment);
   1.653 -
   1.654 -            setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view");
   1.655 -            setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   1.656 -
   1.657 -            renderSite(req, resp);
   1.658 -        } catch (NoSuchElementException | IllegalArgumentException ex) {
   1.659 -            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   1.660 -            // TODO: implement - fix issue #21
   1.661 -        }
   1.662 -    }
   1.663 -}

mercurial