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

net.nemerosa.ontrack.boot.ui.BranchController Maven / Gradle / Ivy

There is a newer version: 4.4.5
Show newest version
package net.nemerosa.ontrack.boot.ui;

import com.fasterxml.jackson.databind.JsonNode;
import net.nemerosa.ontrack.extension.api.BuildDiffExtension;
import net.nemerosa.ontrack.extension.api.ExtensionManager;
import net.nemerosa.ontrack.model.Ack;
import net.nemerosa.ontrack.model.buildfilter.BuildFilterProviderData;
import net.nemerosa.ontrack.model.buildfilter.BuildFilterService;
import net.nemerosa.ontrack.model.exceptions.BranchNotTemplateDefinitionException;
import net.nemerosa.ontrack.model.exceptions.BranchNotTemplateInstanceException;
import net.nemerosa.ontrack.model.form.*;
import net.nemerosa.ontrack.model.security.BranchCreate;
import net.nemerosa.ontrack.model.security.SecurityService;
import net.nemerosa.ontrack.model.structure.*;
import net.nemerosa.ontrack.model.support.Action;
import net.nemerosa.ontrack.ui.controller.AbstractResourceController;
import net.nemerosa.ontrack.ui.resource.Link;
import net.nemerosa.ontrack.ui.resource.Resources;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.WebRequest;

import javax.validation.Valid;
import java.util.*;
import java.util.stream.Collectors;

import static net.nemerosa.ontrack.boot.ui.UIUtils.requestParametersToJson;
import static org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.on;

@RestController
@RequestMapping("/structure")
public class BranchController extends AbstractResourceController {

    private final StructureService structureService;
    private final BranchTemplateService branchTemplateService;
    private final TemplateSynchronisationService templateSynchronisationService;
    private final CopyService copyService;
    private final BuildFilterService buildFilterService;
    private final ExtensionManager extensionManager;
    private final SecurityService securityService;

    @Autowired
    public BranchController(
            StructureService structureService,
            BranchTemplateService branchTemplateService,
            TemplateSynchronisationService templateSynchronisationService,
            CopyService copyService,
            BuildFilterService buildFilterService,
            ExtensionManager extensionManager,
            SecurityService securityService) {
        this.structureService = structureService;
        this.branchTemplateService = branchTemplateService;
        this.templateSynchronisationService = templateSynchronisationService;
        this.copyService = copyService;
        this.buildFilterService = buildFilterService;
        this.extensionManager = extensionManager;
        this.securityService = securityService;
    }

    @RequestMapping(value = "projects/{projectId}/branches", method = RequestMethod.GET)
    public Resources getBranchListForProject(@PathVariable ID projectId) {
        return Resources.of(
                structureService.getBranchesForProject(projectId),
                uri(on(BranchController.class).getBranchListForProject(projectId))
        )
                // Create
                .with(
                        Link.CREATE,
                        uri(on(BranchController.class).newBranch(projectId, null)),
                        securityService.isProjectFunctionGranted(projectId.getValue(), BranchCreate.class)
                )
                ;
    }

    @RequestMapping(value = "projects/{projectId}/branches/create", method = RequestMethod.GET)
    public Form newBranchForm(@PathVariable ID projectId) {
        // Checks the project exists
        structureService.getProject(projectId);
        // Returns the form
        return Branch.form();
    }

    @RequestMapping(value = "projects/{projectId}/branches/create", method = RequestMethod.POST)
    public Branch newBranch(@PathVariable ID projectId, @RequestBody @Valid NameDescriptionState nameDescription) {
        // Gets the project
        Project project = structureService.getProject(projectId);
        // Creates a new branch instance
        Branch branch = Branch.of(project, nameDescription);
        // Saves it into the repository
        branch = structureService.newBranch(branch);
        // OK
        return branch;
    }

    @RequestMapping(value = "branches/{branchId}", method = RequestMethod.GET)
    public Branch getBranch(@PathVariable ID branchId) {
        return structureService.getBranch(branchId);
    }

    @RequestMapping(value = "branches/{branchId}", method = RequestMethod.DELETE)
    public Ack deleteBranch(@PathVariable ID branchId) {
        return structureService.deleteBranch(branchId);
    }

    @RequestMapping(value = "branches/{branchId}/update", method = RequestMethod.GET)
    public Form getUpdateForm(@PathVariable ID branchId) {
        // Loads the branch and gets a form
        return structureService.getBranch(branchId).toForm();
    }

    @RequestMapping(value = "branches/{branchId}/update", method = RequestMethod.PUT)
    public Branch updateBranch(@PathVariable ID branchId, @RequestBody @Valid NameDescriptionState form) {
        // Loads and updates branch
        Branch branch = structureService.getBranch(branchId).update(form);
        // Saves the branch
        structureService.saveBranch(branch);
        // OK
        return branch;
    }

    @RequestMapping(value = "branches/{branchId}/enable", method = RequestMethod.PUT)
    public Branch enableBranch(@PathVariable ID branchId) {
        // Loads and updates branch
        Branch branch = structureService.getBranch(branchId).withDisabled(false);
        // Saves the branch
        structureService.saveBranch(branch);
        // OK
        return branch;
    }

    @RequestMapping(value = "branches/{branchId}/disable", method = RequestMethod.PUT)
    public Branch disableBranch(@PathVariable ID branchId) {
        // Loads and updates branch
        Branch branch = structureService.getBranch(branchId).withDisabled(true);
        // Saves the branch
        structureService.saveBranch(branch);
        // OK
        return branch;
    }

    @RequestMapping(value = "branches/{branchId}/status", method = RequestMethod.GET)
    public BranchStatusView getBranchStatusView(@PathVariable ID branchId) {
        return structureService.getBranchStatusView(structureService.getBranch(branchId));
    }

    @RequestMapping(value = "branches/{branchId}/view", method = RequestMethod.GET)
    public BranchBuildView buildView(@PathVariable ID branchId) {
        return buildViewWithFilter(
                branchId,
                buildFilterService.defaultFilterProviderData()
        );

    }

    @RequestMapping(value = "branches/{branchId}/view/{filterType:.*}", method = RequestMethod.GET)
    public  BranchBuildView buildViewWithFilter(@PathVariable ID branchId, @PathVariable String filterType, WebRequest request) {
        JsonNode jsonParameters = requestParametersToJson(request);
        // Gets the filter provider
        BuildFilterProviderData buildFilterProvider = buildFilterService.getBuildFilterProviderData(filterType, jsonParameters);
        // Gets the build view
        return buildViewWithFilter(
                branchId,
                buildFilterProvider
        );
    }

    /**
     * Gets the form to copy a source branch into this branch
     */
    @RequestMapping(value = "branches/{branchId}/copy", method = RequestMethod.GET)
    public Form copy(@PathVariable ID branchId) {
        return Form.create()
                .with(
                        Selection.of("sourceBranch")
                                .label("Source branch")
                                .help("Branch to copy configuration from")
                                // All branches for all projects
                                .items(structureService.getProjectList().stream()
                                        .flatMap(project -> structureService.getBranchesForProject(project.getId()).stream())
                                        // Keeps only the different branches
                                        .filter(branch -> !branchId.equals(branch.getId()))
                                        .collect(Collectors.toList()))
                )
                .with(
                        Replacements.of("replacements")
                                .label("Replacements")
                )
                ;
    }

    /**
     * Copies the configuration from a branch into this one.
     */
    @RequestMapping(value = "branches/{branchId}/copy", method = RequestMethod.PUT)
    public Branch copy(@PathVariable ID branchId, @RequestBody BranchCopyRequest request) {
        // Gets the branch
        Branch branch = structureService.getBranch(branchId);
        // Performs the copy
        return copyService.copy(branch, request);
    }

    /**
     * Gets the form for a bulk update of the branch
     */
    @RequestMapping(value = "branches/{branchId}/update/bulk", method = RequestMethod.GET)
    public Form bulkUpdate(@SuppressWarnings("UnusedParameters") @PathVariable ID branchId) {
        return Form.create()
                .with(
                        Replacements.of("replacements")
                                .label("Replacements")
                )
                ;
    }

    /**
     * Bulk update for a branch.
     */
    @RequestMapping(value = "branches/{branchId}/update/bulk", method = RequestMethod.PUT)
    public Branch bulkUpdate(@PathVariable ID branchId, @RequestBody BranchBulkUpdateRequest request) {
        // Gets the branch
        Branch branch = structureService.getBranch(branchId);
        // Performs the update
        return copyService.update(branch, request);
    }

    /**
     * Gets the form to clone this branch into another branch
     */
    @RequestMapping(value = "branches/{branchId}/clone", method = RequestMethod.GET)
    public Form clone(@SuppressWarnings("UnusedParameters") @PathVariable ID branchId) {
        return Form.create()
                .with(
                        Text.of("name")
                                .label("Target branch")
                                .help("Name of the branch to create")
                )
                .with(
                        Replacements.of("replacements")
                                .label("Replacements")
                )
                ;
    }

    /**
     * Clones this branch into another one.
     */
    @RequestMapping(value = "branches/{branchId}/clone", method = RequestMethod.POST)
    public Branch clone(@PathVariable ID branchId, @RequestBody BranchCloneRequest request) {
        // Gets the branch
        Branch branch = structureService.getBranch(branchId);
        // Performs the clone
        return copyService.cloneBranch(branch, request);
    }

    /**
     * Gets a form to make this branch a template definition.
     */
    @RequestMapping(value = "branches/{branchId}/template/definition", method = RequestMethod.GET)
    public Form getTemplateDefinition(@PathVariable ID branchId) {
        Branch branch = getBranch(branchId);
        Optional templateDefinition = branchTemplateService.getTemplateDefinition(branchId);
        return Form.create()
                .with(
                        MultiForm.of(
                                "parameters",
                                Form.create()
                                        .with(
                                                Text.of("name").label("Name").help("Parameter name")
                                        )
                                        .with(
                                                Text.of("description").optional().label("Description").help("Parameter description")
                                        )
                                        .with(
                                                Text.of("expression")
                                                        .label("Expression")
                                                        .optional()
                                                        .help(
                                                                "Those expressions are defined for the synchronisation between " +
                                                                        "template definitions and template instances. They bind " +
                                                                        "a parameter name and a source name to an actual parameter " +
                                                                        "value. " +
                                                                        "A template expression is a string that contains " +
                                                                        "references to the source name using the ${...} construct " +
                                                                        "where the content is a Groovy expression where the " +
                                                                        "`sourceName` variable is bound to the source name.")
                                        )
                        )
                                .label("Parameters")
                                .help("List of parameters that define the template")
                                .value(templateDefinition
                                        .map(TemplateDefinition::getParameters)
                                        .orElse(Collections.emptyList()))
                )
                .with(
                        ServiceConfigurator.of("synchronisationSourceConfig")
                                .label("Sync. source")
                                .optional()
                                .help("Source names when synchronising")
                                .sources(
                                        templateSynchronisationService.getSynchronisationSources().stream()
                                                .filter(source -> source.isApplicable(branch))
                                                .map(
                                                        source -> new ServiceConfigurationSource(
                                                                source.getId(),
                                                                source.getName(),
                                                                source.getForm(branch)
                                                        )
                                                )
                                                .collect(Collectors.toList())
                                )
                                .value(
                                        templateDefinition
                                                .map(TemplateDefinition::getSynchronisationSourceConfig)
                                                .orElse(null)
                                )
                )
                .with(
                        Int.of("interval")
                                .label("Interval")
                                .help("Interval between each synchronisation in minutes. If set to zero, " +
                                        "no automated synchronisation is performed and it must be done " +
                                        "manually.")
                                .min(0)
                                .value(templateDefinition.map(TemplateDefinition::getInterval).orElse(0))
                )
                .with(
                        Selection.of("absencePolicy")
                                .label("Absence policy")
                                .help("Defines what to do with a branch template instance when the corresponding " +
                                        "name is not defined any longer.")
                                .items(Arrays.stream(TemplateSynchronisationAbsencePolicy.values())
                                        .map(Describable::toDescription)
                                        .collect(Collectors.toList()))
                                .itemId("id")
                                .itemName("name")
                                .value(
                                        templateDefinition
                                                .map(td -> td.getAbsencePolicy().getId())
                                                .orElse(TemplateSynchronisationAbsencePolicy.DISABLE.getId())
                                )
                )
                ;
    }

    /**
     * Sets this branch as a template definition, or updates the definition.
     */
    @RequestMapping(value = "branches/{branchId}/template/definition", method = RequestMethod.PUT)
    public Branch setTemplateDefinition(@PathVariable ID branchId, @RequestBody @Valid TemplateDefinition templateDefinition) {
        return branchTemplateService.setTemplateDefinition(branchId, templateDefinition);
    }

    /**
     * Sync. this template definition by creating and updating linked template instances.
     */
    @RequestMapping(value = "branches/{branchId}/template/sync", method = RequestMethod.POST)
    public BranchTemplateSyncResults syncTemplateDefinition(@PathVariable ID branchId) {
        return branchTemplateService.sync(branchId);
    }

    /**
     * Sync. this template instance against its template definition
     */
    @RequestMapping(value = "branches/{branchId}/sync", method = RequestMethod.POST)
    public Ack syncTemplateInstance(@PathVariable ID branchId) {
        return branchTemplateService.syncInstance(branchId);
    }

    /**
     * Gets the form to create a template instance using a name.
     */
    @RequestMapping(value = "branches/{branchId}/template", method = RequestMethod.GET)
    public Form singleTemplateInstanceForm(@PathVariable ID branchId) {
        // Gets the template definition for this branch
        Optional templateDefinition = branchTemplateService.getTemplateDefinition(branchId);
        if (!templateDefinition.isPresent()) {
            throw new BranchNotTemplateDefinitionException(branchId);
        }
        // Creates a form with the source name and all needed parameters
        Form form = Form.create().with(
                Form.defaultNameField()
                        .label("Source name")
                        .help("Name used to create the branch from.")
        );
        form.append(templateDefinition.get().getForm());
        // OK
        return form;
    }

    /**
     * Creates a branch template instance for one name.
     * 

*

    *
  • If the target branch does not exist, creates it.
  • *
  • If the target branch exists: *
      *
    • If it is linked to the same definition, updates it.
    • *
    • If it is linked to another definition, this is an error.
    • *
    • If it is a normal branch, this is an error.
    • *
    *
  • *
* * @param branchId ID of the branch template definition * @param request Name to use when creating the branch * @return Created or updated branch */ @RequestMapping(value = "branches/{branchId}/template", method = RequestMethod.PUT) public Branch createTemplateInstance(@PathVariable ID branchId, @RequestBody @Valid BranchTemplateInstanceSingleRequest request) { return branchTemplateService.createTemplateInstance(branchId, request); } /** * Gets the template instance for a branch */ @RequestMapping(value = "branches/{branchId}/template/instance", method = RequestMethod.GET) public TemplateInstance getTemplateInstance(@PathVariable ID branchId) { return branchTemplateService.getTemplateInstance(branchId) .orElseThrow(() -> new BranchNotTemplateInstanceException(branchId)); } /** * Disconnects the branch from any template definition, if any. */ @RequestMapping(value = "branches/{branchId}/template/instance", method = RequestMethod.DELETE) public Branch disconnectTemplateInstance(@PathVariable ID branchId) { return branchTemplateService.disconnectTemplateInstance(branchId); } /** * Gets the form to connect a branch to a template. */ @RequestMapping(value = "branches/{branchId}/template/connect", method = RequestMethod.GET) public Form connectTemplateInstance(@PathVariable ID branchId) { // Loads the branch Branch branch = structureService.getBranch(branchId); // List of templates Collection templateDefinitions = branchTemplateService.getTemplateDefinitions(branch.getProject()); // List of sync form per template return Form.create() .with( ServiceConfigurator.of("connectionRequest") .label("Template") .help("Template to connect to") .sources( templateDefinitions.stream() .map(lbtd -> new ServiceConfigurationSource( lbtd.getBranch().getId().toString(), lbtd.getBranch().getName(), lbtd.getForm() )) .collect(Collectors.toList()) ) ); } /** * Tries to connect an existing branch to a template * * @param branchId Branch to connect */ @RequestMapping(value = "branches/{branchId}/template/connect", method = RequestMethod.POST) public Branch connectTemplateInstance(@PathVariable ID branchId, @RequestBody BranchTemplateInstanceConnectRequest request) { return branchTemplateService.connectTemplateInstance(branchId, request); } private BranchBuildView buildViewWithFilter(ID branchId, BuildFilterProviderData buildFilterProviderData) { // Gets the branch Branch branch = getBranch(branchId); // Gets the list of builds List builds = buildFilterProviderData.filterBranchBuilds(branch); // Gets the list of build diff actions List buildDiffActions = extensionManager.getExtensions(BuildDiffExtension.class) .stream() .filter(extension -> extension.apply(branch.getProject())) .map(this::resolveExtensionAction) .collect(Collectors.toList()); // Gets the views for each build return new BranchBuildView( builds.stream() .map(build -> structureService.getBuildView(build, true)) .collect(Collectors.toList()), buildDiffActions ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy