11package com .objectcomputing .checkins .services .pulseresponse ;
22
33import 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 ;
48import io .micronaut .core .annotation .Nullable ;
59import io .micronaut .core .convert .format .Format ;
10+ import io .micronaut .http .HttpStatus ;
611import io .micronaut .http .HttpRequest ;
712import io .micronaut .http .HttpResponse ;
13+ import io .micronaut .http .annotation .Header ;
814import io .micronaut .http .annotation .Body ;
915import io .micronaut .http .annotation .Controller ;
1016import io .micronaut .http .annotation .Get ;
1420import io .micronaut .scheduling .annotation .ExecuteOn ;
1521import io .micronaut .security .annotation .Secured ;
1622import io .micronaut .security .rules .SecurityRule ;
23+
1724import io .swagger .v3 .oas .annotations .tags .Tag ;
1825import jakarta .validation .Valid ;
1926import jakarta .validation .constraints .NotNull ;
2229import java .time .LocalDate ;
2330import java .util .Set ;
2431import 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" )
3038public 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+ }
0 commit comments