UsersController.java

package se.jobtechdev.personaldatagateway.api.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.context.request.NativeWebRequest;
import se.jobtechdev.personaldatagateway.api.generated.api.UsersApi;
import se.jobtechdev.personaldatagateway.api.generated.entities.ClientEntity;
import se.jobtechdev.personaldatagateway.api.generated.entities.SharingEntity;
import se.jobtechdev.personaldatagateway.api.generated.model.Sharing;
import se.jobtechdev.personaldatagateway.api.service.ClientService;
import se.jobtechdev.personaldatagateway.api.service.DatasourceService;
import se.jobtechdev.personaldatagateway.api.service.PersonService;
import se.jobtechdev.personaldatagateway.api.service.SharingService;
import se.jobtechdev.personaldatagateway.api.util.LinkHeaderUtil;
import se.jobtechdev.personaldatagateway.api.util.OffsetBasedPageRequest;
import se.jobtechdev.personaldatagateway.api.util.ResponseFactory;

import java.util.List;
import java.util.Objects;
import java.util.UUID;

import static se.jobtechdev.personaldatagateway.api.util.ControllerUtil.earlyExit;
import static se.jobtechdev.personaldatagateway.api.util.ControllerUtil.throwApiExceptionOnAbsentValue;

@Controller
public class UsersController implements UsersApi {
  private final String baseUrl;

  private final NativeWebRequest request;

  private final SharingService sharingService;

  private final PersonService personService;

  private final DatasourceService datasourceService;

  private final ClientService clientService;

  @Autowired
  public UsersController(
      @Value("${pdg-api.base-url}") String baseUrl,
      NativeWebRequest request,
      SharingService sharingService,
      PersonService personService,
      DatasourceService datasourceService,
      ClientService clientService) {
    this.baseUrl = baseUrl;
    this.request = request;
    this.sharingService = sharingService;
    this.personService = personService;
    this.datasourceService = datasourceService;
    this.clientService = clientService;
  }

  @Override
  public ResponseEntity<Sharing> postUserSharing(String userId, Sharing sharing) {
    final var publicSharing = sharing.getPublic();
    final var clientId = sharing.getClientId();
    final var expires = sharing.getExpires();
    final var datasourceId = sharing.getDatasourceId();

    final var user =
        personService.getPersonById(userId).orElseGet(() -> personService.createPerson(userId));

    final var datasource =
        throwApiExceptionOnAbsentValue(
            datasourceService.getDatasourceById(datasourceId),
            HttpStatus.BAD_REQUEST,
            "Could not find datasource by datasourceId: " + datasourceId);

    final SharingEntity sharingEntity;
    ClientEntity client = null;

    if (Boolean.TRUE.equals(publicSharing)) {
      earlyExit(
          clientId,
          Objects::nonNull,
          HttpStatus.UNPROCESSABLE_ENTITY,
          "The \"public\" and \"clientId\" properties are mutually exclusive");
    } else {
      earlyExit(
          clientId,
          Objects::isNull,
          HttpStatus.UNPROCESSABLE_ENTITY,
          "One of \"public\" or \"clientId\" properties must be defined");

      client =
          throwApiExceptionOnAbsentValue(
              clientService.getClientById(clientId),
              HttpStatus.BAD_REQUEST,
              "Could not find client by clientId: " + clientId);
    }

    final var activeSharings = sharingService.getActiveSharings(user, datasource, client);

    final HttpStatus httpStatus;
    if (!activeSharings.isEmpty()) {
      sharingEntity = activeSharings.getFirst();
      httpStatus = HttpStatus.OK;
    } else {
      sharingEntity = sharingService.createSharing(user, datasource, publicSharing, expires, client);
      httpStatus = HttpStatus.CREATED;
    }

    final var response = ResponseFactory.createSharing(sharingEntity);

    final var headers =
        LinkHeaderUtil.createHeadersWithLocation(
            response.getSharingId().toString(), new Class<?>[]{String.class, Sharing.class}, userId, response);

    return ResponseEntity.status(httpStatus)
        .contentType(MediaType.APPLICATION_JSON)
        .headers(headers)
        .body(response);
  }

  @Override
  public ResponseEntity<Sharing> getUserSharing(String userId, UUID sharingId) {
    final var sharing =
        throwApiExceptionOnAbsentValue(
            sharingService.getSharingById(sharingId),
            HttpStatus.NOT_FOUND,
            String.format(
                "Failed to handle GET user sharing request - Could not find sharing with id:"
                    + " %s",
                sharingId));

    final var response = ResponseFactory.createSharing(sharing);

    final var headers = LinkHeaderUtil.createHeaders(new Class<?>[]{String.class, UUID.class}, userId, sharingId);

    return ResponseEntity.status(HttpStatus.OK)
        .contentType(MediaType.APPLICATION_JSON)
        .headers(headers)
        .body(response);
  }

  @Override
  public ResponseEntity<List<Sharing>> getUserSharings(
      String userId, Integer offset, Integer limit) {
    throwApiExceptionOnAbsentValue(
        personService.getPersonById(userId),
        HttpStatus.NOT_FOUND,
        String.format(
            "Failed to handle GET user sharings request - Could not find user with id: %s",
            userId));

    final var pageable = OffsetBasedPageRequest.of(offset, limit);
    final var sharings = sharingService.getSharingsByUserId(userId, pageable);

    final var response =
        sharings.stream()
            .map(ResponseFactory::createSharing)
            .toList();

    final var headers =
        LinkHeaderUtil.createHeadersWithPagination(
            sharings, new Class<?>[]{String.class, Integer.class, Integer.class}, userId, (int) pageable.getOffset(), pageable.getPageSize());

    return ResponseEntity.status(HttpStatus.OK)
        .contentType(MediaType.APPLICATION_JSON)
        .headers(headers)
        .body(response);
  }

  @Override
  public ResponseEntity<Sharing> postUserSharingRevoke(String userId, UUID sharingId) {
    final var sharing =
        throwApiExceptionOnAbsentValue(
            sharingService.getSharingById(sharingId),
            HttpStatus.NOT_FOUND,
            String.format(
                "Failed to revoke sharing - Could not find sharing with id:"
                    + " %s",
                sharingId));

    final var revokedSharing = sharingService.revoke(sharing);

    final var response = ResponseFactory.createSharing(revokedSharing);

    final var headers = LinkHeaderUtil.createHeaders(new Class<?>[]{String.class, UUID.class}, userId, sharingId);

    return ResponseEntity.status(HttpStatus.OK)
        .contentType(MediaType.APPLICATION_JSON)
        .headers(headers)
        .body(response);

  }
}