portainer/portainer

View on GitHub
api/http/handler/gitops/git_repo_file_preview.go

Summary

Maintainability
A
40 mins
Test Coverage
package gitops

import (
    "errors"
    "fmt"
    "net/http"

    gittypes "github.com/portainer/portainer/api/git/types"
    httperror "github.com/portainer/portainer/pkg/libhttp/error"
    "github.com/portainer/portainer/pkg/libhttp/request"
    "github.com/portainer/portainer/pkg/libhttp/response"

    "github.com/asaskevich/govalidator"
)

type fileResponse struct {
    FileContent string
}

type repositoryFilePreviewPayload struct {
    Repository string `json:"repository" example:"https://github.com/openfaas/faas" validate:"required"`
    Reference  string `json:"reference" example:"refs/heads/master"`
    Username   string `json:"username" example:"myGitUsername"`
    Password   string `json:"password" example:"myGitPassword"`
    // Path to file whose content will be read
    TargetFile string `json:"targetFile" example:"docker-compose.yml"`
    // TLSSkipVerify skips SSL verification when cloning the Git repository
    TLSSkipVerify bool `example:"false"`
}

func (payload *repositoryFilePreviewPayload) Validate(r *http.Request) error {
    if govalidator.IsNull(payload.Repository) || !govalidator.IsURL(payload.Repository) {
        return errors.New("invalid repository URL. Must correspond to a valid URL format")
    }

    if govalidator.IsNull(payload.Reference) {
        payload.Reference = "refs/heads/main"
    }

    if govalidator.IsNull(payload.TargetFile) {
        return errors.New("invalid target filename")
    }

    return nil
}

// @id GitOperationRepoFilePreview
// @summary preview the content of target file in the git repository
// @description Retrieve the compose file content based on git repository configuration
// @description **Access policy**: authenticated
// @tags gitops
// @security ApiKeyAuth
// @security jwt
// @produce json
// @param body body repositoryFilePreviewPayload true "Template details"
// @success 200 {object} fileResponse "Success"
// @failure 400 "Invalid request"
// @failure 500 "Server error"
// @router /gitops/repo/file/preview [post]
func (handler *Handler) gitOperationRepoFilePreview(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
    var payload repositoryFilePreviewPayload
    err := request.DecodeAndValidateJSONPayload(r, &payload)
    if err != nil {
        return httperror.BadRequest("Invalid request payload", err)
    }

    projectPath, err := handler.fileService.GetTemporaryPath()
    if err != nil {
        return httperror.InternalServerError("Unable to create temporary folder", err)
    }

    err = handler.gitService.CloneRepository(projectPath, payload.Repository, payload.Reference, payload.Username, payload.Password, payload.TLSSkipVerify)
    if err != nil {
        if errors.Is(err, gittypes.ErrAuthenticationFailure) {
            return httperror.BadRequest("Invalid git credential", err)
        }

        newErr := fmt.Errorf("unable to clone git repository, error: %w", err)
        return httperror.InternalServerError(newErr.Error(), newErr)
    }

    defer handler.fileService.RemoveDirectory(projectPath)

    fileContent, err := handler.fileService.GetFileContent(projectPath, payload.TargetFile)
    if err != nil {
        return httperror.InternalServerError("Unable to retrieve custom template file from disk", err)
    }

    return response.JSON(w, &fileResponse{FileContent: string(fileContent)})
}