-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into get_offering
- Loading branch information
Showing
17 changed files
with
793 additions
and
188 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package fake | ||
|
||
import ( | ||
"context" | ||
"sync" | ||
|
||
"code.cloudfoundry.org/korifi/api/authorization" | ||
"code.cloudfoundry.org/korifi/api/handlers" | ||
"code.cloudfoundry.org/korifi/api/repositories" | ||
) | ||
|
||
type StackRepository struct { | ||
ListStacksStub func(context.Context, authorization.Info) ([]repositories.StackRecord, error) | ||
listStacksMutex sync.RWMutex | ||
listStacksArgsForCall []struct { | ||
arg1 context.Context | ||
arg2 authorization.Info | ||
} | ||
listStacksReturns struct { | ||
result1 []repositories.StackRecord | ||
result2 error | ||
} | ||
listStacksReturnsOnCall map[int]struct { | ||
result1 []repositories.StackRecord | ||
result2 error | ||
} | ||
invocations map[string][][]interface{} | ||
invocationsMutex sync.RWMutex | ||
} | ||
|
||
func (fake *StackRepository) ListStacks(arg1 context.Context, arg2 authorization.Info) ([]repositories.StackRecord, error) { | ||
fake.listStacksMutex.Lock() | ||
ret, specificReturn := fake.listStacksReturnsOnCall[len(fake.listStacksArgsForCall)] | ||
fake.listStacksArgsForCall = append(fake.listStacksArgsForCall, struct { | ||
arg1 context.Context | ||
arg2 authorization.Info | ||
}{arg1, arg2}) | ||
stub := fake.ListStacksStub | ||
fakeReturns := fake.listStacksReturns | ||
fake.recordInvocation("ListStacks", []interface{}{arg1, arg2}) | ||
fake.listStacksMutex.Unlock() | ||
if stub != nil { | ||
return stub(arg1, arg2) | ||
} | ||
if specificReturn { | ||
return ret.result1, ret.result2 | ||
} | ||
return fakeReturns.result1, fakeReturns.result2 | ||
} | ||
|
||
func (fake *StackRepository) ListStacksCallCount() int { | ||
fake.listStacksMutex.RLock() | ||
defer fake.listStacksMutex.RUnlock() | ||
return len(fake.listStacksArgsForCall) | ||
} | ||
|
||
func (fake *StackRepository) ListStacksCalls(stub func(context.Context, authorization.Info) ([]repositories.StackRecord, error)) { | ||
fake.listStacksMutex.Lock() | ||
defer fake.listStacksMutex.Unlock() | ||
fake.ListStacksStub = stub | ||
} | ||
|
||
func (fake *StackRepository) ListStacksArgsForCall(i int) (context.Context, authorization.Info) { | ||
fake.listStacksMutex.RLock() | ||
defer fake.listStacksMutex.RUnlock() | ||
argsForCall := fake.listStacksArgsForCall[i] | ||
return argsForCall.arg1, argsForCall.arg2 | ||
} | ||
|
||
func (fake *StackRepository) ListStacksReturns(result1 []repositories.StackRecord, result2 error) { | ||
fake.listStacksMutex.Lock() | ||
defer fake.listStacksMutex.Unlock() | ||
fake.ListStacksStub = nil | ||
fake.listStacksReturns = struct { | ||
result1 []repositories.StackRecord | ||
result2 error | ||
}{result1, result2} | ||
} | ||
|
||
func (fake *StackRepository) ListStacksReturnsOnCall(i int, result1 []repositories.StackRecord, result2 error) { | ||
fake.listStacksMutex.Lock() | ||
defer fake.listStacksMutex.Unlock() | ||
fake.ListStacksStub = nil | ||
if fake.listStacksReturnsOnCall == nil { | ||
fake.listStacksReturnsOnCall = make(map[int]struct { | ||
result1 []repositories.StackRecord | ||
result2 error | ||
}) | ||
} | ||
fake.listStacksReturnsOnCall[i] = struct { | ||
result1 []repositories.StackRecord | ||
result2 error | ||
}{result1, result2} | ||
} | ||
|
||
func (fake *StackRepository) Invocations() map[string][][]interface{} { | ||
fake.invocationsMutex.RLock() | ||
defer fake.invocationsMutex.RUnlock() | ||
fake.listStacksMutex.RLock() | ||
defer fake.listStacksMutex.RUnlock() | ||
copiedInvocations := map[string][][]interface{}{} | ||
for key, value := range fake.invocations { | ||
copiedInvocations[key] = value | ||
} | ||
return copiedInvocations | ||
} | ||
|
||
func (fake *StackRepository) recordInvocation(key string, args []interface{}) { | ||
fake.invocationsMutex.Lock() | ||
defer fake.invocationsMutex.Unlock() | ||
if fake.invocations == nil { | ||
fake.invocations = map[string][][]interface{}{} | ||
} | ||
if fake.invocations[key] == nil { | ||
fake.invocations[key] = [][]interface{}{} | ||
} | ||
fake.invocations[key] = append(fake.invocations[key], args) | ||
} | ||
|
||
var _ handlers.StackRepository = new(StackRepository) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package handlers | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
"net/url" | ||
|
||
"code.cloudfoundry.org/korifi/api/authorization" | ||
apierrors "code.cloudfoundry.org/korifi/api/errors" | ||
"code.cloudfoundry.org/korifi/api/presenter" | ||
"code.cloudfoundry.org/korifi/api/repositories" | ||
"code.cloudfoundry.org/korifi/api/routing" | ||
|
||
"github.com/go-logr/logr" | ||
) | ||
|
||
const ( | ||
StacksPath = "/v3/stacks" | ||
) | ||
|
||
type StackRepository interface { | ||
ListStacks(ctx context.Context, authInfo authorization.Info) ([]repositories.StackRecord, error) | ||
} | ||
|
||
type Stack struct { | ||
serverURL url.URL | ||
stackRepo StackRepository | ||
} | ||
|
||
func NewStack( | ||
serverURL url.URL, | ||
stackRepo StackRepository, | ||
) *Stack { | ||
return &Stack{ | ||
serverURL: serverURL, | ||
stackRepo: stackRepo, | ||
} | ||
} | ||
|
||
func (h *Stack) list(r *http.Request) (*routing.Response, error) { | ||
authInfo, _ := authorization.InfoFromContext(r.Context()) | ||
logger := logr.FromContextOrDiscard(r.Context()).WithName("handlers.build.list") | ||
|
||
stacks, err := h.stackRepo.ListStacks(r.Context(), authInfo) | ||
if err != nil { | ||
return nil, apierrors.LogAndReturn(logger, err, "Failed to fetch buildpacks from Kubernetes") | ||
} | ||
|
||
return routing.NewResponse(http.StatusOK).WithBody(presenter.ForList(presenter.ForStack, stacks, h.serverURL, *r.URL)), nil | ||
Check failure on line 49 in api/handlers/stack.go GitHub Actions / linter
|
||
} | ||
|
||
func (h *Stack) UnauthenticatedRoutes() []routing.Route { | ||
return nil | ||
} | ||
|
||
func (h *Stack) AuthenticatedRoutes() []routing.Route { | ||
return []routing.Route{ | ||
{Method: "GET", Pattern: StacksPath, Handler: h.list}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package handlers_test | ||
|
||
import ( | ||
"errors" | ||
"net/http" | ||
"time" | ||
|
||
. "code.cloudfoundry.org/korifi/api/handlers" | ||
"code.cloudfoundry.org/korifi/api/handlers/fake" | ||
"code.cloudfoundry.org/korifi/api/repositories" | ||
. "code.cloudfoundry.org/korifi/tests/matchers" | ||
"code.cloudfoundry.org/korifi/tools" | ||
|
||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
var _ = Describe("Stack", func() { | ||
var ( | ||
stackRepo *fake.StackRepository | ||
req *http.Request | ||
) | ||
|
||
BeforeEach(func() { | ||
stackRepo = new(fake.StackRepository) | ||
|
||
apiHandler := NewStack(*serverURL, stackRepo) | ||
routerBuilder.LoadRoutes(apiHandler) | ||
}) | ||
|
||
JustBeforeEach(func() { | ||
routerBuilder.Build().ServeHTTP(rr, req) | ||
}) | ||
|
||
Describe("the GET /v3/stacks endpoint", func() { | ||
BeforeEach(func() { | ||
stackRepo.ListStacksReturns([]repositories.StackRecord{ | ||
{ | ||
Name: "io.buildpacks.stacks.jammy", | ||
Description: "Jammy Stack", | ||
CreatedAt: time.UnixMilli(1000), | ||
UpdatedAt: tools.PtrTo(time.UnixMilli(2000)), | ||
}, | ||
}, nil) | ||
|
||
var err error | ||
req, err = http.NewRequestWithContext(ctx, "GET", "/v3/stacks", nil) | ||
Expect(err).NotTo(HaveOccurred()) | ||
}) | ||
|
||
It("returns the stacks for the default builder", func() { | ||
Expect(stackRepo.ListStacksCallCount()).To(Equal(1)) | ||
_, actualAuthInfo := stackRepo.ListStacksArgsForCall(0) | ||
Expect(actualAuthInfo).To(Equal(authInfo)) | ||
|
||
Expect(rr).To(HaveHTTPStatus(http.StatusOK)) | ||
Expect(rr).To(HaveHTTPHeaderWithValue("Content-Type", "application/json")) | ||
Expect(rr).To(HaveHTTPBody(SatisfyAll( | ||
MatchJSONPath("$.pagination.total_results", BeEquivalentTo(1)), | ||
MatchJSONPath("$.pagination.first.href", "https://api.example.org/v3/stacks"), | ||
MatchJSONPath("$.resources", HaveLen(1)), | ||
MatchJSONPath("$.resources[0].name", "io.buildpacks.stacks.jammy"), | ||
))) | ||
}) | ||
When("there is some other error fetching the stacks", func() { | ||
BeforeEach(func() { | ||
stackRepo.ListStacksReturns([]repositories.StackRecord{}, errors.New("unknown!")) | ||
}) | ||
|
||
It("returns an error", func() { | ||
expectUnknownError() | ||
}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package presenter | ||
|
||
import ( | ||
"net/url" | ||
|
||
"code.cloudfoundry.org/korifi/api/repositories" | ||
) | ||
|
||
const ( | ||
stacksBase = "/v3/stacks" | ||
) | ||
|
||
type StackResponse struct { | ||
GUID string `json:"guid"` | ||
CreatedAt string `json:"created_at"` | ||
UpdatedAt string `json:"updated_at"` | ||
Name string `json:"name"` | ||
Description string `json:"description"` | ||
Metadata Metadata `json:"metadata"` | ||
Links StackLinks `json:"links"` | ||
} | ||
|
||
type StackLinks struct { | ||
Self Link `json:"self"` | ||
} | ||
|
||
func ForStack(stackRecord repositories.StackRecord, baseURL url.URL) StackResponse { | ||
return StackResponse{ | ||
GUID: stackRecord.GUID, | ||
CreatedAt: formatTimestamp(&stackRecord.CreatedAt), | ||
UpdatedAt: formatTimestamp(stackRecord.UpdatedAt), | ||
Name: stackRecord.Name, | ||
Links: StackLinks{ | ||
Self: Link{ | ||
HRef: buildURL(baseURL).appendPath(stacksBase, stackRecord.GUID).build(), | ||
}, | ||
}, | ||
} | ||
} |
Oops, something went wrong.