From 514062309b0d3d5ea77e83480360477c96628952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Mon, 23 Sep 2024 17:25:56 +0200 Subject: [PATCH] address code smells, refactor error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jörn Friedrich Dreyer --- .../fix-select-next-gateway-client.md | 3 +- .../pkg/connector/contentconnector.go | 110 +++++++----------- .../pkg/connector/contentconnector_test.go | 29 +++-- .../pkg/connector/fileconnector.go | 6 +- .../pkg/connector/fileconnector_test.go | 46 ++------ .../pkg/connector/httpadapter.go | 16 ++- .../pkg/connector/httpadapter_test.go | 80 +++++++++++++ 7 files changed, 165 insertions(+), 125 deletions(-) diff --git a/changelog/unreleased/fix-select-next-gateway-client.md b/changelog/unreleased/fix-select-next-gateway-client.md index 80730a01151..b08263f1a65 100644 --- a/changelog/unreleased/fix-select-next-gateway-client.md +++ b/changelog/unreleased/fix-select-next-gateway-client.md @@ -2,4 +2,5 @@ Bugfix: Always select next gateway client We now use the gateway selector to always select the next gateway client. This ensures that we can always connect to the gateway during up- and downscaling. -https://github.com/owncloud/ocis/pull/10133 +https://github.com/owncloud/ocis/pull/10141 +https://github.com/owncloud/ocis/pull/10133 \ No newline at end of file diff --git a/services/collaboration/pkg/connector/contentconnector.go b/services/collaboration/pkg/connector/contentconnector.go index 0bdd4e427ac..273937561d2 100644 --- a/services/collaboration/pkg/connector/contentconnector.go +++ b/services/collaboration/pkg/connector/contentconnector.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "crypto/tls" + "fmt" "io" "net/http" "strconv" @@ -105,13 +106,10 @@ func (c *ContentConnector) GetFile(ctx context.Context, w http.ResponseWriter) e sResp, err := gwc.Stat(ctx, &providerv1beta1.StatRequest{ Ref: wopiContext.FileReference, }) - if err != nil { - logger.Error().Err(err).Msg("GetFile: Stat Request failed") + if err := requestFailed(logger, sResp.GetStatus(), false, err, "GetFile: Stat Request failed"); err != nil { return err } - if sResp.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK { - return NewConnectorError(500, sResp.GetStatus().GetCode().String()+" "+sResp.GetStatus().GetMessage()) - } + // Initiate download request req := &providerv1beta1.InitiateFileDownloadRequest{ Ref: wopiContext.FileReference, @@ -125,19 +123,10 @@ func (c *ContentConnector) GetFile(ctx context.Context, w http.ResponseWriter) e return err } resp, err := gwc.InitiateFileDownload(ctx, req) - if err != nil { - logger.Error().Err(err).Msg("GetFile: InitiateFileDownload failed") + if err := requestFailed(logger, resp.GetStatus(), false, err, "GetFile: InitiateFileDownload failed"); err != nil { return err } - if resp.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK { - logger.Error(). - Str("StatusCode", resp.GetStatus().GetCode().String()). - Str("StatusMsg", resp.GetStatus().GetMessage()). - Msg("GetFile: InitiateFileDownload failed with wrong status") - return NewConnectorError(500, resp.GetStatus().GetCode().String()+" "+resp.GetStatus().GetMessage()) - } - // Figure out the download endpoint and download token downloadEndpoint := "" downloadToken := "" @@ -152,6 +141,10 @@ func (c *ContentConnector) GetFile(ctx context.Context, w http.ResponseWriter) e } } + logger = logger.With(). + Str("Endpoint", downloadEndpoint). + Bool("HasDownloadToken", hasDownloadToken).Logger() + httpClient := http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ @@ -164,21 +157,13 @@ func (c *ContentConnector) GetFile(ctx context.Context, w http.ResponseWriter) e // public link downloads have the token in the download endpoint httpReq, err := newHttpRequest(ctx, wopiContext, http.MethodGet, downloadEndpoint, downloadToken, bytes.NewReader([]byte(""))) if err != nil { - logger.Error(). - Err(err). - Str("Endpoint", downloadEndpoint). - Bool("HasDownloadToken", hasDownloadToken). - Msg("GetFile: Could not create the request to the endpoint") + logger.Error().Err(err).Msg("GetFile: Could not create the request to the endpoint") return err } httpResp, err := httpClient.Do(httpReq) if err != nil { - logger.Error(). - Err(err). - Str("Endpoint", downloadEndpoint). - Bool("HasDownloadToken", hasDownloadToken). - Msg("GetFile: Get request to the download endpoint failed") + logger.Error().Err(err).Msg("GetFile: Get request to the download endpoint failed") return err } @@ -186,7 +171,6 @@ func (c *ContentConnector) GetFile(ctx context.Context, w http.ResponseWriter) e if httpResp.StatusCode != http.StatusOK { logger.Error(). - Err(err). Int("HttpCode", httpResp.StatusCode). Msg("GetFile: downloading the file failed") return NewConnectorError(500, "GetFile: Downloading the file failed") @@ -248,19 +232,11 @@ func (c *ContentConnector) PutFile(ctx context.Context, stream io.Reader, stream statRes, err := gwc.Stat(ctx, &providerv1beta1.StatRequest{ Ref: wopiContext.FileReference, }) - if err != nil { - logger.Error().Err(err).Msg("PutFile: stat failed") + // we can ignore a not found error here, as we're going to create the file + if err := requestFailed(logger, statRes.GetStatus(), true, err, "PutFile: stat failed"); err != nil { return nil, err } - if statRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK && statRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_NOT_FOUND { - logger.Error(). - Str("StatusCode", statRes.GetStatus().GetCode().String()). - Str("StatusMsg", statRes.GetStatus().GetMessage()). - Msg("PutFile: stat failed with unexpected status") - return NewResponse(500), nil - } - mtime := statRes.GetInfo().GetMtime() // If there is a lock and it mismatches, return 409 if statRes.GetInfo().GetLock() != nil && statRes.GetInfo().GetLock().GetLockId() != lockID { @@ -308,19 +284,10 @@ func (c *ContentConnector) PutFile(ctx context.Context, stream io.Reader, stream } // Initiate the upload request resp, err := gwc.InitiateFileUpload(ctx, req) - if err != nil { - logger.Error().Err(err).Msg("UploadHelper: InitiateFileUpload failed") + if err := requestFailed(logger, resp.GetStatus(), false, err, "PutFile: InitiateFileUpload failed"); err != nil { return nil, err } - if resp.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK { - logger.Error(). - Str("StatusCode", resp.GetStatus().GetCode().String()). - Str("StatusMsg", resp.GetStatus().GetMessage()). - Msg("UploadHelper: InitiateFileUpload failed with wrong status") - return NewResponse(500), nil - } - // if the content length is greater than 0, we need to upload the content to the // target endpoint, otherwise we're done if streamLength > 0 { @@ -338,6 +305,10 @@ func (c *ContentConnector) PutFile(ctx context.Context, stream io.Reader, stream } } + logger = logger.With(). + Str("Endpoint", uploadEndpoint). + Bool("HasUploadToken", hasUploadToken).Logger() + httpClient := http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ @@ -351,11 +322,7 @@ func (c *ContentConnector) PutFile(ctx context.Context, stream io.Reader, stream // public link uploads have the token in the upload endpoint httpReq, err := newHttpRequest(ctx, wopiContext, http.MethodPut, uploadEndpoint, uploadToken, stream) if err != nil { - logger.Error(). - Err(err). - Str("Endpoint", uploadEndpoint). - Bool("HasUploadToken", hasUploadToken). - Msg("UploadHelper: Could not create the request to the endpoint") + logger.Error().Err(err).Msg("UploadHelper: Could not create the request to the endpoint") return nil, err } // "stream" is an *http.body and doesn't fill the httpReq.ContentLength automatically @@ -371,22 +338,16 @@ func (c *ContentConnector) PutFile(ctx context.Context, stream io.Reader, stream httpResp, err := httpClient.Do(httpReq) if err != nil { - logger.Error(). - Err(err). - Str("Endpoint", uploadEndpoint). - Bool("HasUploadToken", hasUploadToken). - Msg("UploadHelper: Put request to the upload endpoint failed") + logger.Error().Err(err).Msg("UploadHelper: Put request to the upload endpoint failed") return nil, err } defer httpResp.Body.Close() if httpResp.StatusCode != http.StatusOK { logger.Error(). - Str("Endpoint", uploadEndpoint). - Bool("HasUploadToken", hasUploadToken). Int("HttpCode", httpResp.StatusCode). Msg("UploadHelper: Put request to the upload endpoint failed with unexpected status") - return NewResponse(500), nil + return nil, NewConnectorError(500, fmt.Sprintf("unexpected status code %d from the upload endpoint", httpResp.StatusCode)) } gwc, err = c.gws.Next() if err != nil { @@ -397,20 +358,33 @@ func (c *ContentConnector) PutFile(ctx context.Context, stream io.Reader, stream statResAfter, err := gwc.Stat(ctx, &providerv1beta1.StatRequest{ Ref: wopiContext.FileReference, }) - if err != nil { - logger.Error().Err(err).Msg("PutFile: stat after upload failed") + if err := requestFailed(logger, statResAfter.GetStatus(), false, err, "PutFile: stat after upload failed"); err != nil { return nil, err } - if statResAfter.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK { - logger.Error(). - Str("StatusCode", statRes.GetStatus().GetCode().String()). - Str("StatusMsg", statRes.GetStatus().GetMessage()). - Msg("PutFile: stat after upload failed with unexpected status") - return NewResponse(500), nil - } mtime = statResAfter.GetInfo().GetMtime() } logger.Debug().Msg("PutFile: success") return NewResponseWithVersion(mtime), nil } + +func requestFailed(logger zerolog.Logger, s *rpcv1beta1.Status, allowNotFound bool, err error, msg string) error { + switch { + case err != nil: // a connection error + logger.Error().Err(err).Msg(msg) + return err + case s == nil: // we need a status + logger.Error().Msg(msg + ": nil status") + return NewConnectorError(500, msg+": nil status") + case s.GetCode() == rpcv1beta1.Code_CODE_OK: // ok is fine + return nil + case allowNotFound && s.GetCode() == rpcv1beta1.Code_CODE_NOT_FOUND: // not found might be ok + return nil + default: // any other status is an error + logger.Error(). + Str("StatusCode", s.GetCode().String()). + Str("StatusMsg", s.GetMessage()). + Msg(msg) + return NewConnectorError(500, s.GetCode().String()+" "+s.GetMessage()) + } +} diff --git a/services/collaboration/pkg/connector/contentconnector_test.go b/services/collaboration/pkg/connector/contentconnector_test.go index 9bdb374e352..dfe99f09a17 100644 --- a/services/collaboration/pkg/connector/contentconnector_test.go +++ b/services/collaboration/pkg/connector/contentconnector_test.go @@ -126,9 +126,8 @@ var _ = Describe("ContentConnector", func() { }, nil) err := cc.GetFile(ctx, sb) - Expect(err).To(HaveOccurred()) - conErr := err.(*connector.ConnectorError) - Expect(conErr.HttpCodeOut).To(Equal(500)) + targetErr := connector.NewConnectorError(500, "CODE_INTERNAL Something failed") + Expect(err).To(Equal(targetErr)) }) It("Missing download endpoint", func() { @@ -264,9 +263,9 @@ var _ = Describe("ContentConnector", func() { }, nil) response, err := cc.PutFile(ctx, reader, reader.Size(), "notARandomLockId") - Expect(err).ToNot(HaveOccurred()) - Expect(response.Status).To(Equal(500)) - Expect(response.Headers).To(BeNil()) + targetErr := connector.NewConnectorError(500, "CODE_INTERNAL Something failed") + Expect(err).To(Equal(targetErr)) + Expect(response).To(BeNil()) }) It("Mismatched lockId", func() { @@ -354,9 +353,9 @@ var _ = Describe("ContentConnector", func() { }, nil) response, err := cc.PutFile(ctx, reader, reader.Size(), "goodAndValidLock") - Expect(err).ToNot(HaveOccurred()) - Expect(response.Status).To(Equal(500)) - Expect(response.Headers).To(BeNil()) + targetErr := connector.NewConnectorError(500, "CODE_INTERNAL Something failed") + Expect(err).To(Equal(targetErr)) + Expect(response).To(BeNil()) }) It("Empty upload successful", func() { @@ -406,9 +405,9 @@ var _ = Describe("ContentConnector", func() { }, nil) response, err := cc.PutFile(ctx, reader, reader.Size(), "goodAndValidLock") - Expect(err).ToNot(HaveOccurred()) - Expect(response.Status).To(Equal(500)) - Expect(response.Headers).To(BeNil()) + targetErr := connector.NewConnectorError(500, "url is missing") + Expect(err).To(Equal(targetErr)) + Expect(response).To(BeNil()) }) It("upload request failed", func() { @@ -438,9 +437,9 @@ var _ = Describe("ContentConnector", func() { response, err := cc.PutFile(ctx, reader, reader.Size(), "goodAndValidLock") Expect(srvReqHeader.Get("X-Access-Token")).To(Equal(wopiCtx.AccessToken)) - Expect(err).ToNot(HaveOccurred()) - Expect(response.Status).To(Equal(500)) - Expect(response.Headers).To(BeNil()) + targetErr := connector.NewConnectorError(500, "unexpected status code 404 from the upload endpoint") + Expect(err).To(Equal(targetErr)) + Expect(response).To(BeNil()) }) It("upload request success", func() { diff --git a/services/collaboration/pkg/connector/fileconnector.go b/services/collaboration/pkg/connector/fileconnector.go index 12ba12bc7bd..a52b52fa76f 100644 --- a/services/collaboration/pkg/connector/fileconnector.go +++ b/services/collaboration/pkg/connector/fileconnector.go @@ -689,7 +689,7 @@ func (f *FileConnector) PutRelativeFileSuggested(ctx context.Context, ccs Conten // try to put the file. It mustn't return a 400 or 409 putResponse, err := ccs.PutFile(newCtx, stream, streamLength, "") if err != nil { - newLogger.Error().Err(err).Msg("PutRelativeFileSuggested: put file failed") + newLogger.Error().Err(err).Msg("PutRelativeFileSuggested: put file failed") // fails here return nil, err } @@ -799,7 +799,7 @@ func (f *FileConnector) PutRelativeFileRelative(ctx context.Context, ccs Content // try to put the file putResponse, err := ccs.PutFile(newCtx, stream, streamLength, "") if err != nil { - newLogger.Error().Err(err).Msg("PutRelativeFileRelative: put file failed") + newLogger.Error().Err(err).Msg("PutRelativeFileRelative: put file failed") // or here return nil, err } @@ -849,7 +849,7 @@ func (f *FileConnector) PutRelativeFileRelative(ctx context.Context, ccs Content newLogger.Error(). Str("LockID", lockID). Msg("PutRelativeFileRelative: put file failed with unhandled status") - return NewResponse(500), nil + return nil, NewConnectorError(putResponse.Status, "put file failed with unhandled status") } if err := f.adjustWopiReference(ctx, &wopiContext, newLogger); err != nil { diff --git a/services/collaboration/pkg/connector/fileconnector_test.go b/services/collaboration/pkg/connector/fileconnector_test.go index 1401b91f62f..94b8e4e3c2b 100644 --- a/services/collaboration/pkg/connector/fileconnector_test.go +++ b/services/collaboration/pkg/connector/fileconnector_test.go @@ -906,10 +906,7 @@ var _ = Describe("FileConnector", func() { stream := strings.NewReader("This is the content of a file") stat1ParamMatcher := mock.MatchedBy(func(statReq *providerv1beta1.StatRequest) bool { - if statReq.Ref.ResourceId == wopiCtx.FileReference.ResourceId { - return true - } - return false + return statReq.Ref.ResourceId == wopiCtx.FileReference.ResourceId }) gatewayClient.On("Stat", mock.Anything, stat1ParamMatcher).Times(1).Return(&providerv1beta1.StatResponse{ Status: status.NewOK(ctx), @@ -962,10 +959,7 @@ var _ = Describe("FileConnector", func() { stream := strings.NewReader("This is the content of a file") stat1ParamMatcher := mock.MatchedBy(func(statReq *providerv1beta1.StatRequest) bool { - if statReq.Ref.ResourceId == wopiCtx.FileReference.ResourceId { - return true - } - return false + return statReq.Ref.ResourceId == wopiCtx.FileReference.ResourceId }) gatewayClient.On("Stat", mock.Anything, stat1ParamMatcher).Times(1).Return(&providerv1beta1.StatResponse{ Status: status.NewOK(ctx), @@ -1021,10 +1015,7 @@ var _ = Describe("FileConnector", func() { stream := strings.NewReader("This is the content of a file") stat1ParamMatcher := mock.MatchedBy(func(statReq *providerv1beta1.StatRequest) bool { - if statReq.Ref.ResourceId == wopiCtx.FileReference.ResourceId { - return true - } - return false + return statReq.Ref.ResourceId == wopiCtx.FileReference.ResourceId }) gatewayClient.On("Stat", mock.Anything, stat1ParamMatcher).Times(1).Return(&providerv1beta1.StatResponse{ Status: status.NewOK(ctx), @@ -1086,10 +1077,7 @@ var _ = Describe("FileConnector", func() { stream := strings.NewReader("This is the content of a file") stat1ParamMatcher := mock.MatchedBy(func(statReq *providerv1beta1.StatRequest) bool { - if statReq.Ref.ResourceId == wopiCtx.FileReference.ResourceId { - return true - } - return false + return statReq.Ref.ResourceId == wopiCtx.FileReference.ResourceId }) gatewayClient.On("Stat", mock.Anything, stat1ParamMatcher).Times(1).Return(&providerv1beta1.StatResponse{ Status: status.NewOK(ctx), @@ -1159,10 +1147,7 @@ var _ = Describe("FileConnector", func() { stream := strings.NewReader("This is the content of a file") stat1ParamMatcher := mock.MatchedBy(func(statReq *providerv1beta1.StatRequest) bool { - if statReq.Ref.ResourceId == wopiCtx.FileReference.ResourceId { - return true - } - return false + return statReq.Ref.ResourceId == wopiCtx.FileReference.ResourceId }) gatewayClient.On("Stat", mock.Anything, stat1ParamMatcher).Times(1).Return(&providerv1beta1.StatResponse{ Status: status.NewOK(ctx), @@ -1214,10 +1199,7 @@ var _ = Describe("FileConnector", func() { stream := strings.NewReader("This is the content of a file") stat1ParamMatcher := mock.MatchedBy(func(statReq *providerv1beta1.StatRequest) bool { - if statReq.Ref.ResourceId == wopiCtx.FileReference.ResourceId { - return true - } - return false + return statReq.Ref.ResourceId == wopiCtx.FileReference.ResourceId }) gatewayClient.On("Stat", mock.Anything, stat1ParamMatcher).Times(1).Return(&providerv1beta1.StatResponse{ Status: status.NewOK(ctx), @@ -1274,10 +1256,7 @@ var _ = Describe("FileConnector", func() { stream := strings.NewReader("This is the content of a file") stat1ParamMatcher := mock.MatchedBy(func(statReq *providerv1beta1.StatRequest) bool { - if statReq.Ref.ResourceId == wopiCtx.FileReference.ResourceId { - return true - } - return false + return statReq.Ref.ResourceId == wopiCtx.FileReference.ResourceId }) gatewayClient.On("Stat", mock.Anything, stat1ParamMatcher).Times(1).Return(&providerv1beta1.StatResponse{ Status: status.NewOK(ctx), @@ -1291,13 +1270,12 @@ var _ = Describe("FileConnector", func() { }, }, nil) - ccs.On("PutFile", mock.Anything, stream, int64(stream.Len()), "").Times(1).Return(connector.NewResponse(500), nil) + ccs.On("PutFile", mock.Anything, stream, int64(stream.Len()), "").Times(1).Return(nil, connector.NewConnectorError(500, "Something happened")) response, err := fc.PutRelativeFileRelative(ctx, ccs, stream, int64(stream.Len()), "convFile.pdf") - Expect(err).ToNot(HaveOccurred()) - Expect(response.Status).To(Equal(500)) - Expect(response.Headers).To(BeNil()) - Expect(response.Body).To(BeNil()) + targetErr := connector.NewConnectorError(500, "Something happened") + Expect(err).To(Equal(targetErr)) + Expect(response).To(BeNil()) }) }) @@ -1727,7 +1705,7 @@ var _ = Describe("FileConnector", func() { u := &userv1beta1.User{} u.Opaque = &typesv1beta1.Opaque{ Map: map[string]*typesv1beta1.OpaqueEntry{ - "public-share-role": &typesv1beta1.OpaqueEntry{ + "public-share-role": { Decoder: "plain", Value: []byte("viewer"), }, diff --git a/services/collaboration/pkg/connector/httpadapter.go b/services/collaboration/pkg/connector/httpadapter.go index b2d20e026ec..0054df39fac 100644 --- a/services/collaboration/pkg/connector/httpadapter.go +++ b/services/collaboration/pkg/connector/httpadapter.go @@ -190,8 +190,12 @@ func (h *HttpAdapter) PutFile(w http.ResponseWriter, r *http.Request) { response, err := contentCon.PutFile(r.Context(), r.Body, r.ContentLength, lockID) if err != nil { - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return + var connErr *ConnectorError + if errors.As(err, &connErr) && connErr.HttpCodeOut != 0 { + response = NewResponse(connErr.HttpCodeOut) + } else { + response = NewResponse(http.StatusInternalServerError) + } } h.writeConnectorResponse(w, r, response) @@ -240,8 +244,12 @@ func (h *HttpAdapter) PutRelativeFile(w http.ResponseWriter, r *http.Request) { } if putErr != nil { - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return + var connErr *ConnectorError + if errors.As(putErr, &connErr) && connErr.HttpCodeOut != 0 { + response = NewResponse(connErr.HttpCodeOut) + } else { + response = NewResponse(http.StatusInternalServerError) + } } h.writeConnectorResponse(w, r, response) diff --git a/services/collaboration/pkg/connector/httpadapter_test.go b/services/collaboration/pkg/connector/httpadapter_test.go index 3d3d97c7add..7a5921b8a57 100644 --- a/services/collaboration/pkg/connector/httpadapter_test.go +++ b/services/collaboration/pkg/connector/httpadapter_test.go @@ -477,6 +477,26 @@ var _ = Describe("HttpAdapter", func() { httpAdapter.PutFile(w, req) resp := w.Result() Expect(resp.StatusCode).To(Equal(500)) + + content, _ := io.ReadAll(resp.Body) + Expect(content).To(Equal([]byte(""))) + }) + + It("Connector error", func() { + contentBody := "this is the new fake content" + req := httptest.NewRequest("GET", "/wopi/files/abcdef/contents", strings.NewReader(contentBody)) + req.Header.Set(connector.HeaderWopiLock, "abc123") + + w := httptest.NewRecorder() + + cc.On("PutFile", mock.Anything, mock.Anything, int64(len(contentBody)), "abc123").Times(1).Return(nil, connector.NewConnectorError(500, "Something happened")) + + httpAdapter.PutFile(w, req) + resp := w.Result() + Expect(resp.StatusCode).To(Equal(500)) + + content, _ := io.ReadAll(resp.Body) + Expect(content).To(Equal([]byte(""))) }) It("Conflict", func() { @@ -517,4 +537,64 @@ var _ = Describe("HttpAdapter", func() { Expect(resp.Header.Get(connector.HeaderWopiVersion)).To(Equal("v1234567")) }) }) + + Describe("PutRelativeFile", func() { + It("Connector error", func() { + contentBody := "this is the new fake content" + req := httptest.NewRequest("GET", "/wopi/files/abcdef/contents", strings.NewReader(contentBody)) + req.Header.Set(connector.HeaderWopiLock, "abc123") + req.Header.Set(connector.HeaderWopiRT, "relativetarget.docx") + + w := httptest.NewRecorder() + + fc.On("PutRelativeFileRelative", mock.Anything, mock.Anything, mock.Anything, int64(len(contentBody)), "relativetarget.docx").Times(1).Return(nil, connector.NewConnectorError(500, "Something happened")) + + httpAdapter.PutRelativeFile(w, req) + resp := w.Result() + Expect(resp.StatusCode).To(Equal(500)) + + content, _ := io.ReadAll(resp.Body) + Expect(content).To(Equal([]byte(""))) + }) + + It("Conflict", func() { + contentBody := "this is the new fake content" + req := httptest.NewRequest("GET", "/wopi/files/abcdef/contents", strings.NewReader(contentBody)) + req.Header.Set(connector.HeaderWopiLock, "abc123") + req.Header.Set(connector.HeaderWopiRT, "relativetarget.docx") + + w := httptest.NewRecorder() + + fc.On("PutRelativeFileRelative", mock.Anything, mock.Anything, mock.Anything, int64(len(contentBody)), "relativetarget.docx").Times(1).Return( + connector.NewResponseLockConflict("zzz111", "Lock Conflict"), nil) + + httpAdapter.PutRelativeFile(w, req) + resp := w.Result() + Expect(resp.StatusCode).To(Equal(409)) + Expect(resp.Header.Get(connector.HeaderWopiLock)).To(Equal("zzz111")) + Expect(resp.Header.Get(connector.HeaderWopiLockFailureReason)).To(Equal("Lock Conflict")) + }) + + It("Success", func() { + contentBody := "this is the new fake content" + req := httptest.NewRequest("GET", "/wopi/files/abcdef/contents", strings.NewReader(contentBody)) + req.Header.Set(connector.HeaderWopiLock, "abc123") + req.Header.Set(connector.HeaderWopiRT, "relativetarget.docx") + + w := httptest.NewRecorder() + + fc.On("PutRelativeFileRelative", mock.Anything, mock.Anything, mock.Anything, int64(len(contentBody)), "relativetarget.docx").Times(1).Return( + connector.NewResponseWithVersionAndLock( + 200, + &typesv1beta1.Timestamp{Seconds: uint64(1234), Nanos: uint32(567)}, + "abc123", + ), nil) + + httpAdapter.PutRelativeFile(w, req) + resp := w.Result() + Expect(resp.StatusCode).To(Equal(200)) + Expect(resp.Header.Get(connector.HeaderWopiLock)).To(Equal("abc123")) + Expect(resp.Header.Get(connector.HeaderWopiVersion)).To(Equal("v1234567")) + }) + }) })