Skip to content

Commit

Permalink
Merge pull request #492 from Health-Education-England/refactor/traine…
Browse files Browse the repository at this point in the history
…eTisId

refactor: inject trainee ID in to controllers
  • Loading branch information
Judge40 authored Nov 11, 2024
2 parents 32bdb22 + 8c46959 commit f774300
Show file tree
Hide file tree
Showing 13 changed files with 491 additions and 98 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ plugins {
}

group = "uk.nhs.hee.trainee.details"
version = "1.13.1"
version = "1.14.0"

configurations {
compileOnly {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,19 @@
package uk.nhs.hee.trainee.details.api;

import com.amazonaws.xray.spring.aop.XRayEnabled;
import java.io.IOException;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import uk.nhs.hee.trainee.details.api.util.AuthTokenUtil;
import uk.nhs.hee.trainee.details.dto.GmcDetailsDto;
import uk.nhs.hee.trainee.details.dto.PersonalDetailsDto;
import uk.nhs.hee.trainee.details.dto.TraineeIdentity;
import uk.nhs.hee.trainee.details.dto.validation.UserUpdate;
import uk.nhs.hee.trainee.details.mapper.PersonalDetailsMapper;
import uk.nhs.hee.trainee.details.model.PersonalDetails;
Expand All @@ -51,10 +48,13 @@ public class BasicDetailsResource {

private final PersonalDetailsService service;
private final PersonalDetailsMapper mapper;
private final TraineeIdentity traineeIdentity;

public BasicDetailsResource(PersonalDetailsService service, PersonalDetailsMapper mapper) {
public BasicDetailsResource(PersonalDetailsService service, PersonalDetailsMapper mapper,
TraineeIdentity traineeIdentity) {
this.service = service;
this.mapper = mapper;
this.traineeIdentity = traineeIdentity;
}

/**
Expand All @@ -77,19 +77,16 @@ public ResponseEntity<PersonalDetailsDto> updateBasicDetails(
/**
* Update the GMC number for the authenticated trainee.
*
* @param token The authentication token.
* @param gmcDetails The new GMC details.
* @return The updated PersonalDetails.
*/
@PutMapping("/gmc-number")
public ResponseEntity<PersonalDetailsDto> updateGmcNumber(
@RequestHeader(HttpHeaders.AUTHORIZATION) String token,
@Validated(UserUpdate.class) @RequestBody GmcDetailsDto gmcDetails) {
String tisId;
try {
tisId = AuthTokenUtil.getTraineeTisId(token);
} catch (IOException e) {
log.warn("Unable to read tisId from token.", e);
String tisId = traineeIdentity.getTraineeId();

if (tisId == null) {
log.warn("No trainee ID provided.");
return ResponseEntity.badRequest().build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -37,12 +36,11 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import uk.nhs.hee.trainee.details.api.util.AuthTokenUtil;
import uk.nhs.hee.trainee.details.dto.ProgrammeMembershipDto;
import uk.nhs.hee.trainee.details.dto.TraineeIdentity;
import uk.nhs.hee.trainee.details.mapper.ProgrammeMembershipMapper;
import uk.nhs.hee.trainee.details.model.ProgrammeMembership;
import uk.nhs.hee.trainee.details.service.EventPublishService;
Expand All @@ -60,15 +58,18 @@ public class ProgrammeMembershipResource {
private final ProgrammeMembershipService service;
private final ProgrammeMembershipMapper mapper;
private final EventPublishService eventPublishService;
private final TraineeIdentity traineeIdentity;

/**
* ProgrammeMembershipResource class constructor.
*/
public ProgrammeMembershipResource(ProgrammeMembershipService service,
ProgrammeMembershipMapper mapper, EventPublishService eventPublishService) {
ProgrammeMembershipMapper mapper, EventPublishService eventPublishService,
TraineeIdentity traineeIdentity) {
this.service = service;
this.mapper = mapper;
this.eventPublishService = eventPublishService;
this.traineeIdentity = traineeIdentity;
}

/**
Expand Down Expand Up @@ -149,15 +150,12 @@ public ResponseEntity<Boolean> deleteProgrammeMembership(
*/
@PostMapping("/{programmeMembershipId}/sign-coj")
public ResponseEntity<ProgrammeMembershipDto> signCoj(
@PathVariable String programmeMembershipId,
@RequestHeader(HttpHeaders.AUTHORIZATION) String token) {
@PathVariable String programmeMembershipId) {
log.info("Signing COJ with Programme Membership ID {}", programmeMembershipId);
String traineeTisId = traineeIdentity.getTraineeId();

String traineeTisId;
try {
traineeTisId = AuthTokenUtil.getTraineeTisId(token);
} catch (IOException e) {
log.warn("Unable to read tisId from token.", e);
if (traineeTisId == null) {
log.warn("No trainee ID provided.");
return ResponseEntity.badRequest().build();
}

Expand All @@ -176,20 +174,15 @@ public ResponseEntity<ProgrammeMembershipDto> signCoj(
* Generate programme confirmation PDF of a programme membership.
*
* @param programmeMembershipId The ID of the programme membership for generating PDF.
* @param token The authorization token from the request header.
* @return The generated Programme Membership confirmation PDF.
*/
@GetMapping(value = "/{programmeMembershipId}/confirmation",
produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<byte[]> downloadPdf(
@PathVariable String programmeMembershipId,
@RequestHeader(HttpHeaders.AUTHORIZATION) String token) {
public ResponseEntity<byte[]> downloadPdf(@PathVariable String programmeMembershipId) {
String traineeTisId = traineeIdentity.getTraineeId();

String traineeTisId;
try {
traineeTisId = AuthTokenUtil.getTraineeTisId(token);
} catch (IOException e) {
log.warn("Unable to read tisId from token.", e);
if (traineeTisId == null) {
log.warn("No trainee ID provided.");
return ResponseEntity.badRequest().build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,19 @@

import com.amazonaws.xray.spring.aop.XRayEnabled;
import jakarta.validation.constraints.NotNull;
import java.io.IOException;
import java.time.LocalDate;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import uk.nhs.hee.trainee.details.api.util.AuthTokenUtil;
import uk.nhs.hee.trainee.details.dto.PersonalDetailsDto;
import uk.nhs.hee.trainee.details.dto.TraineeIdentity;
import uk.nhs.hee.trainee.details.dto.TraineeProfileDto;
import uk.nhs.hee.trainee.details.dto.UserDetails;
import uk.nhs.hee.trainee.details.mapper.TraineeProfileMapper;
Expand All @@ -53,28 +50,27 @@ public class TraineeProfileResource {

private final TraineeProfileService service;
private final TraineeProfileMapper mapper;
private final TraineeIdentity traineeIdentity;

protected TraineeProfileResource(TraineeProfileService service, TraineeProfileMapper mapper) {
protected TraineeProfileResource(TraineeProfileService service, TraineeProfileMapper mapper,
TraineeIdentity traineeIdentity) {
this.service = service;
this.mapper = mapper;
this.traineeIdentity = traineeIdentity;
}

/**
* Get a trainee's profile.
*
* @param token The authorization token from the request header.
* @return The {@link PersonalDetailsDto} representing the trainee profile.
*/
@GetMapping
public ResponseEntity<TraineeProfileDto> getTraineeProfile(
@RequestHeader(HttpHeaders.AUTHORIZATION) String token) {
public ResponseEntity<TraineeProfileDto> getTraineeProfile() {
log.info("Trainee Profile of authenticated user.");
String tisId = traineeIdentity.getTraineeId();

String tisId;
try {
tisId = AuthTokenUtil.getTraineeTisId(token);
} catch (IOException e) {
log.warn("Unable to read tisId from token.", e);
if (tisId == null) {
log.warn("No trainee ID provided.");
return ResponseEntity.badRequest().build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* The MIT License (MIT)
*
* Copyright 2024 Crown Copyright (Health Education England)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package uk.nhs.hee.trainee.details.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.annotation.RequestScope;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import uk.nhs.hee.trainee.details.dto.TraineeIdentity;
import uk.nhs.hee.trainee.details.interceptor.TraineeIdentityInterceptor;

/**
* Configuration for interceptors.
*/
@Configuration
public class InterceptorConfiguration implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(traineeIdentityInterceptor());
}

/**
* Create an interceptor for creating a {@link TraineeIdentity} from a request.
*
* @return The trainee identity inteceptor.
*/
@Bean
public TraineeIdentityInterceptor traineeIdentityInterceptor() {
return new TraineeIdentityInterceptor(traineeIdentity());
}

/**
* Create a {@link TraineeIdentity} for each request.
*
* @return The created trainee identity.
*/
@Bean
@RequestScope
public TraineeIdentity traineeIdentity() {
return new TraineeIdentity();
}
}
33 changes: 33 additions & 0 deletions src/main/java/uk/nhs/hee/trainee/details/dto/TraineeIdentity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* The MIT License (MIT)
*
* Copyright 2024 Crown Copyright (Health Education England)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package uk.nhs.hee.trainee.details.dto;

import lombok.Data;

/**
* Trainee identity data for an authenticated user.
*/
@Data
public class TraineeIdentity {

String traineeId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* The MIT License (MIT)
*
* Copyright 2024 Crown Copyright (Health Education England)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package uk.nhs.hee.trainee.details.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.web.servlet.HandlerInterceptor;
import uk.nhs.hee.trainee.details.api.util.AuthTokenUtil;
import uk.nhs.hee.trainee.details.dto.TraineeIdentity;

/**
* An interceptor for creating a {@link TraineeIdentity} from a request.
*/
@Slf4j
public class TraineeIdentityInterceptor implements HandlerInterceptor {

private final TraineeIdentity traineeIdentity;

public TraineeIdentityInterceptor(TraineeIdentity traineeIdentity) {
this.traineeIdentity = traineeIdentity;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
String authToken = request.getHeader(HttpHeaders.AUTHORIZATION);

if (authToken != null) {
try {
String traineeId = AuthTokenUtil.getTraineeTisId(authToken);
traineeIdentity.setTraineeId(traineeId);
} catch (IOException e) {
log.warn("Unable to extract trainee ID from authorization token.", e);
}
}

return true;
}
}
Loading

0 comments on commit f774300

Please sign in to comment.