cloudfoundry/cf-k8s-controllers

View on GitHub
api/handlers/build.go

Summary

Maintainability
A
0 mins
Test Coverage
package handlers

import (
    "context"
    "errors"
    "net/http"
    "net/url"

    "code.cloudfoundry.org/korifi/api/authorization"
    apierrors "code.cloudfoundry.org/korifi/api/errors"
    "code.cloudfoundry.org/korifi/api/payloads"
    "code.cloudfoundry.org/korifi/api/presenter"
    "code.cloudfoundry.org/korifi/api/repositories"
    "code.cloudfoundry.org/korifi/api/routing"

    "github.com/go-logr/logr"
)

const (
    BuildPath  = "/v3/builds/{guid}"
    BuildsPath = "/v3/builds"
)

//counterfeiter:generate -o fake -fake-name CFBuildRepository . CFBuildRepository
type CFBuildRepository interface {
    GetBuild(context.Context, authorization.Info, string) (repositories.BuildRecord, error)
    CreateBuild(context.Context, authorization.Info, repositories.CreateBuildMessage) (repositories.BuildRecord, error)
}

type Build struct {
    serverURL        url.URL
    buildRepo        CFBuildRepository
    packageRepo      CFPackageRepository
    appRepo          CFAppRepository
    requestValidator RequestValidator
}

func NewBuild(
    serverURL url.URL,
    buildRepo CFBuildRepository,
    packageRepo CFPackageRepository,
    appRepo CFAppRepository,
    requestValidator RequestValidator,
) *Build {
    return &Build{
        serverURL:        serverURL,
        buildRepo:        buildRepo,
        packageRepo:      packageRepo,
        appRepo:          appRepo,
        requestValidator: requestValidator,
    }
}

func (h *Build) get(r *http.Request) (*routing.Response, error) {
    buildGUID := routing.URLParam(r, "guid")
    authInfo, _ := authorization.InfoFromContext(r.Context())
    logger := logr.FromContextOrDiscard(r.Context()).WithName("handlers.build.get")

    build, err := h.buildRepo.GetBuild(r.Context(), authInfo, buildGUID)
    if err != nil {
        return nil, apierrors.LogAndReturn(logger, apierrors.ForbiddenAsNotFound(err), "failed to fetch "+repositories.BuildResourceType, "guid", buildGUID)
    }

    return routing.NewResponse(http.StatusOK).WithBody(presenter.ForBuild(build, h.serverURL)), nil
}

func (h *Build) create(r *http.Request) (*routing.Response, error) {
    authInfo, _ := authorization.InfoFromContext(r.Context())
    logger := logr.FromContextOrDiscard(r.Context()).WithName("handlers.build.create")

    var payload payloads.BuildCreate
    if err := h.requestValidator.DecodeAndValidateJSONPayload(r, &payload); err != nil {
        return nil, apierrors.LogAndReturn(logger, err, "failed to decode payload")
    }

    packageRecord, err := h.packageRepo.GetPackage(r.Context(), authInfo, payload.Package.GUID)
    if err != nil {
        return nil, apierrors.LogAndReturn(
            logger,
            apierrors.AsUnprocessableEntity(err,
                "Unable to use package. Ensure that the package exists and you have access to it.",
                apierrors.ForbiddenError{},
                apierrors.NotFoundError{},
            ),
            "Error finding Package", "Package GUID", payload.Package.GUID,
        )
    }

    appRecord, err := h.appRepo.GetApp(r.Context(), authInfo, packageRecord.AppGUID)
    if err != nil {
        return nil, apierrors.LogAndReturn(
            logger,
            apierrors.AsUnprocessableEntity(err,
                "Unable to use the app associated with that package. Ensure that the app exists and you have access to it.",
                apierrors.ForbiddenError{},
                apierrors.NotFoundError{},
            ),
            "Error finding App", "App GUID", packageRecord.AppGUID,
        )
    }

    buildCreateMessage := payload.ToMessage(appRecord)

    record, err := h.buildRepo.CreateBuild(r.Context(), authInfo, buildCreateMessage)
    if err != nil {
        return nil, apierrors.LogAndReturn(logger, err, "Error creating build with repository")
    }

    return routing.NewResponse(http.StatusCreated).WithBody(presenter.ForBuild(record, h.serverURL)), nil
}

func (h *Build) update(r *http.Request) (*routing.Response, error) { //nolint:dupl
    return nil, apierrors.NewUnprocessableEntityError(errors.New("update build failed"), "Labels and annotations are not supported for builds.")
}

func (h *Build) UnauthenticatedRoutes() []routing.Route {
    return nil
}

func (h *Build) AuthenticatedRoutes() []routing.Route {
    return []routing.Route{
        {Method: "GET", Pattern: BuildPath, Handler: h.get},
        {Method: "POST", Pattern: BuildsPath, Handler: h.create},
        {Method: "PATCH", Pattern: BuildPath, Handler: h.update},
    }
}