diff --git a/.vscode/launch.json b/.vscode/launch.json index 3ab34ffd7cbd31da6326bc575535ca07d690eaff..7a7d3a18cd92f152ced24c2b051dbbfa23cef377 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,8 @@ "mode": "debug", "program": "${workspaceFolder}/main.go", "envFile": "${workspaceFolder}/.env", - "showLog": true + "showLog": true, + "dlvFlags": ["--check-go-version=false"] } ] } diff --git a/README.md b/README.md index f29cd60e41aa34422d84b0e6a2993a4b01ff765b..f46d1a313cefc7fa0e2863cffe2506f5bdd75ab5 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,4 @@ Ecolyo Agent is the backoffice for the Ecolyo app. This repository contains the backend part of this backoffice. -[check documentation here](https://doc.self-data.alpha.grandlyon.com/ecolyo-agent/technical/getting_started/#ecolyo-agent-server) +[check documentation here](https://doc-self-data.apps.grandlyon.com/ecolyo-agent/technical/getting_started/#ecolyo-agent-server) diff --git a/go.mod b/go.mod index 093ba5d899a24dc496b7a7187a963e495760450e..1476d8bb39a7f7e0e3901a5c179b45e628673c20 100644 --- a/go.mod +++ b/go.mod @@ -3,21 +3,20 @@ module forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-serv go 1.18 require ( - github.com/go-chi/chi/v5 v5.0.8 - github.com/google/uuid v1.3.1 - golang.org/x/oauth2 v0.4.0 - gorm.io/driver/mysql v1.4.7 - gorm.io/driver/sqlite v1.4.4 - gorm.io/gorm v1.24.5 + github.com/go-chi/chi/v5 v5.0.11 + github.com/google/uuid v1.5.0 + golang.org/x/oauth2 v0.16.0 + gorm.io/driver/mysql v1.5.2 + gorm.io/driver/sqlite v1.5.4 + gorm.io/gorm v1.25.5 ) require ( - github.com/go-sql-driver/mysql v1.7.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/go-sql-driver/mysql v1.7.1 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/mattn/go-sqlite3 v1.14.15 // indirect - golang.org/x/net v0.8.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.0 // indirect + github.com/mattn/go-sqlite3 v1.14.19 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/protobuf v1.32.0 // indirect ) diff --git a/go.sum b/go.sum index 8ec307f55c53f5046067c31a54b715a4fba176b5..b71a4a2954f2b1b2a3d1eb1af0b064af89fba3a8 100644 --- a/go.sum +++ b/go.sum @@ -1,44 +1,59 @@ -github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= -github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA= +github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= -github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= +github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gorm.io/driver/mysql v1.4.7 h1:rY46lkCspzGHn7+IYsNpSfEv9tA+SU4SkkB+GFX125Y= -gorm.io/driver/mysql v1.4.7/go.mod h1:SxzItlnT1cb6e1e4ZRpgJN2VYtcqJgqnHxWr4wsP8oc= -gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc= -gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= -gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= -gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= -gorm.io/gorm v1.24.5 h1:g6OPREKqqlWq4kh/3MCQbZKImeB9e6Xgc4zD+JgNZGE= -gorm.io/gorm v1.24.5/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs= +gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8= +gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0= +gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4= +gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= +gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= diff --git a/internal/auth/auth.go b/internal/auth/auth.go index ed83d4e2e732c62a91b125e10ab66c2a2e875e91..860511c060dda2a24bec483dee7ccbde5f074ccd 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -117,7 +117,7 @@ func ValidateAuthMiddleware(next http.Handler, allowedRoles []string, checkXSRF return http.HandlerFunc(roleChecker) } -func SGEAuthMiddleware(next http.Handler) http.Handler { +func BOAuthMiddleware(next http.Handler) http.Handler { tokenChecker := func(w http.ResponseWriter, r *http.Request) { // Check API Token if r.Header.Get("Authorization") != "Bearer "+SGEApiToken { diff --git a/internal/models/customPopup.go b/internal/models/customPopup.go index 79b8f6cd6e5dd4ae3c70c1244384a49bb3d2b095..c64af4a32d8dae29b3d31e16b388ee66247fe85b 100644 --- a/internal/models/customPopup.go +++ b/internal/models/customPopup.go @@ -15,6 +15,7 @@ type CustomPopup struct { ID uint `gorm:"<-:create"` PopupEnabled bool `json:"popupEnabled"` Title string `json:"title"` + Image string `json:"image"` Description string `json:"description"` EndDate time.Time `json:"endDate"` } @@ -77,6 +78,7 @@ func (dh *DataHandler) SaveCustomPopup(w http.ResponseWriter, r *http.Request) { } updatedCustomPopup.PopupEnabled = customPopup.PopupEnabled + updatedCustomPopup.Image = customPopup.Image updatedCustomPopup.Title = customPopup.Title updatedCustomPopup.Description = customPopup.Description updatedCustomPopup.EndDate = customPopup.EndDate diff --git a/internal/models/grdfConsent.go b/internal/models/grdfConsent.go new file mode 100644 index 0000000000000000000000000000000000000000..1316a842ff9d0924d1d9a582a428aed6c919bf46 --- /dev/null +++ b/internal/models/grdfConsent.go @@ -0,0 +1,192 @@ +package models + +import ( + "encoding/json" + "errors" + "log" + "net/http" + "time" + + "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/common" + "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/constants" + "gorm.io/gorm" +) + +type GrdfConsent struct { + Base + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Pce string `json:"pce"` + PostalCode string `json:"postalCode"` + StartDate time.Time `json:"startDate"` + EndDate time.Time `json:"endDate"` +} + +// This type is only used for Swagger documentation +type GrdfConsentSwagger struct { + ID int `json:"ID"` + CreatedAt time.Time `json:"CreatedAt"` + UpdatedAt time.Time `json:"UpdatedAt"` + DeletedAt time.Time `json:"DeletedAt"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Pce string `json:"pce"` + PostalCode string `json:"postalCode"` + StartDate time.Time `json:"startDate"` + EndDate time.Time `json:"endDate"` +} + +// GetConsentById godoc +// +// @Summary Get details of a specific consent +// @Description Get details of a specific consent +// @Tags consent +// @Produce json +// @Success 200 {object} GrdfConsentSwagger +// @Failure 404 {string} string "Not found" +// @Param id path int true "ID of the consent" +// @Router /api/grdf/consent/{id} [get] +func (dh *DataHandler) GetGrdfConsentById(w http.ResponseWriter, r *http.Request) { + id, err := common.IdFromRequest(r) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + var consent GrdfConsent + err = dh.sqlClient.First(&consent, "id = ?", id).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + http.Error(w, "consent not found", http.StatusNotFound) + return + } + http.Error(w, "error while finding consent", http.StatusInternalServerError) + return + } + + w.Header().Set(constants.ContentType, constants.Json) + json.NewEncoder(w).Encode(consent) + + log.Printf("| get consent | name : %v | %v", consent.Lastname, r.RemoteAddr) +} + +// PostConsent godoc +// +// @Summary Create a new consent +// @Description Create a new consent +// @Tags consent +// @Produce json +// @Success 201 {object} GrdfConsentSwagger +// @Failure 400 {string} string "Bad request" +// @Failure 500 {string} string "couldn't create consent" +// @Param id path int true "ID of the consent" +// @Router /api/grdf/consent [post] +func (dh *DataHandler) PostGrdfConsent(w http.ResponseWriter, r *http.Request) { + if r.Body == http.NoBody { + http.Error(w, "request body is empty", http.StatusBadRequest) + return + } + + decoder := json.NewDecoder(r.Body) + var consent GrdfConsent + err := decoder.Decode(&consent) + if err != nil { + http.Error(w, "couldn't parse body", http.StatusInternalServerError) + log.Println(err.Error()) + return + } + + // Create a consent in SQL + err = dh.sqlClient.Create(&consent).Error + if err != nil { + http.Error(w, "couldn't create consent", http.StatusInternalServerError) + log.Println(err.Error()) + return + } + + w.Header().Set(constants.ContentType, constants.Json) + w.WriteHeader(http.StatusCreated) + json.NewEncoder(w).Encode(consent) + + log.Printf("| new consent | name : %v | %v", consent.Lastname, r.RemoteAddr) +} + +// DeleteConsentById godoc +// +// @Summary Delete a specific consent +// @Description Delete a specific consent +// @Tags consent +// @Produce json +// @Success 200 {object} GrdfConsentSwagger +// @Failure 404 {string} string "Not found" +// @Failure 500 {string} string "Not found" +// @Param id path int true "ID of the consent" +// @Router /api/grdf/consent/{id} [delete] +func (dh *DataHandler) DeleteGrdfConsentById(w http.ResponseWriter, r *http.Request) { + id, err := common.IdFromRequest(r) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + var consent = GrdfConsent{} + err = dh.sqlClient.First(&consent, "id = ?", id).Error + if err != nil { + http.Error(w, "couldn't find consent", http.StatusInternalServerError) + log.Println(err.Error()) + return + } + + // Update and save consent in MySQL + consent.EndDate = time.Now() + err = dh.sqlClient.Save(&consent).Error + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + log.Println(err.Error()) + return + } + + dh.sqlClient.Delete(&consent) + + log.Printf("| deleted consent | id : %v | %v", id, r.RemoteAddr) +} + +// SearchConsent godoc +// +// @Summary Search for consents +// @Description Search for consents based on the pointID +// @Tags consent +// @Produce json +// @Success 200 {array} GrdfConsentSwagger +// @Failure 400 {string} string "Not found" +// @Param search query string true "pointID to search" +// @Router /api/admin/consent [get] +func (dh *DataHandler) SearchGrdfConsent(w http.ResponseWriter, r *http.Request) { + search := r.URL.Query().Get("search") + + page, limit, err := common.PageLimitFromRequest(r) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + var totalRows int64 + dh.sqlClient.Unscoped().Model(GrdfConsent{}).Where("pce LIKE ?", "%"+search+"%").Count(&totalRows) + offset := page * limit + + var consents []GrdfConsent + dh.sqlClient.Unscoped().Order("created_at desc").Offset(offset).Limit(limit).Where("pce LIKE ?", "%"+search+"%").Find(&consents) + + var pagination struct { + TotalRows int64 `json:"totalRows"` + Rows []GrdfConsent `json:"rows"` + } + pagination.TotalRows = totalRows + pagination.Rows = consents + + w.Header().Set(constants.ContentType, constants.Json) + json.NewEncoder(w).Encode(pagination) + + log.Printf("| get all consents | limit : %d | page : %d | %v", limit, page, r.RemoteAddr) + +} diff --git a/internal/models/models.go b/internal/models/models.go index e82db3463ec4772766044562040ca05c96f276a4..ca673951a23550750c5318aeed44233411dde563 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -48,7 +48,8 @@ func NewDataHandler() *DataHandler { sqlClient.AutoMigrate(&PartnersInfo{}) sqlClient.AutoMigrate(&CustomPopup{}) sqlClient.AutoMigrate(&Price{}) - sqlClient.AutoMigrate(&Consent{}) + sqlClient.AutoMigrate(&SgeConsent{}) + sqlClient.AutoMigrate(&GrdfConsent{}) // Check if partners info already exists var partnersInfo PartnersInfo @@ -71,6 +72,7 @@ func NewDataHandler() *DataHandler { sqlClient.Create(&CustomPopup{ PopupEnabled: false, Title: "", + Image: "", Description: "", EndDate: time.Now(), }) diff --git a/internal/models/consent.go b/internal/models/sgeConsent.go similarity index 84% rename from internal/models/consent.go rename to internal/models/sgeConsent.go index f7a528747e40aa97027c573088a5a4964752b1cf..b3278c57b02e233fca156a821f906997e6a73491 100644 --- a/internal/models/consent.go +++ b/internal/models/sgeConsent.go @@ -20,7 +20,12 @@ type Base struct { DeletedAt gorm.DeletedAt `sql:"index"` } -type Consent struct { +func (b *Base) BeforeCreate(tx *gorm.DB) (err error) { + b.ID = uuid.NewString() + return +} + +type SgeConsent struct { Base Firstname string `json:"firstname"` Lastname string `json:"lastname"` @@ -35,7 +40,7 @@ type Consent struct { } // This type is only used for Swagger documentation -type ConsentSwagger struct { +type SgeConsentSwagger struct { ID int `json:"ID"` CreatedAt time.Time `json:"CreatedAt"` UpdatedAt time.Time `json:"UpdatedAt"` @@ -52,33 +57,28 @@ type ConsentSwagger struct { ServiceID int `json:"serviceID,omitempty"` } -type UpdateConsentBody struct { +type UpdateSgeConsentBody struct { ServiceID int `json:"serviceID"` } -func (b *Base) BeforeCreate(tx *gorm.DB) (err error) { - b.ID = uuid.NewString() - return -} - -// GetConsentById godoc +// GetSgeConsentById godoc // // @Summary Get details of a specific consent // @Description Get details of a specific consent // @Tags consent // @Produce json -// @Success 200 {object} ConsentSwagger +// @Success 200 {object} SgeConsentSwagger // @Failure 404 {string} string "Not found" // @Param id path int true "ID of the consent" // @Router /api/sge/consent/{id} [get] -func (dh *DataHandler) GetConsentById(w http.ResponseWriter, r *http.Request) { +func (dh *DataHandler) GetSgeConsentById(w http.ResponseWriter, r *http.Request) { id, err := common.IdFromRequest(r) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } - var consent Consent + var consent SgeConsent err = dh.sqlClient.First(&consent, "id = ?", id).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { @@ -95,25 +95,25 @@ func (dh *DataHandler) GetConsentById(w http.ResponseWriter, r *http.Request) { log.Printf("| get consent | name : %v | %v", consent.Lastname, r.RemoteAddr) } -// PostConsent godoc +// PostSgeConsent godoc // // @Summary Create a new consent // @Description Create a new consent // @Tags consent // @Produce json -// @Success 201 {object} ConsentSwagger +// @Success 201 {object} SgeConsentSwagger // @Failure 400 {string} string "Bad request" // @Failure 500 {string} string "couldn't create consent" // @Param id path int true "ID of the consent" // @Router /api/sge/consent [post] -func (dh *DataHandler) PostConsent(w http.ResponseWriter, r *http.Request) { +func (dh *DataHandler) PostSgeConsent(w http.ResponseWriter, r *http.Request) { if r.Body == http.NoBody { http.Error(w, "request body is empty", http.StatusBadRequest) return } decoder := json.NewDecoder(r.Body) - var consent Consent + var consent SgeConsent err := decoder.Decode(&consent) if err != nil { http.Error(w, "couldn't parse body", http.StatusInternalServerError) @@ -137,19 +137,19 @@ func (dh *DataHandler) PostConsent(w http.ResponseWriter, r *http.Request) { log.Printf("| new consent | name : %v | %v", consent.Lastname, r.RemoteAddr) } -// UpdateConsent godoc +// UpdateSgeConsent godoc // // @Summary Update a consent, giving it a serviceID // @Description Update a consent, giving it a serviceID // @Tags consent // @Produce json -// @Success 200 {object} ConsentSwagger +// @Success 200 {object} SgeConsentSwagger // @Failure 400 {string} string "invalid service id" // @Failure 404 {string} string "couldn't find consent" // @Failure 500 {string} string "couldn't parse body" -// @Param id body UpdateConsentBody true "service ID" +// @Param id body UpdateSgeConsentBody true "service ID" // @Router /api/sge/consent [put] -func (dh *DataHandler) UpdateConsent(w http.ResponseWriter, r *http.Request) { +func (dh *DataHandler) UpdateSgeConsent(w http.ResponseWriter, r *http.Request) { if r.Body == http.NoBody { http.Error(w, "request body is empty", http.StatusBadRequest) return @@ -161,7 +161,7 @@ func (dh *DataHandler) UpdateConsent(w http.ResponseWriter, r *http.Request) { } // Find consent - var consent Consent + var consent SgeConsent err = dh.sqlClient.First(&consent, "id = ?", id).Error if err != nil { http.Error(w, "couldn't find consent", http.StatusNotFound) @@ -170,7 +170,7 @@ func (dh *DataHandler) UpdateConsent(w http.ResponseWriter, r *http.Request) { } // Get service ID decoder := json.NewDecoder(r.Body) - var body UpdateConsentBody + var body UpdateSgeConsentBody err = decoder.Decode(&body) if err != nil { http.Error(w, "couldn't parse body", http.StatusInternalServerError) @@ -196,25 +196,25 @@ func (dh *DataHandler) UpdateConsent(w http.ResponseWriter, r *http.Request) { log.Printf("| updated consent | name : %v | serviceID : %v | %v", consent.Lastname, consent.ServiceID, r.RemoteAddr) } -// DeleteConsentById godoc +// DeleteSgeConsentById godoc // // @Summary Delete a specific consent // @Description Delete a specific consent // @Tags consent // @Produce json -// @Success 200 {object} ConsentSwagger +// @Success 200 {object} SgeConsentSwagger // @Failure 404 {string} string "Not found" // @Failure 500 {string} string "Not found" // @Param id path int true "ID of the consent" // @Router /api/sge/consent/{id} [delete] -func (dh *DataHandler) DeleteConsentById(w http.ResponseWriter, r *http.Request) { +func (dh *DataHandler) DeleteSgeConsentById(w http.ResponseWriter, r *http.Request) { id, err := common.IdFromRequest(r) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } - var consent = Consent{} + var consent = SgeConsent{} err = dh.sqlClient.First(&consent, "id = ?", id).Error if err != nil { http.Error(w, "couldn't find consent", http.StatusInternalServerError) @@ -236,17 +236,17 @@ func (dh *DataHandler) DeleteConsentById(w http.ResponseWriter, r *http.Request) log.Printf("| deleted consent | id : %v | %v", id, r.RemoteAddr) } -// SearchConsent godoc +// SearchSgeConsent godoc // // @Summary Search for consents // @Description Search for consents based on the pointID // @Tags consent // @Produce json -// @Success 200 {array} ConsentSwagger +// @Success 200 {array} SgeConsentSwagger // @Failure 400 {string} string "Not found" // @Param search query string true "pointID to search" -// @Router /api/admin/consent [get] -func (dh *DataHandler) SearchConsent(w http.ResponseWriter, r *http.Request) { +// @Router /api/admin/sge/consent [get] +func (dh *DataHandler) SearchSgeConsent(w http.ResponseWriter, r *http.Request) { search := r.URL.Query().Get("search") page, limit, err := common.PageLimitFromRequest(r) @@ -256,15 +256,15 @@ func (dh *DataHandler) SearchConsent(w http.ResponseWriter, r *http.Request) { } var totalRows int64 - dh.sqlClient.Unscoped().Model(Consent{}).Where("point_id LIKE ?", "%"+search+"%").Count(&totalRows) + dh.sqlClient.Unscoped().Model(SgeConsent{}).Where("point_id LIKE ?", "%"+search+"%").Count(&totalRows) offset := page * limit - var consents []Consent + var consents []SgeConsent dh.sqlClient.Unscoped().Order("created_at desc").Offset(offset).Limit(limit).Where("point_id LIKE ?", "%"+search+"%").Find(&consents) var pagination struct { - TotalRows int64 `json:"totalRows"` - Rows []Consent `json:"rows"` + TotalRows int64 `json:"totalRows"` + Rows []SgeConsent `json:"rows"` } pagination.TotalRows = totalRows pagination.Rows = consents diff --git a/internal/rootmux/rootmux.go b/internal/rootmux/rootmux.go index 1d572e9fcab8fbaf61a1b4959c7632aee3848436..5eb4a4df94b6bc5030e7fc626d034933a3aa2269 100644 --- a/internal/rootmux/rootmux.go +++ b/internal/rootmux/rootmux.go @@ -80,15 +80,23 @@ func CreateRootMux() RootMux { r.Route("/api/admin", func(r chi.Router) { r.Use(auth.AdminAuthMiddleware) - r.Get("/consent", dh.SearchConsent) + r.Get("/sge/consent", dh.SearchSgeConsent) + r.Get("/grdf/consent", dh.SearchGrdfConsent) }) r.Route("/api/sge", func(r chi.Router) { - r.Use(auth.SGEAuthMiddleware) - r.Post("/consent", dh.PostConsent) - r.Get("/consent/{id}", dh.GetConsentById) - r.Put("/consent/{id}", dh.UpdateConsent) - r.Delete("/consent/{id}", dh.DeleteConsentById) + r.Use(auth.BOAuthMiddleware) + r.Post("/consent", dh.PostSgeConsent) + r.Get("/consent/{id}", dh.GetSgeConsentById) + r.Put("/consent/{id}", dh.UpdateSgeConsent) + r.Delete("/consent/{id}", dh.DeleteSgeConsentById) + }) + + r.Route("/api/grdf", func(r chi.Router) { + r.Use(auth.BOAuthMiddleware) + r.Post("/consent", dh.PostGrdfConsent) + r.Get("/consent/{id}", dh.GetGrdfConsentById) + r.Delete("/consent/{id}", dh.DeleteGrdfConsentById) }) r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { /** handles Oauth2 redirection */ }) diff --git a/internal/rootmux/rootmux_test.go b/internal/rootmux/rootmux_test.go index 73d1df7a6efa3e98849bfacc7a5093a091b85767..f5de0866c37b003aec423fc178a2c825b30f7ac1 100644 --- a/internal/rootmux/rootmux_test.go +++ b/internal/rootmux/rootmux_test.go @@ -27,24 +27,28 @@ const ( ) var ( - oAuth2Server *httptest.Server - mailSubject = models.MailSubject{Year: 2021, Month: 1, Subject: "[Ecolyo] Newsletter"} - mailSubjectStr string - monthlyInfo = models.MonthlyInfo{Year: 2021, Month: 1, Info: "Informations du mois", Image: "imagebase64"} - monthlyInfoStr string - monthlyNews = models.MonthlyNews{Year: 2021, Month: 1, Title: "", Content: "Nouvelles fonctionnalités"} - monthlyNewsStr string - newPoll = models.Poll{Year: 2021, Month: 1, Question: "pollQuestion", Link: "pollLink"} - newPollStr string - partnersInfo = models.PartnersInfo{ID: 1, GRDFFailure: false, EnedisFailure: false, EGLFailure: true, NotificationActivated: true} - partnersInfoStr string - customPopupInfo = models.CustomPopup{ID: 1, PopupEnabled: false, Title: "Alerte personnalisée", Description: "Ecolyo 4ever"} - customPopupStr string - consent = models.Consent{Firstname: "Foo", Lastname: "Bar", PointID: "12345671234567"} - consentStr string - otherConsent = models.Consent{Firstname: "John", Lastname: "Doe", PointID: "01234560123456"} - otherConsentStr string - noH map[string]string + oAuth2Server *httptest.Server + mailSubject = models.MailSubject{Year: 2021, Month: 1, Subject: "[Ecolyo] Newsletter"} + mailSubjectStr string + monthlyInfo = models.MonthlyInfo{Year: 2021, Month: 1, Info: "Informations du mois", Image: "imagebase64"} + monthlyInfoStr string + monthlyNews = models.MonthlyNews{Year: 2021, Month: 1, Title: "", Content: "Nouvelles fonctionnalités"} + monthlyNewsStr string + newPoll = models.Poll{Year: 2021, Month: 1, Question: "pollQuestion", Link: "pollLink"} + newPollStr string + partnersInfo = models.PartnersInfo{ID: 1, GRDFFailure: false, EnedisFailure: false, EGLFailure: true, NotificationActivated: true} + partnersInfoStr string + customPopupInfo = models.CustomPopup{ID: 1, PopupEnabled: false, Title: "Alerte personnalisée", Description: "Ecolyo 4ever"} + customPopupStr string + consent = models.SgeConsent{Firstname: "Foo", Lastname: "Bar", PointID: "12345671234567"} + consentStr string + otherConsent = models.SgeConsent{Firstname: "John", Lastname: "Doe", PointID: "01234560123456"} + otherConsentStr string + grdfConsent = models.GrdfConsent{Firstname: "Foo", Lastname: "Bar", Pce: "12345671234567"} + grdfConsentStr string + otherGrdfConsent = models.GrdfConsent{Firstname: "John", Lastname: "Doe", Pce: "01234560123456"} + otherGrdfConsentStr string + noH map[string]string ) func TestMain(m *testing.M) { @@ -85,6 +89,10 @@ func TestMain(m *testing.M) { consentStr = string(consentBytes) otherConsentBytes, _ := json.Marshal(otherConsent) otherConsentStr = string(otherConsentBytes) + grdfConsentBytes, _ := json.Marshal(grdfConsent) + grdfConsentStr = string(grdfConsentBytes) + otherGrdfConsentBytes, _ := json.Marshal(otherGrdfConsent) + otherGrdfConsentStr = string(otherGrdfConsentBytes) code := m.Run() // Remove the database @@ -98,6 +106,8 @@ func TestAll(t *testing.T) { // SGE API tests sgeTests(t) + // GRDF API tests + grdfTests(t) // Set up testers os.Setenv("AUTH_URL", oAuth2Server.URL+"/auth-wrong-state") // Set the server to access failing OAuth2 endpoints @@ -266,11 +276,15 @@ func adminTests(t *testing.T) { xsrfHeader := map[string]string{"XSRF-TOKEN": token.XSRFToken} // Try to get SGE consents (must pass) - do("GET", "/api/admin/consent?limit=50&page=0", xsrfHeader, "", http.StatusOK, `{"totalRows":2,"rows":[{"ID":"9566c74d-1003-4c4d-bbbb-0407d1e2c649"`) + do("GET", "/api/admin/sge/consent?limit=50&page=0", xsrfHeader, "", http.StatusOK, `{"totalRows":2,"rows":[{"ID":"9566c74d-1003-4c4d-bbbb-0407d1e2c649"`) + // Try to get GRDF consents (must pass) + do("GET", "/api/admin/grdf/consent?limit=50&page=0", xsrfHeader, "", http.StatusOK, `{"totalRows":2,"rows":[{"ID":"6694d2c4-22ac-4208-a007-2939487f6999"`) // Try to logout (must pass) do("GET", "/Logout", noH, "", http.StatusOK, "") // Try to get SGE consents again (must fail) - do("GET", "/api/admin/consent?limit=50&page=0", xsrfHeader, "", http.StatusUnauthorized, ErrorExtractingToken) + do("GET", "/api/admin/sge/consent?limit=50&page=0", xsrfHeader, "", http.StatusUnauthorized, ErrorExtractingToken) + // Try to get GRDF consents again (must fail) + do("GET", "/api/admin/grdf/consent?limit=50&page=0", xsrfHeader, "", http.StatusUnauthorized, ErrorExtractingToken) } func sgeTests(t *testing.T) { @@ -287,19 +301,44 @@ func sgeTests(t *testing.T) { do("DELETE", "/api/sge/consent/1", noH, "", http.StatusUnauthorized, ErrorInvalidToken) // Create correct authorization header - sgeApiHeader := map[string]string{"Authorization": "Bearer " + auth.SGEApiToken} + boApiHeader := map[string]string{"Authorization": "Bearer " + auth.SGEApiToken} // Try to create a consent (must pass) - do("POST", "/api/sge/consent", sgeApiHeader, consentStr, http.StatusCreated, `{"ID":"52fdfc07-2182-454f-963f-5f0f9a621d72"`) + do("POST", "/api/sge/consent", boApiHeader, consentStr, http.StatusCreated, `{"ID":"52fdfc07-2182-454f-963f-5f0f9a621d72"`) // Try to create another consent (must pass) - do("POST", "/api/sge/consent", sgeApiHeader, otherConsentStr, http.StatusCreated, `{"ID":"9566c74d-1003-4c4d-bbbb-0407d1e2c649"`) + do("POST", "/api/sge/consent", boApiHeader, otherConsentStr, http.StatusCreated, `{"ID":"9566c74d-1003-4c4d-bbbb-0407d1e2c649"`) // Try to update a consent (must pass) - do("PUT", "/api/sge/consent/52fdfc07-2182-454f-963f-5f0f9a621d72", sgeApiHeader, `{"serviceId":123456}`, http.StatusOK, `{"ID":"52fdfc07-2182-454f-963f-5f0f9a621d72"`) + do("PUT", "/api/sge/consent/52fdfc07-2182-454f-963f-5f0f9a621d72", boApiHeader, `{"serviceId":123456}`, http.StatusOK, `{"ID":"52fdfc07-2182-454f-963f-5f0f9a621d72"`) // Try to get a consent (must pass) - do("GET", "/api/sge/consent/52fdfc07-2182-454f-963f-5f0f9a621d72", sgeApiHeader, "", http.StatusOK, `{"ID":"52fdfc07-2182-454f-963f-5f0f9a621d72"`) + do("GET", "/api/sge/consent/52fdfc07-2182-454f-963f-5f0f9a621d72", boApiHeader, "", http.StatusOK, `{"ID":"52fdfc07-2182-454f-963f-5f0f9a621d72"`) // Try to get a consent that doesn't exist (must fail not found) - do("GET", "/api/sge/consent/3", sgeApiHeader, "", http.StatusNotFound, `consent not found`) + do("GET", "/api/sge/consent/3", boApiHeader, "", http.StatusNotFound, `consent not found`) // Try to delete a consent (must pass) - do("DELETE", "/api/sge/consent/52fdfc07-2182-454f-963f-5f0f9a621d72", sgeApiHeader, "", http.StatusOK, "") + do("DELETE", "/api/sge/consent/52fdfc07-2182-454f-963f-5f0f9a621d72", boApiHeader, "", http.StatusOK, "") +} + +func grdfTests(t *testing.T) { + // Create the tester + ts, do, _ := createTester(t) + defer ts.Close() // Close the tester + // Try to create a consent (must fail) + do("POST", "/api/grdf/consent", noH, grdfConsentStr, http.StatusUnauthorized, ErrorInvalidToken) + // Try to get a consent (must fail) + do("GET", "/api/grdf/consent/1", noH, "", http.StatusUnauthorized, ErrorInvalidToken) + // Try to delete a consent (must fail) + do("DELETE", "/api/grdf/consent/1", noH, "", http.StatusUnauthorized, ErrorInvalidToken) + + // Create correct authorization header + boApiHeader := map[string]string{"Authorization": "Bearer " + auth.SGEApiToken} + // Try to create a consent (must pass) + do("POST", "/api/grdf/consent", boApiHeader, grdfConsentStr, http.StatusCreated, `{"ID":"81855ad8-681d-4d86-91e9-1e00167939cb"`) + // Try to create another consent (must pass) + do("POST", "/api/grdf/consent", boApiHeader, otherGrdfConsentStr, http.StatusCreated, `{"ID":"6694d2c4-22ac-4208-a007-2939487f6999"`) + // Try to get a consent (must pass) + do("GET", "/api/grdf/consent/81855ad8-681d-4d86-91e9-1e00167939cb", boApiHeader, "", http.StatusOK, `{"ID":"81855ad8-681d-4d86-91e9-1e00167939cb"`) + // Try to get a consent that doesn't exist (must fail not found) + do("GET", "/api/grdf/consent/3", boApiHeader, "", http.StatusNotFound, `consent not found`) + // Try to delete a consent (must pass) + do("DELETE", "/api/grdf/consent/81855ad8-681d-4d86-91e9-1e00167939cb", boApiHeader, "", http.StatusOK, "") } func createTester(t *testing.T) (*httptest.Server, tester.DoFn, tester.DoFn) {