All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pro.taskana.classification.rest.ClassificationDefinitionController Maven / Gradle / Ivy

The newest version!
package pro.taskana.classification.rest;

import static pro.taskana.common.internal.util.CheckedFunction.wrap;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import pro.taskana.classification.api.ClassificationCustomField;
import pro.taskana.classification.api.ClassificationQuery;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.exceptions.ClassificationAlreadyExistException;
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
import pro.taskana.classification.api.exceptions.MalformedServiceLevelException;
import pro.taskana.classification.api.models.Classification;
import pro.taskana.classification.api.models.ClassificationSummary;
import pro.taskana.classification.rest.assembler.ClassificationDefinitionCollectionRepresentationModel;
import pro.taskana.classification.rest.assembler.ClassificationDefinitionRepresentationModelAssembler;
import pro.taskana.classification.rest.models.ClassificationDefinitionRepresentationModel;
import pro.taskana.classification.rest.models.ClassificationRepresentationModel;
import pro.taskana.common.api.exceptions.ConcurrencyException;
import pro.taskana.common.api.exceptions.DomainNotFoundException;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.common.rest.RestEndpoints;

/** Controller for Importing / Exporting classifications. */
@RestController
@EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
public class ClassificationDefinitionController {

  private final ObjectMapper mapper;
  private final ClassificationService classificationService;
  private final ClassificationDefinitionRepresentationModelAssembler assembler;

  @Autowired
  ClassificationDefinitionController(
      ObjectMapper mapper,
      ClassificationService classificationService,
      ClassificationDefinitionRepresentationModelAssembler assembler) {
    this.mapper = mapper;
    this.classificationService = classificationService;
    this.assembler = assembler;
  }

  @Operation(
      summary = "Export Classifications",
      description = "This endpoint exports all configured Classifications.",
      parameters = {@Parameter(name = "domain", description = "Filter the export by domain")},
      responses = {
        @ApiResponse(
            responseCode = "200",
            description = "the configured Classifications.",
            content =
                @Content(
                    mediaType = MediaTypes.HAL_JSON_VALUE,
                    schema =
                        @Schema(
                            implementation =
                                ClassificationDefinitionCollectionRepresentationModel.class)))
      })
  @GetMapping(path = RestEndpoints.URL_CLASSIFICATION_DEFINITIONS)
  @Transactional(readOnly = true, rollbackFor = Exception.class)
  public ResponseEntity
      exportClassifications(@RequestParam(value = "domain", required = false) String[] domain) {
    ClassificationQuery query = classificationService.createClassificationQuery();

    List summaries =
        domain != null ? query.domainIn(domain).list() : query.list();

    ClassificationDefinitionCollectionRepresentationModel collectionModel =
        summaries.stream()
            .map(ClassificationSummary::getId)
            .map(wrap(classificationService::getClassification))
            .collect(
                Collectors.collectingAndThen(
                    Collectors.toList(), assembler::toTaskanaCollectionModel));

    return ResponseEntity.ok(collectionModel);
  }

