• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DataBiosphere / consent / #6225

28 Jul 2025 12:14PM UTC coverage: 81.088% (+0.4%) from 80.694%
#6225

push

web-flow
DT-2026: Bring feature branch into develop (#2621)

Co-authored-by: Elliot Otchet <otchet@broadinstitute.org>
Co-authored-by: Rachel Johanek <55256690+rjohanek@users.noreply.github.com>
Co-authored-by: rjohanek <rjohanek@broadinstitute.org>
Co-authored-by: Matt Bemis <MatthewBemis@users.noreply.github.com>
Co-authored-by: otchet-broad <111771148+otchet-broad@users.noreply.github.com>

397 of 437 new or added lines in 34 files covered. (90.85%)

10775 of 13288 relevant lines covered (81.09%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.0
/src/main/java/org/broadinstitute/consent/http/resources/DACAutomationRuleResource.java
1
package org.broadinstitute.consent.http.resources;
2

3
import com.google.inject.Inject;
4
import io.dropwizard.auth.Auth;
5
import jakarta.annotation.security.PermitAll;
6
import jakarta.ws.rs.ForbiddenException;
7
import jakarta.ws.rs.GET;
8
import jakarta.ws.rs.PUT;
9
import jakarta.ws.rs.Path;
10
import jakarta.ws.rs.PathParam;
11
import jakarta.ws.rs.Produces;
12
import jakarta.ws.rs.QueryParam;
13
import jakarta.ws.rs.core.MediaType;
14
import jakarta.ws.rs.core.Response;
15
import java.util.List;
16
import java.util.Objects;
17
import java.util.stream.Stream;
18
import javax.annotation.security.RolesAllowed;
19
import org.broadinstitute.consent.http.enumeration.UserRoles;
20
import org.broadinstitute.consent.http.models.AuthUser;
21
import org.broadinstitute.consent.http.models.User;
22
import org.broadinstitute.consent.http.models.UserRole;
23
import org.broadinstitute.consent.http.rules.AuditPageResults;
24
import org.broadinstitute.consent.http.rules.DACAutomationRule;
25
import org.broadinstitute.consent.http.service.DACAutomationRuleService;
26
import org.broadinstitute.consent.http.service.DacService;
27
import org.broadinstitute.consent.http.service.UserService;
28

29
@Path("api/dac/")
30
public class DACAutomationRuleResource extends Resource {
31

32
  private final DACAutomationRuleService ruleService;
33
  private final DacService dacService;
34
  private final UserService userService;
35

36
  @Inject
37
  public DACAutomationRuleResource(DACAutomationRuleService ruleService, DacService dacService,
38
      UserService userService) {
1✔
39
    this.ruleService = ruleService;
1✔
40
    this.dacService = dacService;
1✔
41
    this.userService = userService;
1✔
42
  }
1✔
43

44
  @GET
45
  @Path("rules")
46
  @Produces(MediaType.APPLICATION_JSON)
47
  @PermitAll
48
  public Response getAllRules() {
49
    try {
50
      List<DACAutomationRule> rules = ruleService.findAll();
1✔
51
      return Response.ok(rules).build();
1✔
NEW
52
    } catch (Exception e) {
×
NEW
53
      return createExceptionResponse(e);
×
54
    }
55
  }
56

57
  @GET
58
  @Path("{dacId}/rules")
59
  @Produces(MediaType.APPLICATION_JSON)
60
  @RolesAllowed({Resource.ADMIN, Resource.CHAIRPERSON})
61
  public Response getAvailableRules(@Auth AuthUser authUser, @PathParam("dacId") Integer dacId) {
62
    try {
63
      User user = userService.findUserByEmail(authUser.getEmail());
1✔
64
      validateAdminOrChairForDAC(user, dacId);
1✔
65

66
      if (dacService.findById(dacId) == null) {
1✔
NEW
67
        return Response.status(Response.Status.NOT_FOUND).build();
×
68
      }
69

70
      List<DACAutomationRule> rules = ruleService.findAllByDacId(dacId);
1✔
71

72
      return Response.ok(rules).build();
1✔
73
    } catch (Exception e) {
1✔
74
      return createExceptionResponse(e);
1✔
75
    }
76
  }
77

78
  @GET
79
  @Path("{dacId}/rules/audit")
80
  @Produces(MediaType.APPLICATION_JSON)
81
  @RolesAllowed({Resource.ADMIN, Resource.CHAIRPERSON})
82
  public Response getDacRuleAuditRecords(@Auth AuthUser authUser, @PathParam("dacId") Integer dacId,
83
      @QueryParam("page") Integer page, @QueryParam("pageSize") Integer pageSize) {
84
    try {
NEW
85
      User user = userService.findUserByEmail(authUser.getEmail());
×
NEW
86
      validateAdminOrChairForDAC(user, dacId);
×
87

NEW
88
      if (dacService.findById(dacId) == null) {
×
NEW
89
        return Response.status(Response.Status.NOT_FOUND).build();
×
90
      }
NEW
91
      validateOffsetAndLimit(page, pageSize);
×
NEW
92
      AuditPageResults auditRecords = ruleService.findAuditRecords(dacId, pageSize, page);
×
93

NEW
94
      return Response.ok(auditRecords).build();
×
NEW
95
    } catch (Exception e) {
×
NEW
96
      return createExceptionResponse(e);
×
97
    }
98
  }
99

100
  @PUT
101
  @Path("{dacId}/rules/{ruleId}/toggle")
102
  @Produces(MediaType.APPLICATION_JSON)
103
  @RolesAllowed({Resource.ADMIN, Resource.CHAIRPERSON})
104
  public Response toggleRule(@Auth AuthUser authUser, @PathParam("dacId") Integer dacId,
105
      @PathParam("ruleId") Integer ruleId) {
106
    try {
107
      User user = userService.findUserByEmail(authUser.getEmail());
1✔
108
      validateIsChairOfDAC(user, dacId);
1✔
109

110
      if (dacService.findById(dacId) == null) {
1✔
NEW
111
        return Response.status(Response.Status.NOT_FOUND).build();
×
112
      }
113

114
      return Response.ok(ruleService.toggleRule(dacId, ruleId, user)).build();
1✔
115
    } catch (Exception e) {
1✔
116
      return createExceptionResponse(e);
1✔
117
    }
118
  }
119

120
  private void validateAdminOrChairForDAC(User user, Integer dacId) {
121
    //TODO: harmonize this with the logic in the DACResource so we're consistent.
122
    boolean isAdminOrChair = user.hasUserRole(UserRoles.ADMIN) || isChairOfDAC(user, dacId);
1✔
123
    if (!isAdminOrChair) {
1✔
124
      throw new ForbiddenException("User does not have access to the specified DAC ID");
1✔
125
    }
126
  }
1✔
127

128
  private void validateIsChairOfDAC(User user, Integer dacId) {
129
    if (!isChairOfDAC(user, dacId)) {
1✔
130
      throw new ForbiddenException("User does not have access to the specified DAC ID");
1✔
131
    }
132
  }
1✔
133

134
  private void validateOffsetAndLimit(Integer offset, Integer limit) {
NEW
135
    if (offset < 1 || limit < 1) {
×
NEW
136
      throw new IllegalArgumentException("Page and PageSize must be zero or greater");
×
137
    }
138

NEW
139
    if (limit > 100) {
×
NEW
140
      throw new IllegalArgumentException("PageSize must be less than or equal to 100");
×
141
    }
142

NEW
143
  }
×
144

145
  private boolean isChairOfDAC(User user, Integer dacId) {
146
    return Stream.ofNullable(user.getRoles())
1✔
147
        .flatMap(List::stream)
1✔
148
        .filter(r -> r.getRoleId().equals(UserRoles.Chairperson().getRoleId()))
1✔
149
        .map(UserRole::getDacId)
1✔
150
        .anyMatch(id -> Objects.equals(id, dacId));
1✔
151
  }
152

153
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc