Skip to content

Commit 72ef550

Browse files
committed
Merge branch 'develop' into feature-2645/feedback-request-add-ability-to-request-feedback-from-external-source
2 parents adde728 + 1e2d061 commit 72ef550

23 files changed

+632
-33
lines changed

.github/workflows/gradle-build-production.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ jobs:
8787
--set-env-vars "^@^MICRONAUT_ENVIRONMENTS=cloud,google,gcp" \
8888
--set-env-vars "SLACK_WEBHOOK_URL=${{ secrets.SLACK_WEBHOOK_URL }}" \
8989
--set-env-vars "SLACK_BOT_TOKEN=${{ secrets.SLACK_BOT_TOKEN }}" \
90+
--set-env-vars "SLACK_PULSE_SIGNING_SECRET=${{ secrets.SLACK_PULSE_SIGNING_SECRET }}" \
91+
--set-env-vars "SLACK_PULSE_BOT_TOKEN=${{ secrets.SLACK_PULSE_BOT_TOKEN }}" \
9092
--platform "managed" \
9193
--max-instances 8 \
9294
--allow-unauthenticated

.github/workflows/gradle-deploy-develop.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ jobs:
112112
--set-env-vars "^@^MICRONAUT_ENVIRONMENTS=dev,cloud,google,gcp" \
113113
--set-env-vars "SLACK_WEBHOOK_URL=${{ secrets.SLACK_WEBHOOK_URL }}" \
114114
--set-env-vars "SLACK_BOT_TOKEN=${{ secrets.SLACK_BOT_TOKEN }}" \
115+
--set-env-vars "SLACK_PULSE_SIGNING_SECRET=${{ secrets.SLACK_PULSE_SIGNING_SECRET }}" \
116+
--set-env-vars "SLACK_PULSE_BOT_TOKEN=${{ secrets.SLACK_PULSE_BOT_TOKEN }}" \
115117
--platform "managed" \
116118
--max-instances 2 \
117119
--allow-unauthenticated

.github/workflows/gradle-deploy-native-develop.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ jobs:
111111
--set-env-vars "^@^MICRONAUT_ENVIRONMENTS=dev,cloud,google,gcp" \
112112
--set-env-vars "SLACK_WEBHOOK_URL=${{ secrets.SLACK_WEBHOOK_URL }}" \
113113
--set-env-vars "SLACK_BOT_TOKEN=${{ secrets.SLACK_BOT_TOKEN }}" \
114+
--set-env-vars "SLACK_PULSE_SIGNING_SECRET=${{ secrets.SLACK_PULSE_SIGNING_SECRET }}" \
115+
--set-env-vars "SLACK_PULSE_BOT_TOKEN=${{ secrets.SLACK_PULSE_BOT_TOKEN }}" \
114116
--platform "managed" \
115117
--max-instances 2 \
116118
--allow-unauthenticated

docker-compose.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
version: '3'
22
services:
33
postgresql:
4-
image: postgres:11.6
4+
image: postgres:17.2
55
environment:
66
POSTGRES_DB: checkinsdb
77
POSTGRES_USER: postgres

server/src/main/java/com/objectcomputing/checkins/configuration/CheckInsConfiguration.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public static class ApplicationConfig {
3434
@NotNull
3535
private NotificationsConfig notifications;
3636

37+
@NotNull
38+
private PulseResponseConfig pulseResponse;
39+
3740
@Getter
3841
@Setter
3942
@ConfigurationProperties("feedback")
@@ -89,5 +92,25 @@ public static class SlackConfig {
8992
private String botToken;
9093
}
9194
}
95+
96+
@Getter
97+
@Setter
98+
@ConfigurationProperties("pulse-response")
99+
public static class PulseResponseConfig {
100+
101+
@NotNull
102+
private SlackConfig slack;
103+
104+
@Getter
105+
@Setter
106+
@ConfigurationProperties("slack")
107+
public static class SlackConfig {
108+
@NotBlank
109+
private String signingSecret;
110+
111+
@NotBlank
112+
private String botToken;
113+
}
114+
}
92115
}
93116
}

server/src/main/java/com/objectcomputing/checkins/services/pulse/PulseServices.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
import java.time.LocalDate;
44

55
public interface PulseServices {
6-
public void sendPendingEmail(LocalDate now);
6+
public void notifyUsers(LocalDate now);
77
}

server/src/main/java/com/objectcomputing/checkins/services/pulse/PulseServicesImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public PulseServicesImpl(
6969
this.automatedEmailRepository = automatedEmailRepository;
7070
}
7171

