From 7bf489315e5433577877afddbcc83ba39566b381 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 15:13:42 -0500 Subject: [PATCH 01/17] feat(api): add pave spec to route /v1/spec and add seed to it Exposes the API spec in pave format, which intends to show information about all API routes. Also pave V1SeedPOST and V1SpecGET --- Dockerfile | 2 ++ api/api.go | 16 +++++++++++++--- apihandler/apihandler.go | 10 ++++++---- apihandler/spec.go | 36 ++++++++++++++++++++++++++++++++++++ apirequest/seed.go | 29 +++++++++++++++++++++++++++++ apirequest/spec.go | 29 +++++++++++++++++++++++++++++ apiresponse/apiresponse.go | 16 ++++------------ apiresponse/bucket.go | 4 ++-- apiresponse/document.go | 2 +- apiresponse/seed.go | 2 +- apiresponse/spec.go | 8 ++++++++ cmd/server.go | 18 +++++++++++++++++- go.mod | 1 + go.sum | 2 ++ webhandler/webhandler.go | 2 +- 15 files changed, 152 insertions(+), 25 deletions(-) create mode 100644 apihandler/spec.go create mode 100644 apirequest/seed.go create mode 100644 apirequest/spec.go create mode 100644 apiresponse/spec.go diff --git a/Dockerfile b/Dockerfile index 3b53c4a..6210ca7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,6 +16,8 @@ ADD api/ api/ ADD apihandler/ apihandler/ +ADD apirequest/ apirequest/ + ADD apiresponse/ apiresponse/ ADD config/ config/ diff --git a/api/api.go b/api/api.go index 9e8fbf7..15eb710 100644 --- a/api/api.go +++ b/api/api.go @@ -10,6 +10,7 @@ import ( "net/url" "codeberg.org/vlbeaudoin/voki" + "git.agecem.com/agecem/agecem-org/apirequest" "git.agecem.com/agecem/agecem-org/apiresponse" "git.agecem.com/agecem/agecem-org/config" "github.com/spf13/viper" @@ -35,8 +36,8 @@ func New(client *http.Client, host, key string, port int, protocol string) (*API return &API{Voki: voki.New(client, host, key, port, protocol)}, nil } -func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) (apiresponse.V1DocumentCreateResponse, error) { - var response apiresponse.V1DocumentCreateResponse +func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) (apiresponse.V1DocumentCreate, error) { + var response apiresponse.V1DocumentCreate endpoint := fmt.Sprintf("%s://%s:%d", a.Voki.Protocol, a.Voki.Host, @@ -100,6 +101,15 @@ func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) ( return response, err } -func (a *API) ListBuckets() (response apiresponse.V1BucketListResponse, err error) { +func (a *API) ListBuckets() (response apiresponse.V1BucketList, err error) { return response, a.Voki.Unmarshal(http.MethodGet, "/v1/bucket", nil, true, &response) } + +func (a *API) Seed() (response apiresponse.V1SeedPOST, err error) { + request, err := apirequest.NewV1SeedPOST() + if err != nil { + return + } + + return request.Request(a.Voki) +} diff --git a/apihandler/apihandler.go b/apihandler/apihandler.go index d3d170f..2e4fc37 100644 --- a/apihandler/apihandler.go +++ b/apihandler/apihandler.go @@ -5,6 +5,7 @@ import ( "net/http" "sort" + "codeberg.org/vlbeaudoin/pave" "git.agecem.com/agecem/agecem-org/apiresponse" "git.agecem.com/agecem/agecem-org/config" "git.agecem.com/agecem/agecem-org/media" @@ -15,6 +16,7 @@ import ( type V1Handler struct { Config config.Config MediaClient *media.MediaClient + Pave *pave.Pave } // API Handlers @@ -30,7 +32,7 @@ func (h *V1Handler) HandleV1(c echo.Context) error { // HandleV1Seed créé des buckets dans minio selon la liste de buckets dans server.documents.buckets // Les buckets sont créés avec paramètres par défaut, et sont ensuite visible dans /v1/bucket. func (h *V1Handler) HandleV1Seed(c echo.Context) error { - var response apiresponse.V1SeedResponse + var response apiresponse.V1SeedPOST new_buckets, err := h.MediaClient.Seed() response.Data.Buckets = new_buckets @@ -56,7 +58,7 @@ func (h *V1Handler) HandleV1Seed(c echo.Context) error { // HandleV1BucketList affiche les buckets permis par server.documents.buckets, qui existent. func (h *V1Handler) HandleV1BucketList(c echo.Context) error { - var response apiresponse.V1BucketListResponse + var response apiresponse.V1BucketList var buckets = make(map[string]string) @@ -83,7 +85,7 @@ func (h *V1Handler) HandleV1BucketList(c echo.Context) error { } func (h *V1Handler) HandleV1BucketRead(c echo.Context) error { - var response apiresponse.V1BucketReadResponse + var response apiresponse.V1BucketRead bucket := c.Param("bucket") @@ -137,7 +139,7 @@ func (h *V1Handler) HandleV1BucketRead(c echo.Context) error { // HandleV1DocumentCreate permet d'ajouter un object dans un bucket, par multipart/form-data func (h *V1Handler) HandleV1DocumentCreate(c echo.Context) error { - var response apiresponse.V1DocumentCreateResponse + var response apiresponse.V1DocumentCreate bucket := c.Param("bucket") diff --git a/apihandler/spec.go b/apihandler/spec.go new file mode 100644 index 0000000..de394eb --- /dev/null +++ b/apihandler/spec.go @@ -0,0 +1,36 @@ +package apihandler + +import ( + "fmt" + "net/http" + + "git.agecem.com/agecem/agecem-org/apirequest" + "git.agecem.com/agecem/agecem-org/apiresponse" + "git.agecem.com/agecem/agecem-org/version" + "github.com/labstack/echo/v4" +) + +const DescriptionV1SpecGET string = "Afficher le API spec en format pave" + +func (h *V1Handler) HandleV1Spec(c echo.Context) error { + var request apirequest.V1SpecGET + var response apiresponse.V1SpecGET + + if !request.Complete() { + response.Message = "Incomplete V1SpecGET request received" + response.StatusCode = http.StatusBadRequest + + return c.JSON(response.StatusCode, response) + } + + response.Data.Spec = fmt.Sprintf("# pave spec for agecem-org %s", version.Version()) + + for _, route := range h.Pave.SortedRouteStrings() { + response.Data.Spec = fmt.Sprintf("%s%s", response.Data.Spec, route) + } + + response.Message = "ok" + response.StatusCode = http.StatusOK + + return c.JSON(response.StatusCode, response) +} diff --git a/apirequest/seed.go b/apirequest/seed.go new file mode 100644 index 0000000..89187d7 --- /dev/null +++ b/apirequest/seed.go @@ -0,0 +1,29 @@ +package apirequest + +import ( + "fmt" + "net/http" + + "codeberg.org/vlbeaudoin/voki" + "codeberg.org/vlbeaudoin/voki/request" + "git.agecem.com/agecem/agecem-org/apiresponse" +) + +var _ request.Requester[apiresponse.V1SeedPOST] = V1SeedPOST{} + +type V1SeedPOST struct{} + +func NewV1SeedPOST() (request V1SeedPOST, err error) { + return +} + +func (r V1SeedPOST) Complete() bool { return true } + +func (r V1SeedPOST) Request(v *voki.Voki) (response apiresponse.V1SeedPOST, err error) { + if !r.Complete() { + err = fmt.Errorf("Incomplete V1SeedPOST") + return + } + + return response, v.UnmarshalIfComplete(http.MethodPost, "/v1/seed", nil, true, &response) +} diff --git a/apirequest/spec.go b/apirequest/spec.go new file mode 100644 index 0000000..b2aaa4e --- /dev/null +++ b/apirequest/spec.go @@ -0,0 +1,29 @@ +package apirequest + +import ( + "fmt" + "net/http" + + "codeberg.org/vlbeaudoin/voki" + "codeberg.org/vlbeaudoin/voki/request" + "git.agecem.com/agecem/agecem-org/apiresponse" +) + +var _ request.Requester[apiresponse.V1SpecGET] = V1SpecGET{} + +type V1SpecGET struct{} + +func NewV1SpecGET() (request V1SpecGET, err error) { + return +} + +func (request V1SpecGET) Complete() bool { return true } + +func (request V1SpecGET) Request(v *voki.Voki) (response apiresponse.V1SpecGET, err error) { + if !request.Complete() { + err = fmt.Errorf("Incomplete V1SpecGET") + return + } + + return response, v.UnmarshalIfComplete(http.MethodGet, "/v1/spec", nil, true, &response) +} diff --git a/apiresponse/apiresponse.go b/apiresponse/apiresponse.go index d4e98da..89adb8b 100644 --- a/apiresponse/apiresponse.go +++ b/apiresponse/apiresponse.go @@ -2,27 +2,19 @@ package apiresponse import ( "net/http" + + "codeberg.org/vlbeaudoin/voki/response" ) -type Responder interface { - Respond() Responder -} - type Response struct { - StatusCode int `json:"status_code"` - Message string - Error string -} - -func (r Response) Respond() Responder { - return r + response.ResponseWithError } type SimpleResponse struct { Message string } -func (r SimpleResponse) Respond() Responder { +func (r SimpleResponse) Respond() response.Responder { return r } diff --git a/apiresponse/bucket.go b/apiresponse/bucket.go index 02c26a3..476fcf1 100644 --- a/apiresponse/bucket.go +++ b/apiresponse/bucket.go @@ -1,13 +1,13 @@ package apiresponse -type V1BucketListResponse struct { +type V1BucketList struct { Response Data struct { Buckets map[string]string } } -type V1BucketReadResponse struct { +type V1BucketRead struct { Response Data struct { Keys []string diff --git a/apiresponse/document.go b/apiresponse/document.go index 9e1677b..3e99d70 100644 --- a/apiresponse/document.go +++ b/apiresponse/document.go @@ -1,6 +1,6 @@ package apiresponse -type V1DocumentCreateResponse struct { +type V1DocumentCreate struct { Response Data struct { Bucket string diff --git a/apiresponse/seed.go b/apiresponse/seed.go index 858beab..b266c5a 100644 --- a/apiresponse/seed.go +++ b/apiresponse/seed.go @@ -1,6 +1,6 @@ package apiresponse -type V1SeedResponse struct { +type V1SeedPOST struct { Response Data struct { Buckets []string diff --git a/apiresponse/spec.go b/apiresponse/spec.go new file mode 100644 index 0000000..d9e87c7 --- /dev/null +++ b/apiresponse/spec.go @@ -0,0 +1,8 @@ +package apiresponse + +type V1SpecGET struct { + Response + Data struct { + Spec string + } +} diff --git a/cmd/server.go b/cmd/server.go index 1380154..073c447 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -13,12 +13,15 @@ import ( "io" "net/http" + "codeberg.org/vlbeaudoin/pave" "codeberg.org/vlbeaudoin/serpents" "github.com/spf13/cobra" "github.com/spf13/viper" "git.agecem.com/agecem/agecem-org/api" "git.agecem.com/agecem/agecem-org/apihandler" + "git.agecem.com/agecem/agecem-org/apirequest" + "git.agecem.com/agecem/agecem-org/apiresponse" "git.agecem.com/agecem/agecem-org/config" "git.agecem.com/agecem/agecem-org/media" "git.agecem.com/agecem/agecem-org/public" @@ -205,14 +208,27 @@ func RunServer() { log.Fatal("Error during NewMediaClientFromViper for API handlers") } + p := pave.New() + v1Handler := apihandler.V1Handler{ Config: cfg, MediaClient: mediaClient, + Pave: &p, } groupV1.GET("", v1Handler.HandleV1) - groupV1.POST("/seed", v1Handler.HandleV1Seed) + if err := pave.EchoRegister[ + apirequest.V1SeedPOST, + apiresponse.V1SeedPOST](groupV1, &p, "/v1", http.MethodPost, "/seed", "Créer buckets manquants définis dans `server.documents.buckets`", "V1SeedPOST", v1Handler.HandleV1Seed); err != nil { + log.Fatal(err) + } + + if err := pave.EchoRegister[ + apirequest.V1SpecGET, + apiresponse.V1SpecGET](groupV1, &p, "/v1", http.MethodGet, "/spec", apihandler.DescriptionV1SpecGET, "V1SpecGET", v1Handler.HandleV1Spec); err != nil { + log.Fatal(err) + } groupV1.GET("/bucket", v1Handler.HandleV1BucketList) diff --git a/go.mod b/go.mod index 999b68e..40534c0 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module git.agecem.com/agecem/agecem-org go 1.21.1 require ( + codeberg.org/vlbeaudoin/pave v1.0.1 codeberg.org/vlbeaudoin/serpents v1.1.0 codeberg.org/vlbeaudoin/voki v1.7.1 github.com/labstack/echo/v4 v4.11.3 diff --git a/go.sum b/go.sum index 27815d3..6826586 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +codeberg.org/vlbeaudoin/pave v1.0.1 h1:N7/TIb615By1nds5h+iKSZtPAFeuT3ceuUH/VG6t7Rw= +codeberg.org/vlbeaudoin/pave v1.0.1/go.mod h1:D/Lb/EmfJzl066A+2g4wc42e1Pb/l4nmXjIGouYBviM= codeberg.org/vlbeaudoin/serpents v1.1.0 h1:U9f2+2D1yUVHx90yePi2ZOLRLG/Wkoob4JXDIVyoBwA= codeberg.org/vlbeaudoin/serpents v1.1.0/go.mod h1:3bE/R0ToABwcUJtS1VcGEBa86K5FYhrZGAbFl2qL8kQ= codeberg.org/vlbeaudoin/voki v1.7.1 h1:Eywgk2A8NQmg4vucJjtheUpB0S2RYlDS8A7VwP+wFHU= diff --git a/webhandler/webhandler.go b/webhandler/webhandler.go index cec2667..83bcb0b 100644 --- a/webhandler/webhandler.go +++ b/webhandler/webhandler.go @@ -66,7 +66,7 @@ func (h *WebHandler) HandleDocumentation(c echo.Context) error { for bucket, displayName := range v1BucketListResponse.Data.Buckets { // TODO move call to dedicated API client method - var v1BucketReadResponse apiresponse.V1BucketReadResponse + var v1BucketReadResponse apiresponse.V1BucketRead if err = h.ApiClient.Voki.Unmarshal(http.MethodGet, fmt.Sprintf("/v1/bucket/%s", bucket), nil, true, &v1BucketReadResponse); err != nil { response.Error = err.Error() -- 2.45.2 From 8e40050f812ad5937d893ec1e8fc23cb18c8978d Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 15:26:11 -0500 Subject: [PATCH 02/17] =?UTF-8?q?change!:=20renommer=20apiresponse.V1Bucke?= =?UTF-8?q?tList=20=C3=A0=20V1BucketsGET?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fait partie d'une suite de changements pour normaliser noms d'objets BREAKING: renommer `V1BucketList` à `V1BucketsGET` --- api/api.go | 2 +- apihandler/apihandler.go | 6 +++--- apiresponse/bucket.go | 2 +- cmd/server.go | 2 +- webhandler/webhandler.go | 26 +++++++++++++------------- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/api/api.go b/api/api.go index 15eb710..c648440 100644 --- a/api/api.go +++ b/api/api.go @@ -101,7 +101,7 @@ func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) ( return response, err } -func (a *API) ListBuckets() (response apiresponse.V1BucketList, err error) { +func (a *API) ListBuckets() (response apiresponse.V1BucketsGET, err error) { return response, a.Voki.Unmarshal(http.MethodGet, "/v1/bucket", nil, true, &response) } diff --git a/apihandler/apihandler.go b/apihandler/apihandler.go index 2e4fc37..78c3fc2 100644 --- a/apihandler/apihandler.go +++ b/apihandler/apihandler.go @@ -56,9 +56,9 @@ func (h *V1Handler) HandleV1Seed(c echo.Context) error { return c.JSON(response.StatusCode, response) } -// HandleV1BucketList affiche les buckets permis par server.documents.buckets, qui existent. -func (h *V1Handler) HandleV1BucketList(c echo.Context) error { - var response apiresponse.V1BucketList +// V1BucketsGET affiche les buckets permis par server.documents.buckets, qui existent. +func (h *V1Handler) V1BucketsGET(c echo.Context) error { + var response apiresponse.V1BucketsGET var buckets = make(map[string]string) diff --git a/apiresponse/bucket.go b/apiresponse/bucket.go index 476fcf1..21df0f5 100644 --- a/apiresponse/bucket.go +++ b/apiresponse/bucket.go @@ -1,6 +1,6 @@ package apiresponse -type V1BucketList struct { +type V1BucketsGET struct { Response Data struct { Buckets map[string]string diff --git a/cmd/server.go b/cmd/server.go index 073c447..22ca33d 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -230,7 +230,7 @@ func RunServer() { log.Fatal(err) } - groupV1.GET("/bucket", v1Handler.HandleV1BucketList) + groupV1.GET("/bucket", v1Handler.V1BucketsGET) groupV1.GET("/bucket/:bucket", v1Handler.HandleV1BucketRead) diff --git a/webhandler/webhandler.go b/webhandler/webhandler.go index 83bcb0b..b8df36a 100644 --- a/webhandler/webhandler.go +++ b/webhandler/webhandler.go @@ -53,18 +53,18 @@ func HandleVieEtudianteOrganisme(c echo.Context) error { func (h *WebHandler) HandleDocumentation(c echo.Context) error { var response webresponse.HandleDocumentationResponse - v1BucketListResponse, err := h.ApiClient.ListBuckets() + v1BucketsGET, err := h.ApiClient.ListBuckets() if err != nil { response.Error = err.Error() - response.Message = v1BucketListResponse.Message - response.StatusCode = v1BucketListResponse.StatusCode + response.Message = v1BucketsGET.Message + response.StatusCode = v1BucketsGET.StatusCode return c.Render(response.StatusCode, "documentation-html", response) } - //TODO check v1BucketListRespone StatusCode and Error + //TODO check v1BucketsGET StatusCode and Error - for bucket, displayName := range v1BucketListResponse.Data.Buckets { + for bucket, displayName := range v1BucketsGET.Data.Buckets { // TODO move call to dedicated API client method var v1BucketReadResponse apiresponse.V1BucketRead @@ -134,16 +134,16 @@ func HandleAdmin(c echo.Context) error { func (h *WebHandler) HandleAdminDocumentsUpload(c echo.Context) error { var response webresponse.HandleAdminDocumentsUploadResponse - v1BucketListResponse, err := h.ApiClient.ListBuckets() + v1BucketsGET, err := h.ApiClient.ListBuckets() if err != nil { - response.StatusCode = v1BucketListResponse.StatusCode + response.StatusCode = v1BucketsGET.StatusCode response.Error = err.Error() - response.Message = v1BucketListResponse.Message + response.Message = v1BucketsGET.Message return c.Render(response.StatusCode, "admin-upload-html", nil) } - for bucketName, displayName := range v1BucketListResponse.Data.Buckets { + for bucketName, displayName := range v1BucketsGET.Data.Buckets { response.Data.Buckets = append(response.Data.Buckets, models.Bucket{ Name: bucketName, DisplayName: displayName, @@ -157,16 +157,16 @@ func (h *WebHandler) HandleAdminDocumentsUpload(c echo.Context) error { func (h *WebHandler) HandleAdminDocumentsUploadPOST(c echo.Context) error { var response webresponse.HandleAdminDocumentsUploadResponse - v1BucketListResponse, err := h.ApiClient.ListBuckets() + v1BucketsGET, err := h.ApiClient.ListBuckets() if err != nil { - response.StatusCode = v1BucketListResponse.StatusCode - response.Message = v1BucketListResponse.Message + response.StatusCode = v1BucketsGET.StatusCode + response.Message = v1BucketsGET.Message response.Error = err.Error() return c.Render(response.StatusCode, "admin-upload-html", response) } - for bucketName, displayName := range v1BucketListResponse.Data.Buckets { + for bucketName, displayName := range v1BucketsGET.Data.Buckets { response.Data.Buckets = append(response.Data.Buckets, models.Bucket{ Name: bucketName, DisplayName: displayName, -- 2.45.2 From ffd8109d275a09cfed1aa885dcf301b5aa66a20c Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 15:28:37 -0500 Subject: [PATCH 03/17] =?UTF-8?q?change!:=20renommer=20HandleV1Spec=20?= =?UTF-8?q?=C3=A0=20V1SpecGET?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING: renommer `HandleV1Spec` à `V1SpecGET` --- apihandler/spec.go | 2 +- cmd/server.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apihandler/spec.go b/apihandler/spec.go index de394eb..d638817 100644 --- a/apihandler/spec.go +++ b/apihandler/spec.go @@ -12,7 +12,7 @@ import ( const DescriptionV1SpecGET string = "Afficher le API spec en format pave" -func (h *V1Handler) HandleV1Spec(c echo.Context) error { +func (h *V1Handler) V1SpecGET(c echo.Context) error { var request apirequest.V1SpecGET var response apiresponse.V1SpecGET diff --git a/cmd/server.go b/cmd/server.go index 22ca33d..1dbc918 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -226,7 +226,7 @@ func RunServer() { if err := pave.EchoRegister[ apirequest.V1SpecGET, - apiresponse.V1SpecGET](groupV1, &p, "/v1", http.MethodGet, "/spec", apihandler.DescriptionV1SpecGET, "V1SpecGET", v1Handler.HandleV1Spec); err != nil { + apiresponse.V1SpecGET](groupV1, &p, "/v1", http.MethodGet, "/spec", apihandler.DescriptionV1SpecGET, "V1SpecGET", v1Handler.V1SpecGET); err != nil { log.Fatal(err) } -- 2.45.2 From c40f7eb9dc60a534d1ec5faf37be676d020003dd Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 15:30:08 -0500 Subject: [PATCH 04/17] =?UTF-8?q?refactor:=20d=C3=A9placer=20handlers=20bu?= =?UTF-8?q?cket=20vers=20apihandler/bucket.go?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apihandler/apihandler.go | 81 ----------------------------------- apihandler/bucket.go | 91 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 81 deletions(-) create mode 100644 apihandler/bucket.go diff --git a/apihandler/apihandler.go b/apihandler/apihandler.go index 78c3fc2..b810cc9 100644 --- a/apihandler/apihandler.go +++ b/apihandler/apihandler.go @@ -56,87 +56,6 @@ func (h *V1Handler) HandleV1Seed(c echo.Context) error { return c.JSON(response.StatusCode, response) } -// V1BucketsGET affiche les buckets permis par server.documents.buckets, qui existent. -func (h *V1Handler) V1BucketsGET(c echo.Context) error { - var response apiresponse.V1BucketsGET - - var buckets = make(map[string]string) - - for bucket_name, bucket_display_name := range h.Config.Server.Documents.Buckets { - exists, err := h.MediaClient.MinioClient.BucketExists(context.Background(), bucket_name) - if err != nil { - response.StatusCode = http.StatusInternalServerError - response.Message = "Error during minio#BucketExists" - // response.Error = err.Error() - - return c.JSON(response.StatusCode, response) - } - - if exists { - buckets[bucket_name] = bucket_display_name - } - } - - response.StatusCode = http.StatusOK - response.Message = "Buckets list successful" - response.Data.Buckets = buckets - - return c.JSON(response.StatusCode, response) -} - -func (h *V1Handler) HandleV1BucketRead(c echo.Context) error { - var response apiresponse.V1BucketRead - - bucket := c.Param("bucket") - - allowed := false - for bucket_allowed := range h.Config.Server.Documents.Buckets { - if bucket == bucket_allowed { - allowed = true - } - } - - if !allowed { - return c.JSON(apiresponse.NotFoundResponse()) - } - - ctx, cancel := context.WithCancel(context.Background()) - - defer cancel() - - exists, err := h.MediaClient.MinioClient.BucketExists(ctx, bucket) - if err != nil { - response.StatusCode = http.StatusInternalServerError - response.Message = "Error during minio#BucketExists" - response.Error = err.Error() - - return c.JSON(response.StatusCode, response) - } - - if !exists { - return c.JSON(apiresponse.NotFoundResponse()) - } - - objectCh := h.MediaClient.MinioClient.ListObjects(ctx, bucket, minio.ListObjectsOptions{}) - for object := range objectCh { - if object.Err != nil { - response.StatusCode = http.StatusInternalServerError - response.Message = "Error during minio#ListObjects" - //TODO make sure this is safe - //response.Error = object.Err.Error() - - return c.JSON(response.StatusCode, response) - } - - response.Data.Keys = append(response.Data.Keys, object.Key) - } - - response.StatusCode = http.StatusOK - response.Message = "V1BucketRead ok" - - return c.JSON(response.StatusCode, response) -} - // HandleV1DocumentCreate permet d'ajouter un object dans un bucket, par multipart/form-data func (h *V1Handler) HandleV1DocumentCreate(c echo.Context) error { var response apiresponse.V1DocumentCreate diff --git a/apihandler/bucket.go b/apihandler/bucket.go new file mode 100644 index 0000000..d6540db --- /dev/null +++ b/apihandler/bucket.go @@ -0,0 +1,91 @@ +package apihandler + +import ( + "context" + "net/http" + + "git.agecem.com/agecem/agecem-org/apiresponse" + "github.com/labstack/echo/v4" + "github.com/minio/minio-go/v7" +) + +// V1BucketsGET affiche les buckets permis par server.documents.buckets, qui existent. +func (h *V1Handler) V1BucketsGET(c echo.Context) error { + var response apiresponse.V1BucketsGET + + var buckets = make(map[string]string) + + for bucket_name, bucket_display_name := range h.Config.Server.Documents.Buckets { + exists, err := h.MediaClient.MinioClient.BucketExists(context.Background(), bucket_name) + if err != nil { + response.StatusCode = http.StatusInternalServerError + response.Message = "Error during minio#BucketExists" + // response.Error = err.Error() + + return c.JSON(response.StatusCode, response) + } + + if exists { + buckets[bucket_name] = bucket_display_name + } + } + + response.StatusCode = http.StatusOK + response.Message = "Buckets list successful" + response.Data.Buckets = buckets + + return c.JSON(response.StatusCode, response) +} + +func (h *V1Handler) HandleV1BucketRead(c echo.Context) error { + var response apiresponse.V1BucketRead + + bucket := c.Param("bucket") + + allowed := false + for bucket_allowed := range h.Config.Server.Documents.Buckets { + if bucket == bucket_allowed { + allowed = true + } + } + + if !allowed { + return c.JSON(apiresponse.NotFoundResponse()) + } + + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + + exists, err := h.MediaClient.MinioClient.BucketExists(ctx, bucket) + if err != nil { + response.StatusCode = http.StatusInternalServerError + response.Message = "Error during minio#BucketExists" + response.Error = err.Error() + + return c.JSON(response.StatusCode, response) + } + + if !exists { + return c.JSON(apiresponse.NotFoundResponse()) + } + + objectCh := h.MediaClient.MinioClient.ListObjects(ctx, bucket, minio.ListObjectsOptions{}) + for object := range objectCh { + if object.Err != nil { + response.StatusCode = http.StatusInternalServerError + response.Message = "Error during minio#ListObjects" + //TODO make sure this is safe + //response.Error = object.Err.Error() + + return c.JSON(response.StatusCode, response) + } + + response.Data.Keys = append(response.Data.Keys, object.Key) + } + + response.StatusCode = http.StatusOK + response.Message = "V1BucketRead ok" + + return c.JSON(response.StatusCode, response) +} -- 2.45.2 From d3dc5b15710a9a46777554ae9462aa394a657eba Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 15:33:07 -0500 Subject: [PATCH 05/17] =?UTF-8?q?change!:=20renommer=20HandleV1BucketRead?= =?UTF-8?q?=20=C3=A0=20V1BucketGET?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING: renommer `HandleV1BucketRead` à `V1BucketGET` --- apihandler/bucket.go | 4 ++-- apiresponse/bucket.go | 2 +- cmd/server.go | 2 +- webhandler/webhandler.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apihandler/bucket.go b/apihandler/bucket.go index d6540db..26ea6d1 100644 --- a/apihandler/bucket.go +++ b/apihandler/bucket.go @@ -37,8 +37,8 @@ func (h *V1Handler) V1BucketsGET(c echo.Context) error { return c.JSON(response.StatusCode, response) } -func (h *V1Handler) HandleV1BucketRead(c echo.Context) error { - var response apiresponse.V1BucketRead +func (h *V1Handler) V1BucketGET(c echo.Context) error { + var response apiresponse.V1BucketGET bucket := c.Param("bucket") diff --git a/apiresponse/bucket.go b/apiresponse/bucket.go index 21df0f5..3f4008f 100644 --- a/apiresponse/bucket.go +++ b/apiresponse/bucket.go @@ -7,7 +7,7 @@ type V1BucketsGET struct { } } -type V1BucketRead struct { +type V1BucketGET struct { Response Data struct { Keys []string diff --git a/cmd/server.go b/cmd/server.go index 1dbc918..1b93e1f 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -232,7 +232,7 @@ func RunServer() { groupV1.GET("/bucket", v1Handler.V1BucketsGET) - groupV1.GET("/bucket/:bucket", v1Handler.HandleV1BucketRead) + groupV1.GET("/bucket/:bucket", v1Handler.V1BucketGET) groupV1.POST("/bucket/:bucket", v1Handler.HandleV1DocumentCreate) diff --git a/webhandler/webhandler.go b/webhandler/webhandler.go index b8df36a..6d945c7 100644 --- a/webhandler/webhandler.go +++ b/webhandler/webhandler.go @@ -66,7 +66,7 @@ func (h *WebHandler) HandleDocumentation(c echo.Context) error { for bucket, displayName := range v1BucketsGET.Data.Buckets { // TODO move call to dedicated API client method - var v1BucketReadResponse apiresponse.V1BucketRead + var v1BucketReadResponse apiresponse.V1BucketGET if err = h.ApiClient.Voki.Unmarshal(http.MethodGet, fmt.Sprintf("/v1/bucket/%s", bucket), nil, true, &v1BucketReadResponse); err != nil { response.Error = err.Error() -- 2.45.2 From 9a8a0abe2ea0b802ed306efe004a6a3084b34370 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 15:36:27 -0500 Subject: [PATCH 06/17] =?UTF-8?q?change!:=20renommer=20HandleV1DocumentCre?= =?UTF-8?q?ate=20=C3=A0=20V1DocumentPOST?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING: renommer `HandleV1DocumentCreate` à `V1DocumentPOST` --- api/api.go | 4 +- apihandler/apihandler.go | 179 ------------------------------------- apihandler/document.go | 187 +++++++++++++++++++++++++++++++++++++++ apiresponse/document.go | 2 +- cmd/server.go | 2 +- 5 files changed, 191 insertions(+), 183 deletions(-) create mode 100644 apihandler/document.go diff --git a/api/api.go b/api/api.go index c648440..c1e49e3 100644 --- a/api/api.go +++ b/api/api.go @@ -36,8 +36,8 @@ func New(client *http.Client, host, key string, port int, protocol string) (*API return &API{Voki: voki.New(client, host, key, port, protocol)}, nil } -func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) (apiresponse.V1DocumentCreate, error) { - var response apiresponse.V1DocumentCreate +func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) (apiresponse.V1DocumentPOST, error) { + var response apiresponse.V1DocumentPOST endpoint := fmt.Sprintf("%s://%s:%d", a.Voki.Protocol, a.Voki.Host, diff --git a/apihandler/apihandler.go b/apihandler/apihandler.go index b810cc9..7118b3d 100644 --- a/apihandler/apihandler.go +++ b/apihandler/apihandler.go @@ -1,7 +1,6 @@ package apihandler import ( - "context" "net/http" "sort" @@ -10,7 +9,6 @@ import ( "git.agecem.com/agecem/agecem-org/config" "git.agecem.com/agecem/agecem-org/media" "github.com/labstack/echo/v4" - "github.com/minio/minio-go/v7" ) type V1Handler struct { @@ -55,180 +53,3 @@ func (h *V1Handler) HandleV1Seed(c echo.Context) error { return c.JSON(response.StatusCode, response) } - -// HandleV1DocumentCreate permet d'ajouter un object dans un bucket, par multipart/form-data -func (h *V1Handler) HandleV1DocumentCreate(c echo.Context) error { - var response apiresponse.V1DocumentCreate - - bucket := c.Param("bucket") - - form_file, err := c.FormFile("document") - if err != nil { - response.StatusCode = http.StatusBadRequest - response.Message = "Error during HandleV1DocumentCreate's echo#Context.FormFile" - response.Error = err.Error() - - return c.JSON(response.StatusCode, response) - } - - allowed := false - for bucket_allowed := range h.Config.Server.Documents.Buckets { - if bucket == bucket_allowed { - allowed = true - } - } - - if !allowed { - return c.JSON(apiresponse.NotFoundResponse()) - } - - ctx, cancel := context.WithCancel(context.Background()) - - defer cancel() - - src, err := form_file.Open() - if err != nil { - response.StatusCode = http.StatusBadRequest - response.Message = "Error during form_file.Open()" - response.Error = err.Error() - - return c.JSON(response.StatusCode, response) - } - defer src.Close() - - info, err := h.MediaClient.MinioClient.PutObject(ctx, bucket, form_file.Filename, src, form_file.Size, minio.PutObjectOptions{ - ContentType: form_file.Header.Get("Content-Type"), - }) - if err != nil { - response.StatusCode = http.StatusInternalServerError - response.Message = "Error during minio#PutObject" - //response.Error = err.Error() - - return c.JSON(response.StatusCode, response) - } - - response.StatusCode = http.StatusOK - response.Message = "ok" - response.Data.Bucket = info.Bucket - response.Data.Key = info.Key - response.Data.Size = info.Size - - return c.JSON(response.StatusCode, response) -} - -// HandleV1DocumentRead permet de lire le contenu d'un fichier et protentiellement de le télécharger -func (h *V1Handler) HandleV1DocumentRead(c echo.Context) error { - bucket := c.Param("bucket") - document := c.Param("document") - - allowed := false - for bucket_allowed := range h.Config.Server.Documents.Buckets { - if bucket == bucket_allowed { - allowed = true - } - } - - if !allowed { - return c.JSON(apiresponse.NotFoundResponse()) - } - - ctx, cancel := context.WithCancel(context.Background()) - - defer cancel() - - bucket_exists, err := h.MediaClient.MinioClient.BucketExists(ctx, bucket) - if err != nil { - return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") - } - - if !bucket_exists { - return c.JSON(apiresponse.NotFoundResponse()) - } - - document_info, err := h.MediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) - - if err != nil { - if err.Error() == "The specified key does not exist." { - - return c.JSON(apiresponse.NotFoundResponse()) - } - - return c.JSON(http.StatusInternalServerError, map[string]interface{}{ - "message": "Error during minio#StatObject", - }) - } - - _ = document_info - - document_object, err := h.MediaClient.MinioClient.GetObject(ctx, bucket, document, minio.GetObjectOptions{}) - if err != nil { - return c.JSON(http.StatusInternalServerError, map[string]string{ - "message": "Error during minio#GetObject", - }) - } - - defer document_object.Close() - - return c.Stream(http.StatusOK, document_info.ContentType, document_object) -} - -// HandleV1DocumentUpdate permet de mettre à jour certains champs d'un object, comme le Content-Type ou le Filename -func (h *V1Handler) HandleV1DocumentUpdate(c echo.Context) error { - return c.JSON(apiresponse.NotImplementedResponse()) -} - -// HandleV1DocumentDelete permet de supprimer un object -func (h *V1Handler) HandleV1DocumentDelete(c echo.Context) error { - bucket := c.Param("bucket") - document := c.Param("document") - - allowed := false - for bucket_allowed := range h.Config.Server.Documents.Buckets { - if bucket == bucket_allowed { - allowed = true - } - } - - if !allowed { - return c.JSON(apiresponse.NotFoundResponse()) - } - - ctx, cancel := context.WithCancel(context.Background()) - - defer cancel() - - bucket_exists, err := h.MediaClient.MinioClient.BucketExists(ctx, bucket) - if err != nil { - return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") - } - - if !bucket_exists { - return c.JSON(apiresponse.NotFoundResponse()) - } - - document_info, err := h.MediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) - if err != nil { - if err.Error() == "The specified key does not exist." { - - return c.JSON(apiresponse.NotFoundResponse()) - } - - return c.JSON(http.StatusInternalServerError, map[string]interface{}{ - "message": "Error during minio#StatObject", - }) - } - - //TODO Add error validation - _ = document_info - - err = h.MediaClient.MinioClient.RemoveObject(ctx, bucket, document, minio.RemoveObjectOptions{}) - if err != nil { - return c.JSON(http.StatusInternalServerError, map[string]string{ - "message": "Error during minio#RemoveObject", - }) - } - - return c.JSON(http.StatusOK, map[string]string{ - "message": "Document deleted", - }) -} diff --git a/apihandler/document.go b/apihandler/document.go new file mode 100644 index 0000000..60f9b6c --- /dev/null +++ b/apihandler/document.go @@ -0,0 +1,187 @@ +package apihandler + +import ( + "context" + "net/http" + + "git.agecem.com/agecem/agecem-org/apiresponse" + "github.com/labstack/echo/v4" + "github.com/minio/minio-go/v7" +) + +// V1DocumentPOST permet d'ajouter un object dans un bucket, par multipart/form-data +func (h *V1Handler) V1DocumentPOST(c echo.Context) error { + var response apiresponse.V1DocumentPOST + + bucket := c.Param("bucket") + + form_file, err := c.FormFile("document") + if err != nil { + response.StatusCode = http.StatusBadRequest + response.Message = "Error during HandleV1DocumentCreate's echo#Context.FormFile" + response.Error = err.Error() + + return c.JSON(response.StatusCode, response) + } + + allowed := false + for bucket_allowed := range h.Config.Server.Documents.Buckets { + if bucket == bucket_allowed { + allowed = true + } + } + + if !allowed { + return c.JSON(apiresponse.NotFoundResponse()) + } + + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + + src, err := form_file.Open() + if err != nil { + response.StatusCode = http.StatusBadRequest + response.Message = "Error during form_file.Open()" + response.Error = err.Error() + + return c.JSON(response.StatusCode, response) + } + defer src.Close() + + info, err := h.MediaClient.MinioClient.PutObject(ctx, bucket, form_file.Filename, src, form_file.Size, minio.PutObjectOptions{ + ContentType: form_file.Header.Get("Content-Type"), + }) + if err != nil { + response.StatusCode = http.StatusInternalServerError + response.Message = "Error during minio#PutObject" + //response.Error = err.Error() + + return c.JSON(response.StatusCode, response) + } + + response.StatusCode = http.StatusOK + response.Message = "ok" + response.Data.Bucket = info.Bucket + response.Data.Key = info.Key + response.Data.Size = info.Size + + return c.JSON(response.StatusCode, response) +} + +// HandleV1DocumentRead permet de lire le contenu d'un fichier et protentiellement de le télécharger +func (h *V1Handler) HandleV1DocumentRead(c echo.Context) error { + bucket := c.Param("bucket") + document := c.Param("document") + + allowed := false + for bucket_allowed := range h.Config.Server.Documents.Buckets { + if bucket == bucket_allowed { + allowed = true + } + } + + if !allowed { + return c.JSON(apiresponse.NotFoundResponse()) + } + + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + + bucket_exists, err := h.MediaClient.MinioClient.BucketExists(ctx, bucket) + if err != nil { + return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") + } + + if !bucket_exists { + return c.JSON(apiresponse.NotFoundResponse()) + } + + document_info, err := h.MediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) + + if err != nil { + if err.Error() == "The specified key does not exist." { + + return c.JSON(apiresponse.NotFoundResponse()) + } + + return c.JSON(http.StatusInternalServerError, map[string]interface{}{ + "message": "Error during minio#StatObject", + }) + } + + _ = document_info + + document_object, err := h.MediaClient.MinioClient.GetObject(ctx, bucket, document, minio.GetObjectOptions{}) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during minio#GetObject", + }) + } + + defer document_object.Close() + + return c.Stream(http.StatusOK, document_info.ContentType, document_object) +} + +// HandleV1DocumentUpdate permet de mettre à jour certains champs d'un object, comme le Content-Type ou le Filename +func (h *V1Handler) HandleV1DocumentUpdate(c echo.Context) error { + return c.JSON(apiresponse.NotImplementedResponse()) +} + +// HandleV1DocumentDelete permet de supprimer un object +func (h *V1Handler) HandleV1DocumentDelete(c echo.Context) error { + bucket := c.Param("bucket") + document := c.Param("document") + + allowed := false + for bucket_allowed := range h.Config.Server.Documents.Buckets { + if bucket == bucket_allowed { + allowed = true + } + } + + if !allowed { + return c.JSON(apiresponse.NotFoundResponse()) + } + + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + + bucket_exists, err := h.MediaClient.MinioClient.BucketExists(ctx, bucket) + if err != nil { + return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") + } + + if !bucket_exists { + return c.JSON(apiresponse.NotFoundResponse()) + } + + document_info, err := h.MediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) + if err != nil { + if err.Error() == "The specified key does not exist." { + + return c.JSON(apiresponse.NotFoundResponse()) + } + + return c.JSON(http.StatusInternalServerError, map[string]interface{}{ + "message": "Error during minio#StatObject", + }) + } + + //TODO Add error validation + _ = document_info + + err = h.MediaClient.MinioClient.RemoveObject(ctx, bucket, document, minio.RemoveObjectOptions{}) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during minio#RemoveObject", + }) + } + + return c.JSON(http.StatusOK, map[string]string{ + "message": "Document deleted", + }) +} diff --git a/apiresponse/document.go b/apiresponse/document.go index 3e99d70..75c15e3 100644 --- a/apiresponse/document.go +++ b/apiresponse/document.go @@ -1,6 +1,6 @@ package apiresponse -type V1DocumentCreate struct { +type V1DocumentPOST struct { Response Data struct { Bucket string diff --git a/cmd/server.go b/cmd/server.go index 1b93e1f..ae431fb 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -234,7 +234,7 @@ func RunServer() { groupV1.GET("/bucket/:bucket", v1Handler.V1BucketGET) - groupV1.POST("/bucket/:bucket", v1Handler.HandleV1DocumentCreate) + groupV1.POST("/bucket/:bucket", v1Handler.V1DocumentPOST) groupV1.GET("/bucket/:bucket/:document", v1Handler.HandleV1DocumentRead) -- 2.45.2 From a54fff11927a0e2bed2318ecfe1e2a09113344c3 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 15:40:52 -0500 Subject: [PATCH 07/17] =?UTF-8?q?change!:=20renommer=20HandleV1=20=C3=A0?= =?UTF-8?q?=20V1GET?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING: renommer `HandleV1` à `V1GET` --- apihandler/apihandler.go | 4 ++-- apihandler/document.go | 12 ++++++------ cmd/server.go | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/apihandler/apihandler.go b/apihandler/apihandler.go index 7118b3d..a14183c 100644 --- a/apihandler/apihandler.go +++ b/apihandler/apihandler.go @@ -19,9 +19,9 @@ type V1Handler struct { // API Handlers -// HandleV1 affiche les routes accessibles. +// V1GET affiche les routes accessibles. // Les routes sont triées selon .Path, pour les rendre plus facilement navigables. -func (h *V1Handler) HandleV1(c echo.Context) error { +func (h *V1Handler) V1GET(c echo.Context) error { routes := c.Echo().Routes() sort.Slice(routes, func(i, j int) bool { return routes[i].Path < routes[j].Path }) return c.JSON(http.StatusOK, routes) diff --git a/apihandler/document.go b/apihandler/document.go index 60f9b6c..a93bcb4 100644 --- a/apihandler/document.go +++ b/apihandler/document.go @@ -69,8 +69,8 @@ func (h *V1Handler) V1DocumentPOST(c echo.Context) error { return c.JSON(response.StatusCode, response) } -// HandleV1DocumentRead permet de lire le contenu d'un fichier et protentiellement de le télécharger -func (h *V1Handler) HandleV1DocumentRead(c echo.Context) error { +// V1DocumentGET permet de lire le contenu d'un fichier et protentiellement de le télécharger +func (h *V1Handler) V1DocumentGET(c echo.Context) error { bucket := c.Param("bucket") document := c.Param("document") @@ -125,13 +125,13 @@ func (h *V1Handler) HandleV1DocumentRead(c echo.Context) error { return c.Stream(http.StatusOK, document_info.ContentType, document_object) } -// HandleV1DocumentUpdate permet de mettre à jour certains champs d'un object, comme le Content-Type ou le Filename -func (h *V1Handler) HandleV1DocumentUpdate(c echo.Context) error { +// V1DocumentPUT permet de mettre à jour certains champs d'un object, comme le Content-Type ou le Filename +func (h *V1Handler) V1DocumentPUT(c echo.Context) error { return c.JSON(apiresponse.NotImplementedResponse()) } -// HandleV1DocumentDelete permet de supprimer un object -func (h *V1Handler) HandleV1DocumentDelete(c echo.Context) error { +// V1DocumentDELETE permet de supprimer un object +func (h *V1Handler) V1DocumentDELETE(c echo.Context) error { bucket := c.Param("bucket") document := c.Param("document") diff --git a/cmd/server.go b/cmd/server.go index ae431fb..5cf9294 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -216,7 +216,7 @@ func RunServer() { Pave: &p, } - groupV1.GET("", v1Handler.HandleV1) + groupV1.GET("", v1Handler.V1GET) if err := pave.EchoRegister[ apirequest.V1SeedPOST, @@ -236,11 +236,11 @@ func RunServer() { groupV1.POST("/bucket/:bucket", v1Handler.V1DocumentPOST) - groupV1.GET("/bucket/:bucket/:document", v1Handler.HandleV1DocumentRead) + groupV1.GET("/bucket/:bucket/:document", v1Handler.V1DocumentGET) - groupV1.PUT("/bucket/:bucket/:document", v1Handler.HandleV1DocumentUpdate) + groupV1.PUT("/bucket/:bucket/:document", v1Handler.V1DocumentPUT) - groupV1.DELETE("/bucket/:bucket/:document", v1Handler.HandleV1DocumentDelete) + groupV1.DELETE("/bucket/:bucket/:document", v1Handler.V1DocumentDELETE) // HTML Routes client := http.DefaultClient -- 2.45.2 From 5c56ededc864e9d381f8bb737de460f0dcb2014b Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 15:41:40 -0500 Subject: [PATCH 08/17] =?UTF-8?q?change!:=20renommer=20HandleV1Seed=20?= =?UTF-8?q?=C3=A0=20V1SeedPOST?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING: renommer `HandleV1Seed` à `V1SeedPOST` --- apihandler/apihandler.go | 4 ++-- cmd/server.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apihandler/apihandler.go b/apihandler/apihandler.go index a14183c..c9684ce 100644 --- a/apihandler/apihandler.go +++ b/apihandler/apihandler.go @@ -27,9 +27,9 @@ func (h *V1Handler) V1GET(c echo.Context) error { return c.JSON(http.StatusOK, routes) } -// HandleV1Seed créé des buckets dans minio selon la liste de buckets dans server.documents.buckets +// V1SeedPOST créé des buckets dans minio selon la liste de buckets dans server.documents.buckets // Les buckets sont créés avec paramètres par défaut, et sont ensuite visible dans /v1/bucket. -func (h *V1Handler) HandleV1Seed(c echo.Context) error { +func (h *V1Handler) V1SeedPOST(c echo.Context) error { var response apiresponse.V1SeedPOST new_buckets, err := h.MediaClient.Seed() diff --git a/cmd/server.go b/cmd/server.go index 5cf9294..16989e8 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -220,7 +220,7 @@ func RunServer() { if err := pave.EchoRegister[ apirequest.V1SeedPOST, - apiresponse.V1SeedPOST](groupV1, &p, "/v1", http.MethodPost, "/seed", "Créer buckets manquants définis dans `server.documents.buckets`", "V1SeedPOST", v1Handler.HandleV1Seed); err != nil { + apiresponse.V1SeedPOST](groupV1, &p, "/v1", http.MethodPost, "/seed", "Créer buckets manquants définis dans `server.documents.buckets`", "V1SeedPOST", v1HandleV1SeedPOSTed); err != nil { log.Fatal(err) } -- 2.45.2 From 8e03ba001fb50d3a7bc0ff0052cca97ead7c1121 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 15:53:00 -0500 Subject: [PATCH 09/17] fix: typo dans EchoRegister V1SeedPOST --- cmd/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/server.go b/cmd/server.go index 16989e8..af1d138 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -220,7 +220,7 @@ func RunServer() { if err := pave.EchoRegister[ apirequest.V1SeedPOST, - apiresponse.V1SeedPOST](groupV1, &p, "/v1", http.MethodPost, "/seed", "Créer buckets manquants définis dans `server.documents.buckets`", "V1SeedPOST", v1HandleV1SeedPOSTed); err != nil { + apiresponse.V1SeedPOST](groupV1, &p, "/v1", http.MethodPost, "/seed", "Créer buckets manquants définis dans `server.documents.buckets`", "V1SeedPOST", v1Handler.V1SeedPOST); err != nil { log.Fatal(err) } -- 2.45.2 From 787cb578a19d52d5336623fb8981ea0d13ba15e9 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 15:53:17 -0500 Subject: [PATCH 10/17] =?UTF-8?q?fix:=20ajouter=20retour=20de=20ligne=20ma?= =?UTF-8?q?nquant=20=C3=A0=20pave=20spec?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apihandler/spec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apihandler/spec.go b/apihandler/spec.go index d638817..20985fb 100644 --- a/apihandler/spec.go +++ b/apihandler/spec.go @@ -23,7 +23,7 @@ func (h *V1Handler) V1SpecGET(c echo.Context) error { return c.JSON(response.StatusCode, response) } - response.Data.Spec = fmt.Sprintf("# pave spec for agecem-org %s", version.Version()) + response.Data.Spec = fmt.Sprintf("# pave spec for agecem-org %s\n", version.Version()) for _, route := range h.Pave.SortedRouteStrings() { response.Data.Spec = fmt.Sprintf("%s%s", response.Data.Spec, route) -- 2.45.2 From d70b0643e894e2b278193bf501b9f1471e51d9cb Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 15:54:19 -0500 Subject: [PATCH 11/17] =?UTF-8?q?refactor:=20d=C3=A9placer=20handler=20See?= =?UTF-8?q?dPOST=20=C3=A0=20seed.go?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apihandler/apihandler.go | 28 ---------------------------- apihandler/seed.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 28 deletions(-) create mode 100644 apihandler/seed.go diff --git a/apihandler/apihandler.go b/apihandler/apihandler.go index c9684ce..0dac7db 100644 --- a/apihandler/apihandler.go +++ b/apihandler/apihandler.go @@ -5,7 +5,6 @@ import ( "sort" "codeberg.org/vlbeaudoin/pave" - "git.agecem.com/agecem/agecem-org/apiresponse" "git.agecem.com/agecem/agecem-org/config" "git.agecem.com/agecem/agecem-org/media" "github.com/labstack/echo/v4" @@ -26,30 +25,3 @@ func (h *V1Handler) V1GET(c echo.Context) error { sort.Slice(routes, func(i, j int) bool { return routes[i].Path < routes[j].Path }) return c.JSON(http.StatusOK, routes) } - -// V1SeedPOST créé des buckets dans minio selon la liste de buckets dans server.documents.buckets -// Les buckets sont créés avec paramètres par défaut, et sont ensuite visible dans /v1/bucket. -func (h *V1Handler) V1SeedPOST(c echo.Context) error { - var response apiresponse.V1SeedPOST - - new_buckets, err := h.MediaClient.Seed() - response.Data.Buckets = new_buckets - if err != nil { - response.StatusCode = http.StatusInternalServerError - response.Message = "Error during mediaClient.Seed()" - response.Error = err.Error() - - return c.JSON(response.StatusCode, response) - } - - if len(new_buckets) == 0 { - response.Message = "All buckets already exist" - - } else { - response.Message = "Buckets successfully created" - } - - response.StatusCode = http.StatusOK - - return c.JSON(response.StatusCode, response) -} diff --git a/apihandler/seed.go b/apihandler/seed.go new file mode 100644 index 0000000..77f93c6 --- /dev/null +++ b/apihandler/seed.go @@ -0,0 +1,35 @@ +package apihandler + +import ( + "net/http" + + "git.agecem.com/agecem/agecem-org/apiresponse" + "github.com/labstack/echo/v4" +) + +// V1SeedPOST créé des buckets dans minio selon la liste de buckets dans server.documents.buckets +// Les buckets sont créés avec paramètres par défaut, et sont ensuite visible dans /v1/bucket. +func (h *V1Handler) V1SeedPOST(c echo.Context) error { + var response apiresponse.V1SeedPOST + + new_buckets, err := h.MediaClient.Seed() + response.Data.Buckets = new_buckets + if err != nil { + response.StatusCode = http.StatusInternalServerError + response.Message = "Error during mediaClient.Seed()" + response.Error = err.Error() + + return c.JSON(response.StatusCode, response) + } + + if len(new_buckets) == 0 { + response.Message = "All buckets already exist" + + } else { + response.Message = "Buckets successfully created" + } + + response.StatusCode = http.StatusOK + + return c.JSON(response.StatusCode, response) +} -- 2.45.2 From 02a65f2c470194e434b6cf1e1faf8994422c79ef Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 16:14:22 -0500 Subject: [PATCH 12/17] feat: start paving bucket operations --- apihandler/bucket.go | 25 ++++++++++++++++--- apirequest/bucket.go | 58 ++++++++++++++++++++++++++++++++++++++++++++ cmd/server.go | 12 +++++++-- 3 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 apirequest/bucket.go diff --git a/apihandler/bucket.go b/apihandler/bucket.go index 26ea6d1..04818f6 100644 --- a/apihandler/bucket.go +++ b/apihandler/bucket.go @@ -4,6 +4,7 @@ import ( "context" "net/http" + "git.agecem.com/agecem/agecem-org/apirequest" "git.agecem.com/agecem/agecem-org/apiresponse" "github.com/labstack/echo/v4" "github.com/minio/minio-go/v7" @@ -11,8 +12,16 @@ import ( // V1BucketsGET affiche les buckets permis par server.documents.buckets, qui existent. func (h *V1Handler) V1BucketsGET(c echo.Context) error { + var request apirequest.V1BucketsGET var response apiresponse.V1BucketsGET + if !request.Complete() { + response.Message = "Incomplete V1BucketsGET request received" + response.StatusCode = http.StatusBadRequest + + return c.JSON(response.StatusCode, response) + } + var buckets = make(map[string]string) for bucket_name, bucket_display_name := range h.Config.Server.Documents.Buckets { @@ -38,13 +47,21 @@ func (h *V1Handler) V1BucketsGET(c echo.Context) error { } func (h *V1Handler) V1BucketGET(c echo.Context) error { + var request apirequest.V1BucketGET var response apiresponse.V1BucketGET - bucket := c.Param("bucket") + request.Params.Bucket = c.Param("bucket") + + if !request.Complete() { + response.Message = "Incomplete V1BucketGET request received" + response.StatusCode = http.StatusBadRequest + + return c.JSON(response.StatusCode, response) + } allowed := false for bucket_allowed := range h.Config.Server.Documents.Buckets { - if bucket == bucket_allowed { + if request.Params.Bucket == bucket_allowed { allowed = true } } @@ -57,7 +74,7 @@ func (h *V1Handler) V1BucketGET(c echo.Context) error { defer cancel() - exists, err := h.MediaClient.MinioClient.BucketExists(ctx, bucket) + exists, err := h.MediaClient.MinioClient.BucketExists(ctx, request.Params.Bucket) if err != nil { response.StatusCode = http.StatusInternalServerError response.Message = "Error during minio#BucketExists" @@ -70,7 +87,7 @@ func (h *V1Handler) V1BucketGET(c echo.Context) error { return c.JSON(apiresponse.NotFoundResponse()) } - objectCh := h.MediaClient.MinioClient.ListObjects(ctx, bucket, minio.ListObjectsOptions{}) + objectCh := h.MediaClient.MinioClient.ListObjects(ctx, request.Params.Bucket, minio.ListObjectsOptions{}) for object := range objectCh { if object.Err != nil { response.StatusCode = http.StatusInternalServerError diff --git a/apirequest/bucket.go b/apirequest/bucket.go new file mode 100644 index 0000000..e4ba60a --- /dev/null +++ b/apirequest/bucket.go @@ -0,0 +1,58 @@ +package apirequest + +import ( + "fmt" + "net/http" + + "codeberg.org/vlbeaudoin/voki" + "codeberg.org/vlbeaudoin/voki/request" + "git.agecem.com/agecem/agecem-org/apiresponse" +) + +var _ request.Requester[apiresponse.V1BucketsGET] = V1BucketsGET{} + +type V1BucketsGET struct{} + +func NewV1BucketsGET() (request V1BucketsGET, err error) { + return +} + +func (request V1BucketsGET) Complete() bool { return true } + +func (request V1BucketsGET) Request(v *voki.Voki) (response apiresponse.V1BucketsGET, err error) { + if !request.Complete() { + err = fmt.Errorf("Incomplete V1BucketsGET request") + return + } + + return response, v.UnmarshalIfComplete(http.MethodGet, "/v1/bucket", nil, true, &response) +} + +var _ request.Requester[apiresponse.V1BucketGET] = V1BucketGET{} + +type V1BucketGET struct { + Params struct { + Bucket string `json:"bucket"` + } +} + +func NewV1BucketGET(bucket string) (request V1BucketGET, err error) { + if bucket == "" { + err = fmt.Errorf("NewV1BucketGET requires non-nil bucket name") + } + + request.Params.Bucket = bucket + + return +} + +func (request V1BucketGET) Complete() bool { return request.Params.Bucket != "" } + +func (request V1BucketGET) Request(v *voki.Voki) (response apiresponse.V1BucketGET, err error) { + if !request.Complete() { + err = fmt.Errorf("Incomplete V1BucketGET request") + return + } + + return response, v.UnmarshalIfComplete(http.MethodGet, fmt.Sprintf("/v1/bucket/%s", request.Params.Bucket), nil, true, &response) +} diff --git a/cmd/server.go b/cmd/server.go index af1d138..bd17f2b 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -230,9 +230,17 @@ func RunServer() { log.Fatal(err) } - groupV1.GET("/bucket", v1Handler.V1BucketsGET) + if err := pave.EchoRegister[ + apirequest.V1BucketsGET, + apiresponse.V1BucketsGET](groupV1, &p, "/v1", http.MethodGet, "/bucket", "List buckets", "V1BucketsGET", v1Handler.V1BucketsGET); err != nil { + log.Fatal(err) + } - groupV1.GET("/bucket/:bucket", v1Handler.V1BucketGET) + if err := pave.EchoRegister[ + apirequest.V1BucketGET, + apiresponse.V1BucketGET](groupV1, &p, "/v1", http.MethodGet, "/bucket/:bucket", "Read bucket content", "V1BucketGET", v1Handler.V1BucketGET); err != nil { + log.Fatal(err) + } groupV1.POST("/bucket/:bucket", v1Handler.V1DocumentPOST) -- 2.45.2 From c65cf4a2116dc2ca495db70d180ea82dd164d468 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 16:29:10 -0500 Subject: [PATCH 13/17] feat: pave V1DocumentPOST --- apihandler/document.go | 23 ++++++++++++----- apirequest/document.go | 58 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 apirequest/document.go diff --git a/apihandler/document.go b/apihandler/document.go index a93bcb4..032057d 100644 --- a/apihandler/document.go +++ b/apihandler/document.go @@ -4,18 +4,20 @@ import ( "context" "net/http" + "git.agecem.com/agecem/agecem-org/apirequest" "git.agecem.com/agecem/agecem-org/apiresponse" "github.com/labstack/echo/v4" "github.com/minio/minio-go/v7" ) // V1DocumentPOST permet d'ajouter un object dans un bucket, par multipart/form-data -func (h *V1Handler) V1DocumentPOST(c echo.Context) error { +func (h *V1Handler) V1DocumentPOST(c echo.Context) (err error) { + var request apirequest.V1DocumentPOST var response apiresponse.V1DocumentPOST - bucket := c.Param("bucket") + request.Data.Bucket = c.Param("bucket") - form_file, err := c.FormFile("document") + request.Data.Document, err = c.FormFile("document") if err != nil { response.StatusCode = http.StatusBadRequest response.Message = "Error during HandleV1DocumentCreate's echo#Context.FormFile" @@ -26,7 +28,7 @@ func (h *V1Handler) V1DocumentPOST(c echo.Context) error { allowed := false for bucket_allowed := range h.Config.Server.Documents.Buckets { - if bucket == bucket_allowed { + if request.Data.Bucket == bucket_allowed { allowed = true } } @@ -35,11 +37,18 @@ func (h *V1Handler) V1DocumentPOST(c echo.Context) error { return c.JSON(apiresponse.NotFoundResponse()) } + if !request.Complete() { + response.Message = "Incomplete V1DocumentPOST request received" + response.StatusCode = http.StatusBadRequest + + return c.JSON(response.StatusCode, response) + } + ctx, cancel := context.WithCancel(context.Background()) defer cancel() - src, err := form_file.Open() + src, err := request.Data.Document.Open() if err != nil { response.StatusCode = http.StatusBadRequest response.Message = "Error during form_file.Open()" @@ -49,8 +58,8 @@ func (h *V1Handler) V1DocumentPOST(c echo.Context) error { } defer src.Close() - info, err := h.MediaClient.MinioClient.PutObject(ctx, bucket, form_file.Filename, src, form_file.Size, minio.PutObjectOptions{ - ContentType: form_file.Header.Get("Content-Type"), + info, err := h.MediaClient.MinioClient.PutObject(ctx, request.Data.Bucket, request.Data.Document.Filename, src, request.Data.Document.Size, minio.PutObjectOptions{ + ContentType: request.Data.Document.Header.Get("Content-Type"), }) if err != nil { response.StatusCode = http.StatusInternalServerError diff --git a/apirequest/document.go b/apirequest/document.go new file mode 100644 index 0000000..5c9ca67 --- /dev/null +++ b/apirequest/document.go @@ -0,0 +1,58 @@ +package apirequest + +import ( + "bytes" + "encoding/json" + "fmt" + "mime/multipart" + "net/http" + + "codeberg.org/vlbeaudoin/voki" + "codeberg.org/vlbeaudoin/voki/request" + "git.agecem.com/agecem/agecem-org/apiresponse" +) + +var _ request.Requester[apiresponse.V1DocumentPOST] = V1DocumentPOST{} + +type V1DocumentPOST struct { + Data struct { + Bucket string + Document *multipart.FileHeader + } +} + +func NewV1DocumentPOST(bucket string, document *multipart.FileHeader) (request V1DocumentPOST, err error) { + if bucket == "" { + err = fmt.Errorf("NewV1DocumentPOST requires non-nil bucket name") + return + } + + request.Data.Bucket = bucket + + if document == nil { + err = fmt.Errorf("NewV1DocumentPOST requires non-nil document") + return + } + + request.Data.Document = document + + return +} + +func (request V1DocumentPOST) Complete() bool { + return request.Data.Bucket != "" && request.Data.Document != nil +} + +func (request V1DocumentPOST) Request(v *voki.Voki) (response apiresponse.V1DocumentPOST, err error) { + if !request.Complete() { + err = fmt.Errorf("Incomplete V1DocumentPOST request") + return + } + + var buf bytes.Buffer + if err = json.NewEncoder(&buf).Encode(request.Data); err != nil { + return + } + + return response, v.UnmarshalIfComplete(http.MethodPost, fmt.Sprintf("/v1/bucket/%s", request.Data.Bucket), &buf, true, &response) +} -- 2.45.2 From c7282edcc20d408b7ed83a2bb7f34d7233fb7c17 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 16:34:19 -0500 Subject: [PATCH 14/17] feat: move V1DocumentPOST route declaration to pave --- cmd/server.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/server.go b/cmd/server.go index bd17f2b..11baa50 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -242,7 +242,11 @@ func RunServer() { log.Fatal(err) } - groupV1.POST("/bucket/:bucket", v1Handler.V1DocumentPOST) + if err := pave.EchoRegister[ + apirequest.V1DocumentPOST, + apiresponse.V1DocumentPOST](groupV1, &p, "/v1", http.MethodPost, "/bucket/:bucket", "Upload document to specified bucket", "V1DocumentPOST", v1Handler.V1DocumentPOST); err != nil { + log.Fatal(err) + } groupV1.GET("/bucket/:bucket/:document", v1Handler.V1DocumentGET) -- 2.45.2 From 86f1284e1253420d4efd27b2791614a74908d352 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 16:56:44 -0500 Subject: [PATCH 15/17] feat: pave V1DocumentDELETE --- apihandler/document.go | 45 +++++++++++++++++++++++++++-------------- apirequest/document.go | 40 ++++++++++++++++++++++++++++++++++++ apiresponse/document.go | 8 ++++++++ cmd/server.go | 6 +++++- 4 files changed, 83 insertions(+), 16 deletions(-) diff --git a/apihandler/document.go b/apihandler/document.go index 032057d..8239474 100644 --- a/apihandler/document.go +++ b/apihandler/document.go @@ -141,12 +141,15 @@ func (h *V1Handler) V1DocumentPUT(c echo.Context) error { // V1DocumentDELETE permet de supprimer un object func (h *V1Handler) V1DocumentDELETE(c echo.Context) error { - bucket := c.Param("bucket") - document := c.Param("document") + var request apirequest.V1DocumentDELETE + var response apiresponse.V1DocumentDELETE + + request.Params.Bucket = c.Param("bucket") + request.Params.Document = c.Param("document") allowed := false for bucket_allowed := range h.Config.Server.Documents.Buckets { - if bucket == bucket_allowed { + if request.Params.Bucket == bucket_allowed { allowed = true } } @@ -155,11 +158,18 @@ func (h *V1Handler) V1DocumentDELETE(c echo.Context) error { return c.JSON(apiresponse.NotFoundResponse()) } + if !request.Complete() { + response.Message = "Incomplete V1DocumentDELETE request received" + response.StatusCode = http.StatusBadRequest + + return c.JSON(response.StatusCode, response) + } + ctx, cancel := context.WithCancel(context.Background()) defer cancel() - bucket_exists, err := h.MediaClient.MinioClient.BucketExists(ctx, bucket) + bucket_exists, err := h.MediaClient.MinioClient.BucketExists(ctx, request.Params.Bucket) if err != nil { return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") } @@ -168,29 +178,34 @@ func (h *V1Handler) V1DocumentDELETE(c echo.Context) error { return c.JSON(apiresponse.NotFoundResponse()) } - document_info, err := h.MediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) + document_info, err := h.MediaClient.MinioClient.StatObject(ctx, request.Params.Bucket, request.Params.Document, minio.StatObjectOptions{}) if err != nil { if err.Error() == "The specified key does not exist." { return c.JSON(apiresponse.NotFoundResponse()) } - return c.JSON(http.StatusInternalServerError, map[string]interface{}{ - "message": "Error during minio#StatObject", - }) + //response.Error = err.Error() + response.Message = "Error during minio#StatObject" + response.StatusCode = http.StatusInternalServerError + + return c.JSON(response.StatusCode, response) } //TODO Add error validation _ = document_info - err = h.MediaClient.MinioClient.RemoveObject(ctx, bucket, document, minio.RemoveObjectOptions{}) + err = h.MediaClient.MinioClient.RemoveObject(ctx, request.Params.Bucket, request.Params.Document, minio.RemoveObjectOptions{}) if err != nil { - return c.JSON(http.StatusInternalServerError, map[string]string{ - "message": "Error during minio#RemoveObject", - }) + //response.Error = err.Error() + response.Message = "Error during minio#RemoveObject" + response.StatusCode = http.StatusInternalServerError + + return c.JSON(response.StatusCode, response) } - return c.JSON(http.StatusOK, map[string]string{ - "message": "Document deleted", - }) + response.Message = "Document deleted" + response.StatusCode = http.StatusOK + + return c.JSON(response.StatusCode, response) } diff --git a/apirequest/document.go b/apirequest/document.go index 5c9ca67..c5c1f9a 100644 --- a/apirequest/document.go +++ b/apirequest/document.go @@ -56,3 +56,43 @@ func (request V1DocumentPOST) Request(v *voki.Voki) (response apiresponse.V1Docu return response, v.UnmarshalIfComplete(http.MethodPost, fmt.Sprintf("/v1/bucket/%s", request.Data.Bucket), &buf, true, &response) } + +var _ request.Requester[apiresponse.V1DocumentDELETE] = V1DocumentDELETE{} + +type V1DocumentDELETE struct { + Params struct { + Bucket string + Document string + } +} + +func NewV1DocumentDELETE(bucket, document string) (request V1DocumentDELETE, err error) { + if bucket == "" { + err = fmt.Errorf("NewV1DocumentDELETE requires non-nil bucket name") + return + } + + request.Params.Bucket = bucket + + if document == "" { + err = fmt.Errorf("NewV1DocumentDELETE requires non-nil document name") + return + } + + request.Params.Document = document + + return +} + +func (request V1DocumentDELETE) Complete() bool { + return request.Params.Bucket != "" && request.Params.Document != "" +} + +func (request V1DocumentDELETE) Request(v *voki.Voki) (response apiresponse.V1DocumentDELETE, err error) { + if !request.Complete() { + err = fmt.Errorf("Incomplete V1DocumentDELETE request") + return + } + + return response, v.UnmarshalIfComplete(http.MethodDelete, fmt.Sprintf("/v1/bucket/%s/%s", request.Params.Bucket, request.Params.Document), nil, true, &response) +} diff --git a/apiresponse/document.go b/apiresponse/document.go index 75c15e3..5865584 100644 --- a/apiresponse/document.go +++ b/apiresponse/document.go @@ -8,3 +8,11 @@ type V1DocumentPOST struct { Size int64 } } + +type V1DocumentDELETE struct { + Response + Data struct { + Bucket string + Document string + } +} diff --git a/cmd/server.go b/cmd/server.go index 11baa50..776adfd 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -252,7 +252,11 @@ func RunServer() { groupV1.PUT("/bucket/:bucket/:document", v1Handler.V1DocumentPUT) - groupV1.DELETE("/bucket/:bucket/:document", v1Handler.V1DocumentDELETE) + if err := pave.EchoRegister[ + apirequest.V1DocumentDELETE, + apiresponse.V1DocumentDELETE](groupV1, &p, "/v1", http.MethodDelete, "/bucket/:bucket/:document", "Delete document in specified bucket", "V1DocumentDELEte", v1Handler.V1DocumentDELETE); err != nil { + log.Fatal(err) + } // HTML Routes client := http.DefaultClient -- 2.45.2 From 9818466be5be81e6aaafd62ab36566c16a994004 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 17:01:00 -0500 Subject: [PATCH 16/17] fix(api): remove unused fields and fix typo in V1DocumentDELETE --- apiresponse/document.go | 4 ---- cmd/server.go | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/apiresponse/document.go b/apiresponse/document.go index 5865584..beab0ae 100644 --- a/apiresponse/document.go +++ b/apiresponse/document.go @@ -11,8 +11,4 @@ type V1DocumentPOST struct { type V1DocumentDELETE struct { Response - Data struct { - Bucket string - Document string - } } diff --git a/cmd/server.go b/cmd/server.go index 776adfd..3943c24 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -254,7 +254,7 @@ func RunServer() { if err := pave.EchoRegister[ apirequest.V1DocumentDELETE, - apiresponse.V1DocumentDELETE](groupV1, &p, "/v1", http.MethodDelete, "/bucket/:bucket/:document", "Delete document in specified bucket", "V1DocumentDELEte", v1Handler.V1DocumentDELETE); err != nil { + apiresponse.V1DocumentDELETE](groupV1, &p, "/v1", http.MethodDelete, "/bucket/:bucket/:document", "Delete document in specified bucket", "V1DocumentDELETE", v1Handler.V1DocumentDELETE); err != nil { log.Fatal(err) } -- 2.45.2 From ab81c4bfa29efda3e2b8649a05d008004fa75f4b Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Mon, 20 Nov 2023 17:02:37 -0500 Subject: [PATCH 17/17] remove!: unused and unimplemented V1DocumentPUT --- apihandler/document.go | 5 ----- cmd/server.go | 2 -- 2 files changed, 7 deletions(-) diff --git a/apihandler/document.go b/apihandler/document.go index 8239474..29b70b5 100644 --- a/apihandler/document.go +++ b/apihandler/document.go @@ -134,11 +134,6 @@ func (h *V1Handler) V1DocumentGET(c echo.Context) error { return c.Stream(http.StatusOK, document_info.ContentType, document_object) } -// V1DocumentPUT permet de mettre à jour certains champs d'un object, comme le Content-Type ou le Filename -func (h *V1Handler) V1DocumentPUT(c echo.Context) error { - return c.JSON(apiresponse.NotImplementedResponse()) -} - // V1DocumentDELETE permet de supprimer un object func (h *V1Handler) V1DocumentDELETE(c echo.Context) error { var request apirequest.V1DocumentDELETE diff --git a/cmd/server.go b/cmd/server.go index 3943c24..a5a9fbf 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -250,8 +250,6 @@ func RunServer() { groupV1.GET("/bucket/:bucket/:document", v1Handler.V1DocumentGET) - groupV1.PUT("/bucket/:bucket/:document", v1Handler.V1DocumentPUT) - if err := pave.EchoRegister[ apirequest.V1DocumentDELETE, apiresponse.V1DocumentDELETE](groupV1, &p, "/v1", http.MethodDelete, "/bucket/:bucket/:document", "Delete document in specified bucket", "V1DocumentDELETE", v1Handler.V1DocumentDELETE); err != nil { -- 2.45.2