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

Sat, 29 Aug 2020 16:48:15 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 29 Aug 2020 16:48:15 +0200
changeset 104
8be80ea4f52b
parent 102
7eee7cad093d
child 105
250c5cbb8276
permissions
-rw-r--r--

fixes issue not containing version information in issue form

     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.List;
    47 import java.util.NoSuchElementException;
    48 import java.util.Optional;
    49 import java.util.stream.Collectors;
    50 import java.util.stream.Stream;
    52 import static de.uapcore.lightpit.Functions.fqn;
    54 @WebServlet(
    55         name = "ProjectsModule",
    56         urlPatterns = "/projects/*"
    57 )
    58 public final class ProjectsModule extends AbstractLightPITServlet {
    60     private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class);
    62     private static final String SESSION_ATTR_SELECTED_PROJECT = fqn(ProjectsModule.class, "selected_project");
    63     private static final String SESSION_ATTR_SELECTED_VERSION = fqn(ProjectsModule.class, "selected_version");
    64     private static final String PARAMETER_SELECTED_PROJECT = "pid";
    65     private static final String PARAMETER_SELECTED_VERSION = "vid";
    67     @Override
    68     protected String getResourceBundleName() {
    69         return "localization.projects";
    70     }
    72     private String queryParams(Project p, Version v) {
    73         return String.format("pid=%d&vid=%d",
    74                 p == null ? -1 : p.getId(),
    75                 v == null ? -1 : v.getId()
    76         );
    77     }
    79     /**
    80      * Creates the navigation menu.
    81      *
    82      * @param req the servlet request
    83      * @param viewModel the current view model
    84      */
    85     private void setNavigationMenu(HttpServletRequest req, ProjectView viewModel) {
    86         final Project selectedProject = Optional.ofNullable(viewModel.getProjectInfo()).map(ProjectInfo::getProject).orElse(null);
    88         final var navigation = new ArrayList<MenuEntry>();
    90         for (ProjectInfo plistInfo : viewModel.getProjectList()) {
    91             final var proj = plistInfo.getProject();
    92             final var projEntry = new MenuEntry(
    93                     proj.getName(),
    94                     "projects/view?" + queryParams(proj, null)
    95             );
    96             navigation.add(projEntry);
    97             if (proj.equals(selectedProject)) {
    98                 final var projInfo = viewModel.getProjectInfo();
    99                 projEntry.setActive(true);
   101                 // ****************
   102                 // Versions Section
   103                 // ****************
   104                 {
   105                     final var entry = new MenuEntry(1,
   106                             new ResourceKey(getResourceBundleName(), "menu.versions"),
   107                             "projects/view?" + queryParams(proj, null)
   108                     );
   109                     navigation.add(entry);
   110                 }
   112                 final var level2 = new ArrayList<MenuEntry>();
   113                 {
   114                     final var entry = new MenuEntry(
   115                             new ResourceKey(getResourceBundleName(), "filter.all"),
   116                             "projects/view?" + queryParams(proj, null)
   117                     );
   118                     if (viewModel.getVersionFilter() == null) entry.setActive(true);
   119                     level2.add(entry);
   120                 }
   122                 for (Version version : projInfo.getVersions()) {
   123                     final var entry = new MenuEntry(
   124                             version.getName(),
   125                             "projects/view?" + queryParams(proj, version)
   126                     );
   127                     if (version.equals(viewModel.getVersionFilter())) entry.setActive(true);
   128                     level2.add(entry);
   129                 }
   131                 level2.forEach(e -> e.setLevel(2));
   132                 navigation.addAll(level2);
   133             }
   134         }
   136         setNavigationMenu(req, navigation);
   137     }
   139     private int syncParamWithSession(HttpServletRequest req, String param, String attr) {
   140         final var session = req.getSession();
   141         final var idParam = getParameter(req, Integer.class, param);
   142         final int id;
   143         if (idParam.isPresent()) {
   144             id = idParam.get();
   145             session.setAttribute(attr, id);
   146         } else {
   147             id = Optional.ofNullable(session.getAttribute(attr)).map(x->(Integer)x).orElse(-1);
   148         }
   149         return id;
   150     }
   152     private void populate(ProjectView viewModel, HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   153         final var projectDao = dao.getProjectDao();
   154         final var versionDao = dao.getVersionDao();
   156         projectDao.list().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add);
   158         // Select Project
   159         final int pid = syncParamWithSession(req, PARAMETER_SELECTED_PROJECT, SESSION_ATTR_SELECTED_PROJECT);
   160         if (pid >= 0) {
   161             final var project = projectDao.find(pid);
   162             final var info = new ProjectInfo(project);
   163             info.setVersions(versionDao.list(project));
   164             info.setIssueSummary(projectDao.getIssueSummary(project));
   165             viewModel.setProjectInfo(info);
   166         }
   168         // Select Version
   169         final int vid = syncParamWithSession(req, PARAMETER_SELECTED_VERSION, SESSION_ATTR_SELECTED_VERSION);
   170         if (vid >= 0) {
   171             viewModel.setVersionFilter(versionDao.find(vid));
   172         }
   173     }
   175     private ResponseType forwardView(HttpServletRequest req, ProjectView viewModel, String name) {
   176         setViewModel(req, viewModel);
   177         setContentPage(req, name);
   178         setStylesheet(req, "projects");
   179         setNavigationMenu(req, viewModel);
   180         return ResponseType.HTML;
   181     }
   183     @RequestMapping(method = HttpMethod.GET)
   184     public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   185         final var viewModel = new ProjectView();
   186         populate(viewModel, req, dao);
   188         final var projectDao = dao.getProjectDao();
   189         final var versionDao = dao.getVersionDao();
   191         for (var info : viewModel.getProjectList()) {
   192             info.setVersions(versionDao.list(info.getProject()));
   193             info.setIssueSummary(projectDao.getIssueSummary(info.getProject()));
   194         }
   196         return forwardView(req, viewModel, "projects");
   197     }
   199     private void configure(ProjectEditView viewModel, Project project, DataAccessObjects dao) throws SQLException {
   200         viewModel.setProject(project);
   201         viewModel.setUsers(dao.getUserDao().list());
   202     }
   204     @RequestMapping(requestPath = "edit", method = HttpMethod.GET)
   205     public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   206         final var viewModel = new ProjectEditView();
   207         populate(viewModel, req, dao);
   209         final var project = Optional.ofNullable(viewModel.getProjectInfo())
   210                 .map(ProjectInfo::getProject)
   211                 .orElse(new Project(-1));
   212         configure(viewModel, project, dao);
   214         return forwardView(req, viewModel, "project-form");
   215     }
   217     @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
   218     public ResponseType commit(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   220         Project project = new Project(-1);
   221         try {
   222             project = new Project(getParameter(req, Integer.class, "pid").orElseThrow());
   223             project.setName(getParameter(req, String.class, "name").orElseThrow());
   224             getParameter(req, String.class, "description").ifPresent(project::setDescription);
   225             getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl);
   226             getParameter(req, Integer.class, "owner").map(
   227                     ownerId -> ownerId >= 0 ? new User(ownerId) : null
   228             ).ifPresent(project::setOwner);
   230             dao.getProjectDao().saveOrUpdate(project);
   232             setRedirectLocation(req, "./projects/view?pid="+project.getId());
   233             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   234             LOG.debug("Successfully updated project {}", project.getName());
   236             return ResponseType.HTML;
   237         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   238             LOG.warn("Form validation failure: {}", ex.getMessage());
   239             LOG.debug("Details:", ex);
   240             final var viewModel = new ProjectEditView();
   241             populate(viewModel, req, dao);
   242             configure(viewModel, project, dao);
   243             // TODO: error text
   244             return forwardView(req, viewModel, "project-form");
   245         }
   246     }
   248     @RequestMapping(requestPath = "view", method = HttpMethod.GET)
   249     public ResponseType view(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException {
   250         final var viewModel = new ProjectDetailsView();
   251         populate(viewModel, req, dao);
   253         if (viewModel.getProjectInfo() == null) {
   254             resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected.");
   255             return ResponseType.NONE;
   256         }
   258         final var issueDao = dao.getIssueDao();
   260         final var project = viewModel.getProjectInfo().getProject();
   262         final var detailView = viewModel.getProjectDetails();
   263         final var issues = issueDao.list(project);
   264         for (var issue : issues) issueDao.joinVersionInformation(issue);
   265         detailView.setIssues(issues);
   266         if (viewModel.getVersionFilter() != null) {
   267             detailView.updateVersionInfo(List.of(viewModel.getVersionFilter()));
   268         } else {
   269             detailView.updateVersionInfo(viewModel.getProjectInfo().getVersions());
   270         }
   272         return forwardView(req, viewModel, "project-details");
   273     }
   275     @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET)
   276     public ResponseType editVersion(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   277         final var viewModel = new VersionEditView();
   278         populate(viewModel, req, dao);
   280         if (viewModel.getVersionFilter() == null) {
   281             viewModel.setVersion(new Version(-1));
   282         } else {
   283             viewModel.setVersion(viewModel.getVersionFilter());
   284         }
   286         return forwardView(req, viewModel, "version-form");
   287     }
   289     @RequestMapping(requestPath = "versions/commit", method = HttpMethod.POST)
   290     public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
   292         var version = new Version(-1);
   293         try {
   294             version = new Version(getParameter(req, Integer.class, "id").orElseThrow());
   295             version.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow()));
   296             version.setName(getParameter(req, String.class, "name").orElseThrow());
   297             getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
   298             version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
   299             dao.getVersionDao().saveOrUpdate(version);
   301             // specifying the pid parameter will purposely reset the session selected version!
   302             setRedirectLocation(req, "./projects/view?pid=" + version.getProject().getId());
   303             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   304         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   305             LOG.warn("Form validation failure: {}", ex.getMessage());
   306             LOG.debug("Details:", ex);
   307             final var viewModel = new VersionEditView();
   308             populate(viewModel, req, dao);
   309             viewModel.setVersion(version);
   310             // TODO: set Error Text
   311             return forwardView(req, viewModel, "version-form");
   312         }
   314         return ResponseType.HTML;
   315     }
   317     private void configure(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException {
   318         issue.setProject(viewModel.getProjectInfo().getProject());
   319         viewModel.setIssue(issue);
   320         viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions());
   321         viewModel.setUsers(dao.getUserDao().list());
   322     }
   324     @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET)
   325     public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
   326         final var viewModel = new IssueEditView();
   328         final var issueParam = getParameter(req, Integer.class, "issue");
   329         if (issueParam.isPresent()) {
   330             final var issueDao = dao.getIssueDao();
   331             final var issue = issueDao.find(issueParam.get());
   332             issueDao.joinVersionInformation(issue);
   333             req.getSession().setAttribute(SESSION_ATTR_SELECTED_PROJECT, issue.getProject().getId());
   334             populate(viewModel, req, dao);
   335             configure(viewModel, issue, dao);
   336         } else {
   337             populate(viewModel, req, dao);
   338             configure(viewModel, new Issue(-1), dao);
   339         }
   341         return forwardView(req, viewModel, "issue-form");
   342     }
   344     @RequestMapping(requestPath = "issues/commit", method = HttpMethod.POST)
   345     public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
   346         Issue issue = new Issue(-1);
   347         try {
   348             issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow());
   349             issue.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow()));
   350             getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory);
   351             getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus);
   352             issue.setSubject(getParameter(req, String.class, "subject").orElseThrow());
   353             getParameter(req, Integer.class, "assignee").map(
   354                     userid -> userid >= 0 ? new User(userid) : null
   355             ).ifPresent(issue::setAssignee);
   356             getParameter(req, String.class, "description").ifPresent(issue::setDescription);
   357             getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
   359             getParameter(req, Integer[].class, "affected")
   360                     .map(Stream::of)
   361                     .map(stream ->
   362                             stream.map(Version::new).collect(Collectors.toList())
   363                     ).ifPresent(issue::setAffectedVersions);
   364             getParameter(req, Integer[].class, "resolved")
   365                     .map(Stream::of)
   366                     .map(stream ->
   367                             stream.map(Version::new).collect(Collectors.toList())
   368                     ).ifPresent(issue::setResolvedVersions);
   370             dao.getIssueDao().saveOrUpdate(issue);
   372             // specifying the issue parameter keeps the edited issue as menu item
   373             setRedirectLocation(req, "./projects/view?pid=" + issue.getProject().getId());
   374             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   375         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   376             // TODO: set request attribute with error text
   377             LOG.warn("Form validation failure: {}", ex.getMessage());
   378             LOG.debug("Details:", ex);
   379             final var viewModel = new IssueEditView();
   380             configure(viewModel, issue, dao);
   381             // TODO: set Error Text
   382             return forwardView(req, viewModel, "issue-form");
   383         }
   385         return ResponseType.HTML;
   386     }
   387 }

mercurial