PortfolioReviewedImportService.java

package com.taxonomy.portfolio.service;

import com.taxonomy.portfolio.dto.PortfolioDtos.CreateRequirementRequest;
import com.taxonomy.portfolio.dto.PortfolioDtos.CreateRequirementVersionRequest;
import com.taxonomy.portfolio.dto.PortfolioDtos.RequirementView;
import com.taxonomy.portfolio.dto.PortfolioImportReviewDtos.ImportDecision;
import com.taxonomy.portfolio.dto.PortfolioImportReviewDtos.PersistedReviewImport;
import com.taxonomy.portfolio.dto.PortfolioImportReviewDtos.ReviewedImportItem;
import com.taxonomy.portfolio.model.PortfolioTypes.Criticality;
import com.taxonomy.portfolio.model.PortfolioTypes.RequirementStatus;
import com.taxonomy.portfolio.model.PortfolioTypes.RequirementType;
import com.taxonomy.portfolio.model.PortfolioTypes.ReviewStatus;
import com.taxonomy.workspace.service.WorkspaceContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/** Applies all reviewed import decisions in one transaction. */
@Service
public class PortfolioReviewedImportService {

    private final ProjectPortfolioService projectService;

    public PortfolioReviewedImportService(ProjectPortfolioService projectService) {
        this.projectService = projectService;
    }

    @Transactional
    public PersistedReviewImport persist(Long projectId,
                                         List<ReviewedImportItem> items,
                                         String username,
                                         WorkspaceContext context) {
        if (items == null || items.isEmpty()) {
            throw PortfolioException.validation("At least one reviewed import item is required");
        }
        projectService.requireProject(projectId, username, context);
        List<RequirementView> created = new ArrayList<>();
        List<RequirementView> versioned = new ArrayList<>();
        Set<String> newKeys = new HashSet<>();
        Set<Long> versionTargets = new HashSet<>();

        for (ReviewedImportItem item : items) {
            if (item == null || item.decision() == null) {
                throw PortfolioException.validation("Every import item requires a decision");
            }
            if (item.decision() == ImportDecision.NEW_REQUIREMENT) {
                String normalizedKey = ProjectPortfolioService.requireText(
                        item.requirementKey(), "requirementKey", 64).toUpperCase(java.util.Locale.ROOT);
                if (!newKeys.add(normalizedKey)) {
                    throw PortfolioException.conflict(
                            "The reviewed import contains duplicate requirement key " + normalizedKey);
                }
                created.add(projectService.createRequirement(
                        projectId,
                        new CreateRequirementRequest(
                                normalizedKey,
                                item.title(),
                                item.text(),
                                RequirementStatus.DRAFT,
                                item.priority(),
                                item.criticality() != null ? item.criticality() : Criticality.MEDIUM,
                                item.requirementType() != null
                                        ? item.requirementType() : RequirementType.FUNCTIONAL,
                                ReviewStatus.PROPOSED,
                                username,
                                "Created from a human-reviewed document candidate",
                                item.source()),
                        username,
                        context));
            } else {
                if (item.targetRequirementId() == null) {
                    throw PortfolioException.validation(
                            "A NEW_VERSION decision requires targetRequirementId");
                }
                if (!versionTargets.add(item.targetRequirementId())) {
                    throw PortfolioException.validation(
                            "Only one reviewed new version per target requirement is allowed in one import");
                }
                projectService.addRequirementVersion(
                        projectId,
                        item.targetRequirementId(),
                        new CreateRequirementVersionRequest(
                                item.text(),
                                "Created from a human-reviewed document candidate",
                                item.source()),
                        username,
                        context);
                versioned.add(projectService.getRequirement(
                        projectId, item.targetRequirementId(), username, context));
            }
        }
        return new PersistedReviewImport(List.copyOf(created), List.copyOf(versioned));
    }
}