OrganizationsController.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.bind.annotation.CrossOrigin;
import org.springframework.web.context.request.NativeWebRequest;
import se.jobtechdev.personaldatagateway.api.generated.api.OrganizationsApi;
import se.jobtechdev.personaldatagateway.api.generated.model.Organization;
import se.jobtechdev.personaldatagateway.api.generated.model.OrganizationFaq;
import se.jobtechdev.personaldatagateway.api.generated.model.OrganizationFaqPatch;
import se.jobtechdev.personaldatagateway.api.generated.model.OrganizationPatch;
import se.jobtechdev.personaldatagateway.api.service.FaqService;
import se.jobtechdev.personaldatagateway.api.service.OrganizationService;
import se.jobtechdev.personaldatagateway.api.util.ControllerUtil;
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.UUID;

import static se.jobtechdev.personaldatagateway.api.util.ControllerUtil.*;
import static se.jobtechdev.personaldatagateway.api.util.ResponseFactory.createOrganization;

@Controller
public class OrganizationsController implements OrganizationsApi {
  public static final String DELETED = "deleted";
  private final OrganizationService organizationService;

  private final FaqService faqService;

  private final NativeWebRequest request;

  private final String baseUrl;

  @Autowired
  public OrganizationsController(
      @Value("${pdg-api.base-url}") String baseUrl,
      NativeWebRequest request,
      OrganizationService organizationService,
      FaqService faqService) {
    this.baseUrl = baseUrl;
    this.request = request;
    this.organizationService = organizationService;
    this.faqService = faqService;
  }

  @Override
  public ResponseEntity<Organization> postOrganization(Organization organization) {
    final var createdOrganization =
        throwApiExceptionOnAbsentValue(
            organizationService.createOrganization(
                organization.getOrgNr(), organization.getName()),
            HttpStatus.BAD_REQUEST,
            "Failed to handle POST organization request - Attempt to create organization"
                + " resulted in an empty result");

    final var response = createOrganization(createdOrganization);

    final var headers =
        LinkHeaderUtil.createHeadersWithLocation(
            createdOrganization.getId().toString(),
            new Class<?>[]{Organization.class},
            organization);

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

  @Override
  @CrossOrigin(origins = "*")
  public ResponseEntity<List<Organization>> getOrganizations(Integer offset, Integer limit) {
    final var pageable = OffsetBasedPageRequest.of(offset, limit);
    final var organizations = organizationService.getOrganizations(pageable);
    final var response =
        organizations.stream()
            .map(
                ResponseFactory::createOrganization)
            .toList();

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

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

  @Override
  @CrossOrigin(origins = "*")
  public ResponseEntity<Organization> getOrganization(UUID organizationId) {
    final var organization =
        throwApiExceptionOnAbsentValue(
            organizationService.getOrganizationById(organizationId),
            HttpStatus.NOT_FOUND,
            String.format(
                "Failed to handle GET organization request - Could not find organization with"
                    + " id: %s",
                organizationId));

    final var response = createOrganization(organization);

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

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

  @Override
  public ResponseEntity<Organization> patchOrganization(
      UUID organizationId, OrganizationPatch organizationPatch) {
    final var organization =
        throwApiExceptionOnAbsentValue(
            organizationService.getOrganizationById(organizationId),
            HttpStatus.NOT_FOUND,
            String.format(
                "Failed to handle PATCH organization request - Could not find organization with"
                    + " id: %s",
                organizationId));

    final var assigned =
        ControllerUtil.assignNonNull(
            organizationPatch, organization, List.of("name", DELETED), List.of(DELETED));

    final var updated = organizationService.update(assigned);

    final var response = ResponseFactory.createOrganization(updated);

    final var headers =
        LinkHeaderUtil.createHeaders(
            new Class<?>[]{UUID.class, OrganizationPatch.class},
            organizationId,
            organizationPatch);

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

  @Override
  public ResponseEntity<OrganizationFaq> postOrganizationFaq(
      UUID organizationId, OrganizationFaq organizationFaq) {
    final var organization =
        throwApiExceptionOnAbsentValue(
            organizationService.getOrganizationById(organizationId),
            HttpStatus.NOT_FOUND,
            String.format(
                "Failed to handle POST organization faq request - Could not find organization"
                    + " with id: %s",
                organizationId));

    final var faqs = organization.getFaqEntities();

    final var faq =
        faqService.createFaq(
            organizationFaq.getLang(),
            organizationFaq.getTitle(),
            organizationFaq.getContent());
    faqs.add(faq);
    organizationService.save(organization);

    final var response =
        ResponseFactory.createOrganizationFaq(organizationId, faq);

    final var headers =
        LinkHeaderUtil.createHeadersWithLocation(
            faq.getId().toString(),
            new Class<?>[]{UUID.class, OrganizationFaq.class},
            organizationId,
            organizationFaq);

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

  @Override
  @CrossOrigin(origins = "*")
  public ResponseEntity<List<OrganizationFaq>> getOrganizationFaqs(
      UUID organizationId, String acceptLanguage, Integer offset, Integer limit) {
    throwApiExceptionOnAbsentValue(
        organizationService.getOrganizationById(organizationId),
        HttpStatus.NOT_FOUND,
        String.format(
            "Failed to handle GET organization faqs request - Could not find organization"
                + " with id: %s",
            organizationId));

    final var pageable = OffsetBasedPageRequest.of(offset, limit);
    final var faqs = faqService.getOrganizationFaqs(organizationId, acceptLanguage, pageable);

    final var response =
        faqs.stream()
            .map(
                faq ->
                    ResponseFactory.createOrganizationFaq(organizationId, faq))
            .toList();

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

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

  @Override
  @CrossOrigin(origins = "*")
  public ResponseEntity<OrganizationFaq> getOrganizationFaq(
      UUID organizationId, UUID faqId) {
    final var faq =
        throwApiExceptionOnAbsentValue(
            faqService.getFaqById(faqId),
            HttpStatus.NOT_FOUND,
            String.format(
                "Failed to handle GET organization faq request - Could not find faq with id:"
                    + " %s",
                faqId));

    final var organization =
        throwApiExceptionOnAbsentValue(
            organizationService.getOrganizationById(organizationId),
            HttpStatus.NOT_FOUND,
            String.format(
                "Failed to handle GET organization faq request - Could not find organization with id:"
                    + " %s",
                organizationId));

    earlyExit(
        organization,
        notAmong(faq.getOrganizationEntities()),
        HttpStatus.NOT_FOUND,
        String.format(
            "Failed to handle GET organization faq request - Could not find faq with id: %s for organization with id: %s",
            faqId, organizationId));

    final var response =
        ResponseFactory.createOrganizationFaq(organizationId, faq);

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

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

  @Override
  public ResponseEntity<OrganizationFaq> patchOrganizationFaq(
      UUID organizationId, UUID faqId, OrganizationFaqPatch organizationFaqPatch) {

    final var faq =
        throwApiExceptionOnAbsentValue(
            faqService.getFaqById(faqId),
            HttpStatus.NOT_FOUND,
            String.format(
                "Failed to handle PATCH organization faq request - Could not find faq with id:"
                    + " %s",
                faqId));

    final var patchedFaq = assignNonNull(organizationFaqPatch, faq, List.of(DELETED));

    final var updated = faqService.update(patchedFaq);

    final var response = ResponseFactory.createOrganizationFaq(organizationId, updated);

    final var headers =
        LinkHeaderUtil.createHeaders(
            new Class<?>[]{UUID.class, UUID.class, OrganizationFaqPatch.class},
            organizationId,
            faqId,
            organizationFaqPatch);

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