72-
public void sendPendingEmail(LocalDate check) {
72+
public void notifyUsers(LocalDate check) {
7373
if (check.getDayOfWeek() == emailDay) {
7474
LOG.info("Checking for pending Pulse email");
7575
// Start from the first of the year and move forward to ensure that we

server/src/main/java/com/objectcomputing/checkins/services/pulseresponse/PulseResponseController.java

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package com.objectcomputing.checkins.services.pulseresponse;
22

33
import com.objectcomputing.checkins.exceptions.NotFoundException;
4+
import com.objectcomputing.checkins.util.form.FormUrlEncodedDecoder;
5+
import com.objectcomputing.checkins.services.memberprofile.MemberProfileServices;
6+
7+
import io.micronaut.http.MediaType;
48
import io.micronaut.core.annotation.Nullable;
59
import io.micronaut.core.convert.format.Format;
10+
import io.micronaut.http.HttpStatus;
611
import io.micronaut.http.HttpRequest;
712
import io.micronaut.http.HttpResponse;
13+
import io.micronaut.http.annotation.Header;
814
import io.micronaut.http.annotation.Body;
915
import io.micronaut.http.annotation.Controller;
1016
import io.micronaut.http.annotation.Get;
@@ -14,6 +20,7 @@
1420
import io.micronaut.scheduling.annotation.ExecuteOn;
1521
import io.micronaut.security.annotation.Secured;
1622
import io.micronaut.security.rules.SecurityRule;
23+
1724
import io.swagger.v3.oas.annotations.tags.Tag;
1825
import jakarta.validation.Valid;
1926
import jakarta.validation.constraints.NotNull;
@@ -22,17 +29,27 @@
2229
import java.time.LocalDate;
2330
import java.util.Set;
2431
import java.util.UUID;
32+
import java.util.Map;
33+
import java.nio.charset.StandardCharsets;
2534

2635
@Controller("/services/pulse-responses")
2736
@ExecuteOn(TaskExecutors.BLOCKING)
28-
@Secured(SecurityRule.IS_AUTHENTICATED)
2937
@Tag(name = "pulse-responses")
3038
public class PulseResponseController {
3139

3240
private final PulseResponseService pulseResponseServices;
41+
private final MemberProfileServices memberProfileServices;
42+
private final SlackSignatureVerifier slackSignatureVerifier;
43+
private final PulseSlackCommand pulseSlackCommand;
3344

34-
public PulseResponseController(PulseResponseService pulseResponseServices) {
45+
public PulseResponseController(PulseResponseService pulseResponseServices,
46+
MemberProfileServices memberProfileServices,
47+
SlackSignatureVerifier slackSignatureVerifier,
48+
PulseSlackCommand pulseSlackCommand) {
3549
this.pulseResponseServices = pulseResponseServices;
50+
this.memberProfileServices = memberProfileServices;
51+
this.slackSignatureVerifier = slackSignatureVerifier;
52+
this.pulseSlackCommand = pulseSlackCommand;
3653
}
3754

3855
/**
@@ -43,6 +60,7 @@ public PulseResponseController(PulseResponseService pulseResponseServices) {
4360
* @param dateTo
4461
* @return
4562
*/
63+
@Secured(SecurityRule.IS_AUTHENTICATED)
4664
@Get("/{?teamMemberId,dateFrom,dateTo}")
4765
public Set<PulseResponse> findPulseResponses(@Nullable @Format("yyyy-MM-dd") LocalDate dateFrom,
4866
@Nullable @Format("yyyy-MM-dd") LocalDate dateTo,
@@ -56,6 +74,7 @@ public Set<PulseResponse> findPulseResponses(@Nullable @Format("yyyy-MM-dd") Loc
5674
* @param pulseResponse, {@link PulseResponseCreateDTO}
5775
* @return {@link HttpResponse<PulseResponse>}
5876
*/
77+
@Secured(SecurityRule.IS_AUTHENTICATED)
5978
@Post
6079
public HttpResponse<PulseResponse> createPulseResponse(@Body @Valid PulseResponseCreateDTO pulseResponse,
6180
HttpRequest<?> request) {
@@ -70,6 +89,7 @@ public HttpResponse<PulseResponse> createPulseResponse(@Body @Valid PulseRespons
7089
* @param pulseResponse, {@link PulseResponse}
7190
* @return {@link HttpResponse<PulseResponse>}
7291
*/
92+
@Secured(SecurityRule.IS_AUTHENTICATED)
7393
@Put
7494
public HttpResponse<PulseResponse> update(@Body @Valid @NotNull PulseResponse pulseResponse,
7595
HttpRequest<?> request) {
@@ -82,6 +102,7 @@ public HttpResponse<PulseResponse> update(@Body @Valid @NotNull PulseResponse pu
82102
* @param id
83103
* @return
84104
*/
105+
@Secured(SecurityRule.IS_AUTHENTICATED)
85106
@Get("/{id}")
86107
public PulseResponse readRole(@NotNull UUID id) {
87108
PulseResponse result = pulseResponseServices.read(id);
@@ -90,4 +111,72 @@ public PulseResponse readRole(@NotNull UUID id) {
90111
}
91112
return result;
92113
}
93-
}
114+
115+
@Secured(SecurityRule.IS_ANONYMOUS)
116+
@Post(uri = "/command", consumes = MediaType.APPLICATION_FORM_URLENCODED)
117+
public HttpResponse commandPulseResponse(
118+
@Header("X-Slack-Signature") String signature,
119+
@Header("X-Slack-Request-Timestamp") String timestamp,
120+
@Body String requestBody) {
121+
// Validate the request
122+
if (slackSignatureVerifier.verifyRequest(signature,
123+
timestamp, requestBody)) {
124+
// Convert the request body to a map of values.
125+
FormUrlEncodedDecoder formUrlEncodedDecoder = new FormUrlEncodedDecoder();
126+
Map<String, Object> body =
127+
formUrlEncodedDecoder.decode(requestBody,
128+
StandardCharsets.UTF_8);
129+
130+
// Respond to the slack command.
131+
String triggerId = (String)body.get("trigger_id");
132+
if (pulseSlackCommand.send(triggerId)) {
133+
return HttpResponse.ok();
134+
} else {
135+
return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR);
136+
}
137+
} else {
138+
return HttpResponse.unauthorized();
139+
}
140+
}
141+
142+
@Secured(SecurityRule.IS_ANONYMOUS)
143+
@Post(uri = "/external", consumes = MediaType.APPLICATION_FORM_URLENCODED)
144+
public HttpResponse<PulseResponse> externalPulseResponse(
145+
@Header("X-Slack-Signature") String signature,
146+
@Header("X-Slack-Request-Timestamp") String timestamp,
147+
@Body String requestBody,
148+
HttpRequest<?> request) {
149+
// Validate the request
150+
if (slackSignatureVerifier.verifyRequest(signature,
151+
timestamp, requestBody)) {
152+
PulseResponseCreateDTO pulseResponseDTO =
153+
SlackPulseResponseConverter.get(memberProfileServices,
154+
requestBody);
155+
156+
// Create the pulse response
157+
PulseResponse pulseResponse = pulseResponseServices.unsecureSave(
158+
new PulseResponse(
159+
pulseResponseDTO.getInternalScore(),
160+
pulseResponseDTO.getExternalScore(),
161+
pulseResponseDTO.getSubmissionDate(),
162+
pulseResponseDTO.getTeamMemberId(),
163+
pulseResponseDTO.getInternalFeelings(),
164+
pulseResponseDTO.getExternalFeelings()
165+
)
166+
);
167+
168+
if (pulseResponse == null) {
169+
return HttpResponse.status(HttpStatus.CONFLICT,
170+
"Already submitted today");
171+
} else {
172+
return HttpResponse.created(pulseResponse)
173+
.headers(headers -> headers.location(
174+
URI.create(String.format("%s/%s",
175+
request.getPath(),
176+
pulseResponse.getId()))));
177+
}
178+
} else {
179+
return HttpResponse.unauthorized();
180+
}
181+
}
182+
}

server/src/main/java/com/objectcomputing/checkins/services/pulseresponse/PulseResponseRepository.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import jakarta.validation.constraints.NotNull;
77

88
import java.time.LocalDate;
9+
import java.util.Optional;
910
import java.util.List;
1011
import java.util.UUID;
1112

@@ -14,4 +15,5 @@ public interface PulseResponseRepository extends CrudRepository<PulseResponse, U
1415

1516
List<PulseResponse> findByTeamMemberId(@NotNull UUID teamMemberId);
1617
List<PulseResponse> findBySubmissionDateBetween(@NotNull LocalDate dateFrom, @NotNull LocalDate dateTo);
17-
}
18+
Optional<PulseResponse> getByTeamMemberIdAndSubmissionDate(@NotNull UUID teamMemberId, @NotNull LocalDate submissionDate);
19+
}

server/src/main/java/com/objectcomputing/checkins/services/pulseresponse/PulseResponseService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ public interface PulseResponseService {
99
PulseResponse read(UUID id);
1010

1111
PulseResponse save(PulseResponse pulseResponse);
12+
PulseResponse unsecureSave(PulseResponse pulseResponse);
1213

1314
PulseResponse update(PulseResponse pulseResponse);
1415

1516
Set<PulseResponse> findByFields(UUID teamMemberId, LocalDate dateFrom, LocalDate dateTo);
16-
}
17+
}

0 commit comments

Comments
 (0)