  @Operation(
      summary = "Import Classifications",
      description =
          "This endpoint imports all Classifications. Existing Classifications will not be removed."
              + " Existing Classifications with the same key/domain will be overridden.",
      requestBody =
          @io.swagger.v3.oas.annotations.parameters.RequestBody(
              description =
                  "the file containing the Classifications which should be imported. To get an "
                      + "example file containing the Classificatioins, go to the "
                      + "[TASKANA UI](http://localhost:8080/taskana/index.html) and export the "
                      + "Classifications",
              required = true,
              content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA_VALUE)),
      responses = {
        @ApiResponse(
            responseCode = "204",
            content = {@Content(schema = @Schema())})
      })
  @PostMapping(
      path = RestEndpoints.URL_CLASSIFICATION_DEFINITIONS,
      consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
  @Transactional(rollbackFor = Exception.class)
  public ResponseEntity importClassifications(@RequestParam("file") MultipartFile file)
      throws InvalidArgumentException,
          ConcurrencyException,
          ClassificationNotFoundException,
          ClassificationAlreadyExistException,
          DomainNotFoundException,
          IOException,
          MalformedServiceLevelException,
          NotAuthorizedException {
    Map systemIds = getSystemIds();
    ClassificationDefinitionCollectionRepresentationModel collection =
        extractClassificationResourcesFromFile(file);
    checkForDuplicates(collection.getContent());

    Map childrenInFile =
        mapChildrenToParentKeys(collection.getContent(), systemIds);
    insertOrUpdateClassificationsWithoutParent(collection.getContent(), systemIds);
    updateParentChildrenRelations(childrenInFile);
    return ResponseEntity.noContent().build();
  }

  private Map getSystemIds() {
    return classificationService.createClassificationQuery().list().stream()
        .collect(
            Collectors.toMap(i -> i.getKey() + "|" + i.getDomain(), ClassificationSummary::getId));
  }

  private ClassificationDefinitionCollectionRepresentationModel
      extractClassificationResourcesFromFile(MultipartFile file) throws IOException {
    return mapper.readValue(
        file.getInputStream(), ClassificationDefinitionCollectionRepresentationModel.class);
  }

  private void checkForDuplicates(
      Collection definitionList)
      throws ClassificationAlreadyExistException {
    List identifiers = new ArrayList<>();
    for (ClassificationDefinitionRepresentationModel definition : definitionList) {
      ClassificationRepresentationModel classification = definition.getClassification();
      String identifier = classification.getKey() + "|" + classification.getDomain();
      if (identifiers.contains(identifier)) {
        throw new ClassificationAlreadyExistException(
            definition.getClassification().getKey(), definition.getClassification().getDomain());
      }
      identifiers.add(identifier);
    }
  }

  private Map mapChildrenToParentKeys(
      Collection definitionList,
      Map systemIds) {
    Map childrenInFile = new HashMap<>();
    Set newKeysWithDomain = new HashSet<>();
    definitionList.stream()
        .map(ClassificationDefinitionRepresentationModel::getClassification)
        .forEach(cl -> newKeysWithDomain.add(cl.getKey() + "|" + cl.getDomain()));

    for (ClassificationDefinitionRepresentationModel def : definitionList) {
      ClassificationRepresentationModel cl = def.getClassification();
      cl.setParentId(cl.getParentId() == null ? "" : cl.getParentId());
      cl.setParentKey(cl.getParentKey() == null ? "" : cl.getParentKey());

      if (!cl.getParentId().equals("") && cl.getParentKey().equals("")) {
        for (ClassificationDefinitionRepresentationModel parentDef : definitionList) {
          ClassificationRepresentationModel parent = parentDef.getClassification();
          if (cl.getParentId().equals(parent.getClassificationId())) {
            cl.setParentKey(parent.getKey());
          }
        }
      }

      String parentKeyAndDomain = cl.getParentKey() + "|" + cl.getDomain();
      if ((!cl.getParentKey().isEmpty()
          && !cl.getParentKey().equals("")
          && (newKeysWithDomain.contains(parentKeyAndDomain)
              || systemIds.containsKey(parentKeyAndDomain)))) {
        childrenInFile.put(assembler.toEntityModel(def), cl.getParentKey());
      }
    }
    return childrenInFile;
  }

  private void insertOrUpdateClassificationsWithoutParent(
      Collection definitionList,
      Map systemIds)
      throws ClassificationNotFoundException,
          InvalidArgumentException,
          ClassificationAlreadyExistException,
          DomainNotFoundException,
          ConcurrencyException,
          MalformedServiceLevelException,
          NotAuthorizedException {
    for (ClassificationDefinitionRepresentationModel definition : definitionList) {
      ClassificationRepresentationModel classificationRepModel = definition.getClassification();
      classificationRepModel.setParentKey(null);
      classificationRepModel.setParentId(null);
      classificationRepModel.setClassificationId(null);

      Classification newClassification = assembler.toEntityModel(definition);

      String systemId =
          systemIds.get(classificationRepModel.getKey() + "|" + classificationRepModel.getDomain());
      if (systemId != null) {
        updateExistingClassification(newClassification, systemId);
      } else {
        classificationService.createClassification(newClassification);
      }
    }
  }

  private void updateParentChildrenRelations(Map childrenInFile)
      throws ClassificationNotFoundException,
          ConcurrencyException,
          InvalidArgumentException,
          MalformedServiceLevelException,
          NotAuthorizedException {
    for (Map.Entry entry : childrenInFile.entrySet()) {
      Classification childRes = entry.getKey();
      String parentKey = entry.getValue();
      String classificationKey = childRes.getKey();
      String classificationDomain = childRes.getDomain();

      Classification child =
          classificationService.getClassification(classificationKey, classificationDomain);
      String parentId =
          (parentKey == null)
              ? ""
              : classificationService.getClassification(parentKey, classificationDomain).getId();

      child.setParentKey(parentKey);
      child.setParentId(parentId);

      classificationService.updateClassification(child);
    }
  }

  private void updateExistingClassification(Classification newClassification, String systemId)
      throws ClassificationNotFoundException,
          ConcurrencyException,
          InvalidArgumentException,
          MalformedServiceLevelException,
          NotAuthorizedException {
    Classification currentClassification = classificationService.getClassification(systemId);
    if (newClassification.getType() != null
        && !newClassification.getType().equals(currentClassification.getType())) {
      throw new InvalidArgumentException("Can not change the type of a classification.");
    }
    currentClassification.setCategory(newClassification.getCategory());
    currentClassification.setIsValidInDomain(newClassification.getIsValidInDomain());
    currentClassification.setName(newClassification.getName());
    currentClassification.setParentId(newClassification.getParentId());
    currentClassification.setParentKey(newClassification.getParentKey());
    currentClassification.setDescription(newClassification.getDescription());
    currentClassification.setPriority(newClassification.getPriority());
    currentClassification.setServiceLevel(newClassification.getServiceLevel());
    currentClassification.setApplicationEntryPoint(newClassification.getApplicationEntryPoint());
    currentClassification.setCustomField(
        ClassificationCustomField.CUSTOM_1,
        newClassification.getCustomField(ClassificationCustomField.CUSTOM_1));
    currentClassification.setCustomField(
        ClassificationCustomField.CUSTOM_2,
        newClassification.getCustomField(ClassificationCustomField.CUSTOM_2));
    currentClassification.setCustomField(
        ClassificationCustomField.CUSTOM_3,
        newClassification.getCustomField(ClassificationCustomField.CUSTOM_3));
    currentClassification.setCustomField(
        ClassificationCustomField.CUSTOM_4,
        newClassification.getCustomField(ClassificationCustomField.CUSTOM_4));
    currentClassification.setCustomField(
        ClassificationCustomField.CUSTOM_5,
        newClassification.getCustomField(ClassificationCustomField.CUSTOM_5));
    currentClassification.setCustomField(
        ClassificationCustomField.CUSTOM_6,
        newClassification.getCustomField(ClassificationCustomField.CUSTOM_6));
    currentClassification.setCustomField(
        ClassificationCustomField.CUSTOM_7,
        newClassification.getCustomField(ClassificationCustomField.CUSTOM_7));
    currentClassification.setCustomField(
        ClassificationCustomField.CUSTOM_8,
        newClassification.getCustomField(ClassificationCustomField.CUSTOM_8));
    classificationService.updateClassification(currentClassification);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy