From 33c23d89e4e3e7391f4192e8d35baca16082a526 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Tue, 23 Jan 2024 16:50:03 -0500 Subject: [PATCH 1/2] WIP: Ajouter fonction de renom de document --- apihandler/document.go | 78 +++++++++++++++++++++++++++++++++++++++++ apirequest/document.go | 54 ++++++++++++++++++++++++++++ apiresponse/document.go | 8 +++++ cmd/server.go | 7 ++++ 4 files changed, 147 insertions(+) diff --git a/apihandler/document.go b/apihandler/document.go index 3f4a2ae..284add4 100644 --- a/apihandler/document.go +++ b/apihandler/document.go @@ -277,3 +277,81 @@ func (h *V1Handler) V1DocumentDELETE(c echo.Context) error { return c.JSON(response.StatusCode, response) } + +// V1DocumentKeyPUT +func (h *V1Handler) V1DocumentKeyPUT(c echo.Context) (err error) { + var request apirequest.V1DocumentKeyPUT + var response apiresponse.V1DocumentKeyPUT + + bucket := c.Param("bucket") + document := c.Param("document") + newKey := c.Bind( + + request, err = apirequest.NewV1DocumentKeyPUT(bucket, document, newKey) + if err != nil { + response.StatusCode = http.StatusBadRequest + response.Message = err.Error() + + return c.JSON(response.StatusCode, response) + } + + if !request.Complete() { + response.StatusCode = http.StatusBadRequest + response.Message = "Incomplete V1DocumentKeyPUT request received" + + return c.JSON(response.StatusCode, response) + } + + var allowed bool + for bucketAllowed := range h.Config.Server.Documents.Buckets { + if bucket == bucketAllowed { + allowed = true + } + } + + if !allowed { + return c.JSON(apiresponse.NotFoundResponse()) + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + bucketExists, err := h.MediaClient.MinioClient.BucketExists(ctx, bucket) + if err != nil { + return c.JSON(http.StatusInternalServerError, "Could not validate bucket exists") + } + + if !bucketExists { + return c.JSON(apiresponse.NotFoundResponse()) + } + + if _, err := h.MediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}); err != nil { + if err.Error() == "The specified key does not exist." { + + return c.JSON(apiresponse.NotFoundResponse()) + } + + response.Message = fmt.Sprintf("Could not obtain information on %s/%s", bucket, document) + response.StatusCode = http.StatusInternalServerError + + return c.JSON(response.StatusCode, response) + } + + if _, err := h.MediaClient.MinioClient.CopyObject(ctx, + minio.CopyDestOptions{Bucket: bucket, Object: newKey}, + minio.CopySrcOptions{Bucket: bucket, Object: document}, + ); err != nil { + response.StatusCode = http.StatusInternalServerError + response.Message = "Impossible de copier un document pour le renommer" + return c.JSON(response.StatusCode, response) + } + + //TODO verify copy was successful + + //TODO delete old file + + //TODO cleanup + + //TODO return result + +} diff --git a/apirequest/document.go b/apirequest/document.go index b5c1191..3953a6d 100644 --- a/apirequest/document.go +++ b/apirequest/document.go @@ -163,3 +163,57 @@ func (request V1DocumentDELETE) Request(v *voki.Voki) (response apiresponse.V1Do return response, v.UnmarshalIfComplete(http.MethodDelete, fmt.Sprintf("/v1/bucket/%s/%s", request.Params.Bucket, request.Params.Document), nil, true, &response) } + +var _ request.Requester[apiresponse.V1DocumentKeyPUT] = V1DocumentKeyPUT{} + +type V1DocumentKeyPUT struct { + Data struct { + NewKey string `json:"newKey"` + } + Params struct { + Bucket string `json:"bucket"` + Document string `json:"document"` + } +} + +func NewV1DocumentKeyPUT(bucket, document, newKey string) (request V1DocumentKeyPUT, err error) { + if bucket == "" { + err = fmt.Errorf("NewV1DocumentKeyPUT requires non-nil bucket name") + return + } + + request.Params.Bucket = bucket + + if document == "" { + err = fmt.Errorf("NewV1DocumentKeyPUT requires non-nil src document name") + return + } + + request.Params.Document = document + + if newKey == "" { + err = fmt.Errorf("NewV1DocumentKeyPUT requires non-nil dst document name") + } + + request.Data.NewKey = newKey + + return +} + +func (request V1DocumentKeyPUT) Complete() bool { + return request.Params.Bucket != "" && request.Params.Document != "" && request.Data.NewKey != "" +} + +func (request V1DocumentKeyPUT) Request(v *voki.Voki) (response apiresponse.V1DocumentKeyPUT, err error) { + if !request.Complete() { + err = fmt.Errorf("Incomplete V1DocumentKeyPUT request") + return + } + + var buf bytes.Buffer + if err = json.NewEncoder(&buf).Encode(request.Data); err != nil { + return + } + + return response, v.UnmarshalIfComplete(http.MethodPut, fmt.Sprintf("/v1/bucket/%s/%s", request.Params.Bucket, request.Params.Document), &buf, true, &response) +} diff --git a/apiresponse/document.go b/apiresponse/document.go index b6518b9..488a65e 100644 --- a/apiresponse/document.go +++ b/apiresponse/document.go @@ -25,3 +25,11 @@ type V1DocumentPOST struct { type V1DocumentDELETE struct { Response } + +type V1DocumentKeyPUT struct { + Response + Data struct { + Bucket string + Key string + } +} diff --git a/cmd/server.go b/cmd/server.go index fc7f54b..1028f89 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -224,6 +224,13 @@ func RunServer() { log.Fatal(err) } + if err := pave.EchoRegister[ + apirequest.V1DocumentKeyPUT, + apiresponse.V1DocumentKeyPUT](groupV1, &p, "/v1", http.MethodPut, "/:bucket/:document", "Renommer un document", "V1DocumentKeyPUT", v1Handler.V1DocumentKeyPUT); err != nil { + + log.Fatal(err) + } + if err := pave.EchoRegister[ apirequest.V1SpecGET, apiresponse.V1SpecGET](groupV1, &p, "/v1", http.MethodGet, "/spec", apihandler.DescriptionV1SpecGET, "V1SpecGET", v1Handler.V1SpecGET); err != nil { From ef75eb29fab393c8ce1dee6ca42674ed02a0505b Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Thu, 1 Feb 2024 17:38:48 -0500 Subject: [PATCH 2/2] =?UTF-8?q?feat(api):=20Impl=C3=A9menter=20`V1Document?= =?UTF-8?q?KeyPUT`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit N'inclue pas de méthodes associées au API client --- apihandler/document.go | 47 ++++++++++++++++++++++++++++++++++-------- cmd/server.go | 2 +- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/apihandler/document.go b/apihandler/document.go index 284add4..b7fecd0 100644 --- a/apihandler/document.go +++ b/apihandler/document.go @@ -285,7 +285,15 @@ func (h *V1Handler) V1DocumentKeyPUT(c echo.Context) (err error) { bucket := c.Param("bucket") document := c.Param("document") - newKey := c.Bind( + + var newKey string + err = c.Bind(&newKey) + if err != nil { + response.StatusCode = http.StatusBadRequest + response.Message = err.Error() + + return c.JSON(response.StatusCode, response) + } request, err = apirequest.NewV1DocumentKeyPUT(bucket, document, newKey) if err != nil { @@ -316,7 +324,7 @@ func (h *V1Handler) V1DocumentKeyPUT(c echo.Context) (err error) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - bucketExists, err := h.MediaClient.MinioClient.BucketExists(ctx, bucket) + bucketExists, err := h.MediaClient.MinioClient.BucketExists(ctx, request.Params.Bucket) if err != nil { return c.JSON(http.StatusInternalServerError, "Could not validate bucket exists") } @@ -325,33 +333,54 @@ func (h *V1Handler) V1DocumentKeyPUT(c echo.Context) (err error) { return c.JSON(apiresponse.NotFoundResponse()) } - if _, err := h.MediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}); err != nil { + // Check source object exists + + if _, err := h.MediaClient.MinioClient.StatObject(ctx, request.Params.Bucket, request.Params.Document, minio.StatObjectOptions{}); err != nil { if err.Error() == "The specified key does not exist." { return c.JSON(apiresponse.NotFoundResponse()) } - response.Message = fmt.Sprintf("Could not obtain information on %s/%s", bucket, document) + response.Message = fmt.Sprintf("Could not obtain information on %s/%s", request.Params.Bucket, request.Params.Document) response.StatusCode = http.StatusInternalServerError return c.JSON(response.StatusCode, response) } + // Copy object to newKey + if _, err := h.MediaClient.MinioClient.CopyObject(ctx, - minio.CopyDestOptions{Bucket: bucket, Object: newKey}, - minio.CopySrcOptions{Bucket: bucket, Object: document}, + minio.CopyDestOptions{Bucket: request.Params.Bucket, Object: request.Data.NewKey}, + minio.CopySrcOptions{Bucket: request.Params.Bucket, Object: request.Params.Document}, ); err != nil { response.StatusCode = http.StatusInternalServerError response.Message = "Impossible de copier un document pour le renommer" return c.JSON(response.StatusCode, response) } - //TODO verify copy was successful + // Verify copy was successful + if _, err := h.MediaClient.MinioClient.StatObject(ctx, + request.Params.Bucket, request.Data.NewKey, minio.GetObjectOptions{}); err != nil { + response.StatusCode = http.StatusInternalServerError + response.Message = "Copie de document ne semble pas avoir fonctionnée" + return c.JSON(response.StatusCode, response) + } - //TODO delete old file + // Delete old file + if err := h.MediaClient.MinioClient.RemoveObject(ctx, + request.Params.Bucket, request.Params.Document, minio.RemoveObjectOptions{}); err != nil { + response.StatusCode = http.StatusInternalServerError + response.Message = "Erreur pendant tentative de supprimer document source" + return c.JSON(response.StatusCode, response) + } //TODO cleanup - //TODO return result + // Return result + response.StatusCode = http.StatusOK + response.Message = "Document renommé avec succès" + response.Data.Bucket = request.Params.Bucket + response.Data.Key = request.Data.NewKey + return c.JSON(response.StatusCode, response) } diff --git a/cmd/server.go b/cmd/server.go index 1028f89..b528664 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -226,7 +226,7 @@ func RunServer() { if err := pave.EchoRegister[ apirequest.V1DocumentKeyPUT, - apiresponse.V1DocumentKeyPUT](groupV1, &p, "/v1", http.MethodPut, "/:bucket/:document", "Renommer un document", "V1DocumentKeyPUT", v1Handler.V1DocumentKeyPUT); err != nil { + apiresponse.V1DocumentKeyPUT](groupV1, &p, "/v1", http.MethodPut, "/bucket/:bucket/:document/key", "Renommer un document", "V1DocumentKeyPUT", v1Handler.V1DocumentKeyPUT); err != nil { log.Fatal(err) }