feat: Added FE support for SelectObjectContent action

This commit is contained in:
jonaustin09
2023-08-02 00:08:28 +04:00
parent af69adf080
commit 009ceee748
5 changed files with 126 additions and 0 deletions

View File

@@ -43,6 +43,7 @@ type Backend interface {
ListParts(context.Context, *s3.ListPartsInput) (s3response.ListPartsResult, error)
UploadPart(context.Context, *s3.UploadPartInput) (etag string, err error)
UploadPartCopy(context.Context, *s3.UploadPartCopyInput) (s3response.CopyObjectResult, error)
SelectObjectContent(context.Context, *s3.SelectObjectContentInput) (s3response.SelectObjectContentResult, error)
PutObject(context.Context, *s3.PutObjectInput) (string, error)
HeadObject(context.Context, *s3.HeadObjectInput) (*s3.HeadObjectOutput, error)
@@ -100,6 +101,9 @@ func (BackendUnsupported) CreateBucket(context.Context, *s3.CreateBucketInput) e
func (BackendUnsupported) DeleteBucket(context.Context, *s3.DeleteBucketInput) error {
return s3err.GetAPIError(s3err.ErrNotImplemented)
}
func (BackendUnsupported) SelectObjectContent(context.Context, *s3.SelectObjectContentInput) (s3response.SelectObjectContentResult, error) {
return s3response.SelectObjectContentResult{}, s3err.GetAPIError(s3err.ErrNotImplemented)
}
func (BackendUnsupported) CreateMultipartUpload(context.Context, *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) {
return nil, s3err.GetAPIError(s3err.ErrNotImplemented)

View File

@@ -97,6 +97,9 @@ var _ backend.Backend = &BackendMock{}
// RestoreObjectFunc: func(contextMoqParam context.Context, restoreObjectInput *s3.RestoreObjectInput) error {
// panic("mock out the RestoreObject method")
// },
// SelectObjectContentFunc: func(contextMoqParam context.Context, selectObjectContentInput *s3.SelectObjectContentInput) (s3response.SelectObjectContentResult, error) {
// panic("mock out the SelectObjectContent method")
// },
// SetTagsFunc: func(contextMoqParam context.Context, bucket string, object string, tags map[string]string) error {
// panic("mock out the SetTags method")
// },
@@ -194,6 +197,9 @@ type BackendMock struct {
// RestoreObjectFunc mocks the RestoreObject method.
RestoreObjectFunc func(contextMoqParam context.Context, restoreObjectInput *s3.RestoreObjectInput) error
// SelectObjectContentFunc mocks the SelectObjectContent method.
SelectObjectContentFunc func(contextMoqParam context.Context, selectObjectContentInput *s3.SelectObjectContentInput) (s3response.SelectObjectContentResult, error)
// SetTagsFunc mocks the SetTags method.
SetTagsFunc func(contextMoqParam context.Context, bucket string, object string, tags map[string]string) error
@@ -396,6 +402,13 @@ type BackendMock struct {
// RestoreObjectInput is the restoreObjectInput argument value.
RestoreObjectInput *s3.RestoreObjectInput
}
// SelectObjectContent holds details about calls to the SelectObjectContent method.
SelectObjectContent []struct {
// ContextMoqParam is the contextMoqParam argument value.
ContextMoqParam context.Context
// SelectObjectContentInput is the selectObjectContentInput argument value.
SelectObjectContentInput *s3.SelectObjectContentInput
}
// SetTags holds details about calls to the SetTags method.
SetTags []struct {
// ContextMoqParam is the contextMoqParam argument value.
@@ -453,6 +466,7 @@ type BackendMock struct {
lockPutObjectAcl sync.RWMutex
lockRemoveTags sync.RWMutex
lockRestoreObject sync.RWMutex
lockSelectObjectContent sync.RWMutex
lockSetTags sync.RWMutex
lockShutdown sync.RWMutex
lockString sync.RWMutex
@@ -1380,6 +1394,42 @@ func (mock *BackendMock) RestoreObjectCalls() []struct {
return calls
}
// SelectObjectContent calls SelectObjectContentFunc.
func (mock *BackendMock) SelectObjectContent(contextMoqParam context.Context, selectObjectContentInput *s3.SelectObjectContentInput) (s3response.SelectObjectContentResult, error) {
if mock.SelectObjectContentFunc == nil {
panic("BackendMock.SelectObjectContentFunc: method is nil but Backend.SelectObjectContent was just called")
}
callInfo := struct {
ContextMoqParam context.Context
SelectObjectContentInput *s3.SelectObjectContentInput
}{
ContextMoqParam: contextMoqParam,
SelectObjectContentInput: selectObjectContentInput,
}
mock.lockSelectObjectContent.Lock()
mock.calls.SelectObjectContent = append(mock.calls.SelectObjectContent, callInfo)
mock.lockSelectObjectContent.Unlock()
return mock.SelectObjectContentFunc(contextMoqParam, selectObjectContentInput)
}
// SelectObjectContentCalls gets all the calls that were made to SelectObjectContent.
// Check the length with:
//
// len(mockedBackend.SelectObjectContentCalls())
func (mock *BackendMock) SelectObjectContentCalls() []struct {
ContextMoqParam context.Context
SelectObjectContentInput *s3.SelectObjectContentInput
} {
var calls []struct {
ContextMoqParam context.Context
SelectObjectContentInput *s3.SelectObjectContentInput
}
mock.lockSelectObjectContent.RLock()
calls = mock.calls.SelectObjectContent
mock.lockSelectObjectContent.RUnlock()
return calls
}
// SetTags calls SetTagsFunc.
func (mock *BackendMock) SetTags(contextMoqParam context.Context, bucket string, object string, tags map[string]string) error {
if mock.SetTagsFunc == nil {

View File

@@ -830,6 +830,34 @@ func (c S3ApiController) CreateActions(ctx *fiber.Ctx) error {
})
}
if ctx.Request().URI().QueryArgs().Has("select") && ctx.Query("select-type") == "2" {
var payload s3response.SelectObjectContentPayload
if err := xml.Unmarshal(ctx.Body(), &payload); err != nil {
return SendXMLResponse(ctx, nil, s3err.GetAPIError(s3err.ErrMalformedXML), &MetaOpts{
Logger: c.logger,
Action: "SelectObjectContent",
BucketOwner: parsedAcl.Owner,
})
}
if err := auth.VerifyACL(parsedAcl, bucket, access, "WRITE", isRoot); err != nil {
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "SelectObjectContent", BucketOwner: parsedAcl.Owner})
}
res, err := c.be.SelectObjectContent(ctx.Context(), &s3.SelectObjectContentInput{
Bucket: &bucket,
Key: &key,
Expression: payload.Expression,
ExpressionType: payload.ExpressionType,
InputSerialization: payload.InputSerialization,
OutputSerialization: payload.OutputSerialization,
RequestProgress: payload.RequestProgress,
ScanRange: payload.ScanRange,
})
return SendXMLResponse(ctx, res, err, &MetaOpts{Logger: c.logger, Action: "SelectObjectContent", BucketOwner: parsedAcl.Owner})
}
if uploadId != "" {
data := struct {
Parts []types.CompletedPart `xml:"Part"`

View File

@@ -1299,9 +1299,19 @@ func TestS3ApiController_CreateActions(t *testing.T) {
CreateMultipartUploadFunc: func(context.Context, *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) {
return &s3.CreateMultipartUploadOutput{}, nil
},
SelectObjectContentFunc: func(contextMoqParam context.Context, selectObjectContentInput *s3.SelectObjectContentInput) (s3response.SelectObjectContentResult, error) {
return s3response.SelectObjectContentResult{}, nil
},
},
}
bdy := `
<SelectObjectContentRequest>
<Expression>string</Expression>
<ExpressionType>string</ExpressionType>
</SelectObjectContentRequest>
`
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("isRoot", true)
@@ -1336,6 +1346,24 @@ func TestS3ApiController_CreateActions(t *testing.T) {
wantErr: false,
statusCode: 500,
},
{
name: "Select-object-content-invalid-body",
app: app,
args: args{
req: httptest.NewRequest(http.MethodPost, "/my-bucket/my-key?select&select-type=2", nil),
},
wantErr: false,
statusCode: 400,
},
{
name: "Select-object-content-invalid-body",
app: app,
args: args{
req: httptest.NewRequest(http.MethodPost, "/my-bucket/my-key?select&select-type=2", strings.NewReader(bdy)),
},
wantErr: false,
statusCode: 200,
},
{
name: "Complete-multipart-upload-error",
app: app,

View File

@@ -118,3 +118,19 @@ type DeleteObjectsResult struct {
Deleted []types.DeletedObject
Errors []types.Error
}
type SelectObjectContentPayload struct {
Expression *string
ExpressionType types.ExpressionType
RequestProgress *types.RequestProgress
InputSerialization *types.InputSerialization
OutputSerialization *types.OutputSerialization
ScanRange *types.ScanRange
}
type SelectObjectContentResult struct {
Records *types.RecordsEvent
Stats *types.StatsEvent
Progress *types.ProgressEvent
Cont *string
End *string
}