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

Sat, 29 Aug 2020 17:13:09 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 29 Aug 2020 17:13:09 +0200
changeset 105
250c5cbb8276
parent 104
8be80ea4f52b
child 107
b5f740a87af4
permissions
-rw-r--r--

simplifies issues per version view and re-adds edit version button

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2018 Mike Becker. All rights reserved.
     5  *
     6  * Redistribution and use in source and binary forms, with or without
     7  * modification, are permitted provided that the following conditions are met:
     8  *
     9  *   1. Redistributions of source code must retain the above copyright
    10  *      notice, this list of conditions and the following disclaimer.
    11  *
    12  *   2. Redistributions in binary form must reproduce the above copyright
    13  *      notice, this list of conditions and the following disclaimer in the
    14  *      documentation and/or other materials provided with the distribution.
    15  *
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    26  * POSSIBILITY OF SUCH DAMAGE.
    27  *
    28  */
    29 package de.uapcore.lightpit.modules;
    32 import de.uapcore.lightpit.*;
    33 import de.uapcore.lightpit.dao.DataAccessObjects;
    34 import de.uapcore.lightpit.entities.*;
    35 import de.uapcore.lightpit.viewmodel.*;
    36 import org.slf4j.Logger;
    37 import org.slf4j.LoggerFactory;
    39 import javax.servlet.annotation.WebServlet;
    40 import javax.servlet.http.HttpServletRequest;
    41 import javax.servlet.http.HttpServletResponse;
    42 import java.io.IOException;
    43 import java.sql.Date;
    44 import java.sql.SQLException;
    45 import java.util.ArrayList;
    46 import java.util.NoSuchElementException;
    47 import java.util.Optional;
    48 import java.util.stream.Collectors;
    49 import java.util.stream.Stream;
    51 import static de.uapcore.lightpit.Functions.fqn;
    53 @WebServlet(
    54         name = "ProjectsModule",
    55         urlPatterns = "/projects/*"
    56 )
    57 public final class ProjectsModule extends AbstractLightPITServlet {
    59     private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class);
    61     private static final String SESSION_ATTR_SELECTED_PROJECT = fqn(ProjectsModule.class, "selected_project");
    62     private static final String SESSION_ATTR_SELECTED_VERSION = fqn(ProjectsModule.class, "selected_version");
    63     private static final String PARAMETER_SELECTED_PROJECT = "pid";
    64     private static final String PARAMETER_SELECTED_VERSION = "vid";
    66     @Override
    67     protected String getResourceBundleName() {
    68         return "localization.projects";
    69     }
    71     private String queryParams(Project p, Version v) {
    72         return String.format("pid=%d&vid=%d",
    73                 p == null ? -1 : p.getId(),
    74                 v == null ? -1 : v.getId()
    75         );
    76     }
    78     /**
    79      * Creates the navigation menu.
    80      *
    81      * @param req the servlet request
    82      * @param viewModel the current view model
    83      */
    84     private void setNavigationMenu(HttpServletRequest req, ProjectView viewModel) {
    85         final Project selectedProject = Optional.ofNullable(viewModel.getProjectInfo()).map(ProjectInfo::getProject).orElse(null);
    87         final var navigation = new ArrayList<MenuEntry>();
    89         for (ProjectInfo plistInfo : viewModel.getProjectList()) {
    90             final var proj = plistInfo.getProject();
    91             final var projEntry = new MenuEntry(
    92                     proj.getName(),
    93                     "projects/view?" + queryParams(proj, null)
    94             );
    95             navigation.add(projEntry);
    96             if (proj.equals(selectedProject)) {
    97                 final var projInfo = viewModel.getProjectInfo();
    98                 projEntry.setActive(true);
   100                 // ****************
   101                 // Versions Section
   102                 // ****************
   103                 {
   104                     final var entry = new MenuEntry(1,
   105                             new ResourceKey(getResourceBundleName(), "menu.versions"),
   106                             "projects/view?" + queryParams(proj, null)
   107                     );
   108                     navigation.add(entry);
   109                 }
   111                 final var level2 = new ArrayList<MenuEntry>();
   112                 {
   113                     final var entry = new MenuEntry(
   114                             new ResourceKey(getResourceBundleName(), "filter.none"),
   115                             "projects/view?" + queryParams(proj, null)
   116                     );
   117                     if (viewModel.getVersionFilter() == null) entry.setActive(true);
   118                     level2.add(entry);
   119                 }
   121                 for (Version version : projInfo.getVersions()) {
   122                     final var entry = new MenuEntry(
   123                             version.getName(),
   124                             "projects/view?" + queryParams(proj, version)
   125                     );
   126                     if (version.equals(viewModel.getVersionFilter())) entry.setActive(true);
   127                     level2.add(entry);
   128                 }
   130                 level2.forEach(e -> e.setLevel(2));
   131                 navigation.addAll(level2);
   132             }
   133         }
   135         setNavigationMenu(req, navigation);
   136     }
   138     private int syncParamWithSession(HttpServletRequest req, String param, String attr) {
   139         final var session = req.getSession();
   140         final var idParam = getParameter(req, Integer.class, param);
   141         final int id;
   142         if (idParam.isPresent()) {
   143             id = idParam.get();
   144             session.setAttribute(attr, id);
   145         } else {
   146             id = Optional.ofNullable(session.getAttribute(attr)).map(x->(Integer)x).orElse(-1);
   147         }
   148         return id;
   149     }
   151     private void populate(ProjectView viewModel, HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   152         final var projectDao = dao.getProjectDao();
   153         final var versionDao = dao.getVersionDao();
   155         projectDao.list().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add);
   157         // Select Project
   158         final int pid = syncParamWithSession(req, PARAMETER_SELECTED_PROJECT, SESSION_ATTR_SELECTED_PROJECT);
   159         if (pid >= 0) {
   160             final var project = projectDao.find(pid);
   161             final var info = new ProjectInfo(project);
   162             info.setVersions(versionDao.list(project));
   163             info.setIssueSummary(projectDao.getIssueSummary(project));
   164             viewModel.setProjectInfo(info);
   165         }
   167         // Select Version
   168         final int vid = syncParamWithSession(req, PARAMETER_SELECTED_VERSION, SESSION_ATTR_SELECTED_VERSION);
   169         if (vid >= 0) {
   170             viewModel.setVersionFilter(versionDao.find(vid));
   171         }
   172     }
   174     private ResponseType forwardView(HttpServletRequest req, ProjectView viewModel, String name) {
   175         setViewModel(req, viewModel);
   176         setContentPage(req, name);
   177         setStylesheet(req, "projects");
   178         setNavigationMenu(req, viewModel);
   179         return ResponseType.HTML;
   180     }
   182     @RequestMapping(method = HttpMethod.GET)
   183     public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   184         final var viewModel = new ProjectView();
   185         populate(viewModel, req, dao);
   187         final var projectDao = dao.getProjectDao();
   188         final var versionDao = dao.getVersionDao();
   190         for (var info : viewModel.getProjectList()) {
   191             info.setVersions(versionDao.list(info.getProject()));
   192             info.setIssueSummary(projectDao.getIssueSummary(info.getProject()));
   193         }
   195         return forwardView(req, viewModel, "projects");
   196     }
   198     private void configure(ProjectEditView viewModel, Project project, DataAccessObjects dao) throws SQLException {
   199         viewModel.setProject(project);
   200         viewModel.setUsers(dao.getUserDao().list());
   201     }
   203     @RequestMapping(requestPath = "edit", method = HttpMethod.GET)
   204     public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   205         final var viewModel = new ProjectEditView();
   206         populate(viewModel, req, dao);
   208         final var project = Optional.ofNullable(viewModel.getProjectInfo())
   209                 .map(ProjectInfo::getProject)
   210                 .orElse(new Project(-1));
   211         configure(viewModel, project, dao);
   213         return forwardView(req, viewModel, "project-form");
   214     }
   216     @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
   217     public ResponseType commit(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   219         Project project = new Project(-1);
   220         try {
   221             project = new Project(getParameter(req, Integer.class, "pid").orElseThrow());
   222             project.setName(getParameter(req, String.class, "name").orElseThrow());
   223             getParameter(req, String.class, "description").ifPresent(project::setDescription);
   224             getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl);
   225             getParameter(req, Integer.class, "owner").map(
   226                     ownerId -> ownerId >= 0 ? new User(ownerId) : null
   227             ).ifPresent(project::setOwner);
   229             dao.getProjectDao().saveOrUpdate(project);
   231             setRedirectLocation(req, "./projects/view?pid="+project.getId());
   232             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   233             LOG.debug("Successfully updated project {}", project.getName());
   235             return ResponseType.HTML;
   236         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   237             LOG.warn("Form validation failure: {}", ex.getMessage());
   238             LOG.debug("Details:", ex);
   239             final var viewModel = new ProjectEditView();
   240             populate(viewModel, req, dao);
   241             configure(viewModel, project, dao);
   242             // TODO: error text
   243             return forwardView(req, viewModel, "project-form");
   244         }
   245     }
   247     @RequestMapping(requestPath = "view", method = HttpMethod.GET)
   248     public ResponseType view(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException {
   249         final var viewModel = new ProjectDetailsView();
   250         populate(viewModel, req, dao);
   252         if (viewModel.getProjectInfo() == null) {
   253             resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected.");
   254             return ResponseType.NONE;
   255         }
   257         final var issueDao = dao.getIssueDao();
   259         final var version = viewModel.getVersionFilter();
   261         final var detailView = viewModel.getProjectDetails();
   262         final var issues = issueDao.list(version);
   263         for (var issue : issues) issueDao.joinVersionInformation(issue);
   264         detailView.updateDetails(issues, version);
   266         return forwardView(req, viewModel, "project-details");
   267     }
   269     @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET)
   270     public ResponseType editVersion(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   271         final var viewModel = new VersionEditView();
   272         populate(viewModel, req, dao);
   274         if (viewModel.getVersionFilter() == null) {
   275             viewModel.setVersion(new Version(-1));
   276         } else {
   277             viewModel.setVersion(viewModel.getVersionFilter());
   278         }
   280         return forwardView(req, viewModel, "version-form");
   281     }
   283     @RequestMapping(requestPath = "versions/commit", method = HttpMethod.POST)
   284     public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
   286         var version = new Version(-1);
   287         try {
   288             version = new Version(getParameter(req, Integer.class, "id").orElseThrow());
   289             version.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow()));
   290             version.setName(getParameter(req, String.class, "name").orElseThrow());
   291             getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
   292             version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
   293             dao.getVersionDao().saveOrUpdate(version);
   295             // specifying the pid parameter will purposely reset the session selected version!
   296             setRedirectLocation(req, "./projects/view?pid=" + version.getProject().getId());
   297             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   298         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   299             LOG.warn("Form validation failure: {}", ex.getMessage());
   300             LOG.debug("Details:", ex);
   301             final var viewModel = new VersionEditView();
   302             populate(viewModel, req, dao);
   303             viewModel.setVersion(version);
   304             // TODO: set Error Text
   305             return forwardView(req, viewModel, "version-form");
   306         }
   308         return ResponseType.HTML;
   309     }
   311     private void configure(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException {
   312         issue.setProject(viewModel.getProjectInfo().getProject());
   313         viewModel.setIssue(issue);
   314         viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions());
   315         viewModel.setUsers(dao.getUserDao().list());
   316     }
   318     @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET)
   319     public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
   320         final var viewModel = new IssueEditView();
   322         final var issueParam = getParameter(req, Integer.class, "issue");
   323         if (issueParam.isPresent()) {
   324             final var issueDao = dao.getIssueDao();
   325             final var issue = issueDao.find(issueParam.get());
   326             issueDao.joinVersionInformation(issue);
   327             req.getSession().setAttribute(SESSION_ATTR_SELECTED_PROJECT, issue.getProject().getId());
   328             populate(viewModel, req, dao);
   329             configure(viewModel, issue, dao);
   330         } else {
   331             populate(viewModel, req, dao);
   332             configure(viewModel, new Issue(-1), dao);
   333         }
   335         return forwardView(req, viewModel, "issue-form");
   336     }
   338     @RequestMapping(requestPath = "issues/commit", method = HttpMethod.POST)
   339     public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
   340         Issue issue = new Issue(-1);
   341         try {
   342             issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow());
   343             issue.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow()));
   344             getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory);
   345             getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus);
   346             issue.setSubject(getParameter(req, String.class, "subject").orElseThrow());
   347             getParameter(req, Integer.class, "assignee").map(
   348                     userid -> userid >= 0 ? new User(userid) : null
   349             ).ifPresent(issue::setAssignee);
   350             getParameter(req, String.class, "description").ifPresent(issue::setDescription);
   351             getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
   353             getParameter(req, Integer[].class, "affected")
   354                     .map(Stream::of)
   355                     .map(stream ->
   356                             stream.map(Version::new).collect(Collectors.toList())
   357                     ).ifPresent(issue::setAffectedVersions);
   358             getParameter(req, Integer[].class, "resolved")
   359                     .map(Stream::of)
   360                     .map(stream ->
   361                             stream.map(Version::new).collect(Collectors.toList())
   362                     ).ifPresent(issue::setResolvedVersions);
   364             dao.getIssueDao().saveOrUpdate(issue);
   366             // specifying the issue parameter keeps the edited issue as menu item
   367             setRedirectLocation(req, "./projects/view?pid=" + issue.getProject().getId());
   368             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   369         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   370             // TODO: set request attribute with error text
   371             LOG.warn("Form validation failure: {}", ex.getMessage());
   372             LOG.debug("Details:", ex);
   373             final var viewModel = new IssueEditView();
   374             configure(viewModel, issue, dao);
   375             // TODO: set Error Text
   376             return forwardView(req, viewModel, "issue-form");
   377         }
   379         return ResponseType.HTML;
   380     }
   381 }

mercurial