This repository has been archived by the owner on Nov 27, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
rest: add initial support for PATCH #315
Merged
openshift-merge-bot
merged 10 commits into
konflux-workspaces:main
from
filariow:rest-patch-initial-support
Oct 14, 2024
Merged
Changes from 6 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
6c0ed76
rest: add initial support for PATCH
filariow 8499492
enable patch endpoint
filariow 5c4f878
add patch endpoint e2e test
filariow 5f4e364
Merge branch 'main' into rest-patch-initial-support
filariow 38e1e8b
remove @wip
filariow c5f41bd
fix user patch step
filariow bd8a3d3
Add tests for unsupported patch types
filariow 28668ed
Merge branch 'main' into rest-patch-initial-support
filariow d001c6a
add strategic merge support
filariow f4c6ad4
Merge branch 'main' into rest-patch-initial-support
filariow File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
Feature: Patch workspaces via REST API | ||
|
||
Scenario: users can update their workspaces' visibility | ||
Given An user is onboarded | ||
And Default workspace is created for them | ||
When The user patches workspace visibility to "community" | ||
Then The workspace visibility is updated to "community" | ||
|
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
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,106 @@ | ||
package workspace | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
|
||
"k8s.io/apimachinery/pkg/types" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
jsonpatch "github.com/evanphx/json-patch/v5" | ||
ccontext "github.com/konflux-workspaces/workspaces/server/core/context" | ||
"github.com/konflux-workspaces/workspaces/server/log" | ||
|
||
restworkspacesv1alpha1 "github.com/konflux-workspaces/workspaces/server/api/v1alpha1" | ||
workspacesv1alpha1 "github.com/konflux-workspaces/workspaces/server/api/v1alpha1" | ||
) | ||
|
||
// PatchWorkspaceCommand contains the information needed to retrieve a Workspace the user has access to from the data source | ||
type PatchWorkspaceCommand struct { | ||
Owner string | ||
Workspace string | ||
Patch []byte | ||
PatchType types.PatchType | ||
} | ||
|
||
// PatchWorkspaceResponse contains the workspace the user requested | ||
type PatchWorkspaceResponse struct { | ||
Workspace *restworkspacesv1alpha1.Workspace | ||
} | ||
|
||
// PatchWorkspaceHandler processes PatchWorkspaceCommand and returns PatchWorkspaceResponse fetching data from a WorkspacePatcher | ||
type PatchWorkspaceHandler struct { | ||
reader WorkspaceReader | ||
updater WorkspaceUpdater | ||
} | ||
|
||
// NewPatchWorkspaceHandler creates a new PatchWorkspaceHandler that uses a specified WorkspacePatcher | ||
func NewPatchWorkspaceHandler(reader WorkspaceReader, updater WorkspaceUpdater) *PatchWorkspaceHandler { | ||
return &PatchWorkspaceHandler{ | ||
reader: reader, | ||
updater: updater, | ||
} | ||
} | ||
|
||
// Handle handles a PatchWorkspaceCommand and returns a PatchWorkspaceResponse or an error | ||
func (h *PatchWorkspaceHandler) Handle(ctx context.Context, command PatchWorkspaceCommand) (*PatchWorkspaceResponse, error) { | ||
// authorization | ||
// If required, implement here complex logic like multiple-domains filtering, etc | ||
u, ok := ctx.Value(ccontext.UserSignupComplaintNameKey).(string) | ||
if !ok { | ||
return nil, fmt.Errorf("unauthenticated request") | ||
} | ||
|
||
// validate query | ||
// TODO: sanitize input, block reserved labels, etc | ||
|
||
// retrieve workspace | ||
w := workspacesv1alpha1.Workspace{} | ||
if err := h.reader.ReadUserWorkspace(ctx, u, command.Owner, command.Workspace, &w); err != nil { | ||
return nil, err | ||
} | ||
|
||
// apply patch | ||
pw, err := h.applyPatch(&w, command) | ||
if err != nil { | ||
return nil, fmt.Errorf("error patching Workspace %s/%s: %w", command.Owner, command.Workspace, err) | ||
} | ||
|
||
log.FromContext(ctx).Debug("updating workspace", "workspace", pw) | ||
opts := &client.UpdateOptions{} | ||
if err := h.updater.UpdateUserWorkspace(ctx, u, pw, opts); err != nil { | ||
return nil, err | ||
} | ||
|
||
// reply | ||
return &PatchWorkspaceResponse{ | ||
Workspace: pw, | ||
}, nil | ||
} | ||
|
||
func (h *PatchWorkspaceHandler) applyPatch(w *workspacesv1alpha1.Workspace, command PatchWorkspaceCommand) (*workspacesv1alpha1.Workspace, error) { | ||
if command.PatchType != types.MergePatchType { | ||
return nil, fmt.Errorf("unsupported patch type: %s", command.PatchType) | ||
} | ||
|
||
// marshal workspace as json | ||
wj, err := json.Marshal(w) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// apply jsonpatch | ||
pwj, err := jsonpatch.MergePatch(wj, []byte(command.Patch)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// unmarshal json to struct | ||
pw := workspacesv1alpha1.Workspace{} | ||
if err := json.Unmarshal(pwj, &pw); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &pw, nil | ||
} |
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,91 @@ | ||
package workspace_test | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
"go.uber.org/mock/gomock" | ||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/types" | ||
|
||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
ccontext "github.com/konflux-workspaces/workspaces/server/core/context" | ||
"github.com/konflux-workspaces/workspaces/server/core/workspace" | ||
|
||
workspacesv1alpha1 "github.com/konflux-workspaces/workspaces/server/api/v1alpha1" | ||
) | ||
|
||
var _ = Describe("", func() { | ||
var ( | ||
ctrl *gomock.Controller | ||
ctx context.Context | ||
reader *MockWorkspaceReader | ||
updater *MockWorkspaceUpdater | ||
request workspace.PatchWorkspaceCommand | ||
handler workspace.PatchWorkspaceHandler | ||
w workspacesv1alpha1.Workspace | ||
) | ||
|
||
BeforeEach(func() { | ||
ctrl = gomock.NewController(GinkgoT()) | ||
ctx = context.Background() | ||
w = workspacesv1alpha1.Workspace{ | ||
ObjectMeta: v1.ObjectMeta{ | ||
Name: "default", | ||
Namespace: "user", | ||
}, | ||
Spec: workspacesv1alpha1.WorkspaceSpec{ | ||
Visibility: workspacesv1alpha1.WorkspaceVisibilityPrivate, | ||
}, | ||
} | ||
updater = NewMockWorkspaceUpdater(ctrl) | ||
reader = NewMockWorkspaceReader(ctrl) | ||
request = workspace.PatchWorkspaceCommand{ | ||
Workspace: w.Name, | ||
Owner: w.Namespace, | ||
} | ||
handler = *workspace.NewPatchWorkspaceHandler(reader, updater) | ||
}) | ||
|
||
AfterEach(func() { ctrl.Finish() }) | ||
|
||
It("should not allow unauthenticated requests", func() { | ||
// don't set the "user" value within ctx | ||
|
||
response, err := handler.Handle(ctx, request) | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err).To(Equal(fmt.Errorf("unauthenticated request"))) | ||
Expect(response).To(BeNil()) | ||
}) | ||
|
||
It("should allow authenticated requests", func() { | ||
// given | ||
request.PatchType = types.MergePatchType | ||
request.Patch = []byte(`{"spec":{"visibility":"community"}}`) | ||
username := "foo" | ||
ctx := context.WithValue(ctx, ccontext.UserSignupComplaintNameKey, username) | ||
opts := &client.UpdateOptions{} | ||
reader.EXPECT(). | ||
ReadUserWorkspace(ctx, username, w.Namespace, w.Name, gomock.Any(), gomock.Any()). | ||
DoAndReturn(func(ctx context.Context, user, owner, workspace string, rw *workspacesv1alpha1.Workspace, opts ...client.GetOption) error { | ||
w.DeepCopyInto(rw) | ||
return nil | ||
}) | ||
updater.EXPECT(). | ||
UpdateUserWorkspace(ctx, username, gomock.Any(), opts). | ||
Return(nil) | ||
|
||
// when | ||
response, err := handler.Handle(ctx, request) | ||
|
||
// then | ||
Expect(err).NotTo(HaveOccurred()) | ||
Expect(response).NotTo(BeNil()) | ||
expectedWorkspace := w.DeepCopy() | ||
expectedWorkspace.Spec.Visibility = workspacesv1alpha1.WorkspaceVisibilityCommunity | ||
Expect(response.Workspace).To(BeEquivalentTo(expectedWorkspace)) | ||
}) | ||
}) |
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
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe add a test so that other patch types are rejected for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good idea, let me add them
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in bd8a3d3