Skip to content

Commit 990cea0

Browse files
committed
Merge branch 'release/0.11.0'
2 parents 1289100 + 7920f1c commit 990cea0

27 files changed

+564
-473
lines changed

api/api.go

Lines changed: 94 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package api
22

33
import (
4+
"context"
5+
"encoding/json"
46
"net/http"
57

6-
"github.com/ONSdigital/dp-permissions-api/config"
7-
88
"github.com/ONSdigital/dp-authorisation/v2/authorisation"
9-
9+
"github.com/ONSdigital/dp-permissions-api/config"
1010
"github.com/ONSdigital/dp-permissions-api/models"
11-
11+
"github.com/ONSdigital/log.go/v2/log"
1212
"github.com/gorilla/mux"
1313
)
1414

@@ -22,6 +22,20 @@ type API struct {
2222
maximumDefaultLimit int
2323
}
2424

25+
type baseHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request) (*models.SuccessResponse, *models.ErrorResponse)
26+
27+
func contextAndErrors(h baseHandler) http.HandlerFunc {
28+
return func(w http.ResponseWriter, req *http.Request) {
29+
ctx := req.Context()
30+
response, err := h(ctx, w, req)
31+
if err != nil {
32+
writeErrorResponse(ctx, w, err)
33+
return
34+
}
35+
writeSuccessResponse(ctx, w, response)
36+
}
37+
}
38+
2539
//Setup function sets up the api and returns an api
2640
func Setup(
2741
cfg *config.Config,
@@ -39,13 +53,82 @@ func Setup(
3953
bundler: bundler,
4054
}
4155

42-
r.HandleFunc("/v1/roles", auth.Require(models.RolesRead, api.GetRolesHandler)).Methods(http.MethodGet)
43-
r.HandleFunc("/v1/roles/{id}", auth.Require(models.RolesRead, api.GetRoleHandler)).Methods(http.MethodGet)
44-
r.HandleFunc("/v1/policies", auth.Require(models.PoliciesCreate, api.PostPolicyHandler)).Methods(http.MethodPost)
45-
r.HandleFunc("/v1/policies/{id}", auth.Require(models.PoliciesRead, api.GetPolicyHandler)).Methods(http.MethodGet)
46-
r.HandleFunc("/v1/policies/{id}", auth.Require(models.PoliciesUpdate, api.UpdatePolicyHandler)).Methods(http.MethodPut)
47-
r.HandleFunc("/v1/policies/{id}", auth.Require(models.PoliciesDelete, api.DeletePolicyHandler)).Methods(http.MethodDelete)
48-
r.HandleFunc("/v1/permissions-bundle", api.GetPermissionsBundleHandler).Methods(http.MethodGet)
56+
r.HandleFunc("/v1/roles", auth.Require(models.RolesRead, contextAndErrors(api.GetRolesHandler))).Methods(http.MethodGet)
57+
r.HandleFunc("/v1/roles/{id}", auth.Require(models.RolesRead, contextAndErrors(api.GetRoleHandler))).Methods(http.MethodGet)
58+
r.HandleFunc("/v1/policies", auth.Require(models.PoliciesCreate, contextAndErrors(api.PostPolicyHandler))).Methods(http.MethodPost)
59+
r.HandleFunc("/v1/policies/{id}", auth.Require(models.PoliciesRead, contextAndErrors(api.GetPolicyHandler))).Methods(http.MethodGet)
60+
r.HandleFunc("/v1/policies/{id}", auth.Require(models.PoliciesUpdate, contextAndErrors(api.UpdatePolicyHandler))).Methods(http.MethodPut)
61+
r.HandleFunc("/v1/policies/{id}", auth.Require(models.PoliciesDelete, contextAndErrors(api.DeletePolicyHandler))).Methods(http.MethodDelete)
62+
r.HandleFunc("/v1/permissions-bundle", contextAndErrors(api.GetPermissionsBundleHandler)).Methods(http.MethodGet)
4963

5064
return api
5165
}
66+
67+
func writeErrorResponse(ctx context.Context, w http.ResponseWriter, errorResponse *models.ErrorResponse) {
68+
// override internal server error response to prevent leaking sensitive data
69+
if errorResponse.Status == http.StatusInternalServerError {
70+
http.Error(w, models.InternalServerErrorDescription, http.StatusInternalServerError)
71+
return
72+
}
73+
74+
w.Header().Set("Content-Type", "application/json; charset=utf-8")
75+
// process custom headers
76+
for key, value := range errorResponse.Headers {
77+
w.Header().Set(key, value)
78+
}
79+
80+
w.WriteHeader(errorResponse.Status)
81+
82+
jsonResponse, err := json.Marshal(errorResponse)
83+
if err != nil {
84+
responseErr := models.NewError(ctx, err, models.JSONMarshalError, models.ErrorMarshalFailedDescription, nil)
85+
http.Error(w, responseErr.Description, http.StatusInternalServerError)
86+
return
87+
}
88+
89+
_, err = w.Write(jsonResponse)
90+
if err != nil {
91+
responseErr := models.NewError(ctx, err, models.WriteResponseError, models.WriteResponseFailedDescription, nil)
92+
http.Error(w, responseErr.Description, http.StatusInternalServerError)
93+
return
94+
}
95+
}
96+
97+
func writeSuccessResponse(ctx context.Context, w http.ResponseWriter, successResponse *models.SuccessResponse) {
98+
w.Header().Set("Content-Type", "application/json; charset=utf-8")
99+
// process custom headers
100+
for key, value := range successResponse.Headers {
101+
w.Header().Set(key, value)
102+
}
103+
w.WriteHeader(successResponse.Status)
104+
105+
_, err := w.Write(successResponse.Body)
106+
if err != nil {
107+
responseErr := models.NewError(ctx, err, models.WriteResponseError, models.WriteResponseFailedDescription, nil)
108+
http.Error(w, responseErr.Description, http.StatusInternalServerError)
109+
return
110+
}
111+
}
112+
113+
func handleInvalidQueryParameterError(ctx context.Context, err error, name string, value string) *models.ErrorResponse {
114+
logData := log.Data{name: value}
115+
return models.NewErrorResponse(http.StatusBadRequest,
116+
nil,
117+
models.NewError(ctx, err, models.InvalidQueryParameterError, models.InvalidQueryParameterDescription, logData),
118+
)
119+
}
120+
121+
func handleBodyMarshalError(ctx context.Context, err error, name string, value interface{}) *models.ErrorResponse {
122+
logData := log.Data{name: value}
123+
return models.NewErrorResponse(http.StatusInternalServerError,
124+
nil,
125+
models.NewError(ctx, err, models.JSONMarshalError, models.MarshalFailedDescription, logData),
126+
)
127+
}
128+
129+
func handleBodyUnmarshalError(ctx context.Context, err error) *models.ErrorResponse {
130+
return models.NewErrorResponse(http.StatusBadRequest,
131+
nil,
132+
models.NewError(ctx, err, models.JSONUnmarshalError, models.UnmarshalFailedDescription, nil),
133+
)
134+
}

api/bundle.go

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,32 @@
11
package api
22

33
import (
4+
"context"
45
"encoding/json"
5-
"github.com/ONSdigital/log.go/v2/log"
66
"net/http"
7+
8+
"github.com/ONSdigital/dp-permissions-api/models"
79
)
810

911
// GetPermissionsBundleHandler gets and returns the permissions bundle as JSON in the HTTP response body.
10-
func (api *API) GetPermissionsBundleHandler(w http.ResponseWriter, req *http.Request) {
11-
ctx := req.Context()
12+
func (api *API) GetPermissionsBundleHandler(ctx context.Context, w http.ResponseWriter, req *http.Request) (*models.SuccessResponse, *models.ErrorResponse) {
1213

1314
bundle, err := api.bundler.Get(ctx)
1415
if err != nil {
15-
log.Error(ctx, "failed to get permissions bundle", err)
16-
http.Error(w, err.Error(), http.StatusInternalServerError)
17-
return
16+
return nil, handleGetPermissionsBundleError(ctx, err)
1817
}
1918

2019
b, err := json.Marshal(bundle)
2120
if err != nil {
22-
log.Error(ctx, "failed to marshal permissions bundle to json", err)
23-
http.Error(w, err.Error(), http.StatusInternalServerError)
24-
return
21+
return nil, handleBodyMarshalError(ctx, err, "bundle", bundle)
2522
}
2623

27-
w.Header().Set("Content-Type", "application/json; charset=utf-8")
24+
return models.NewSuccessResponse(b, http.StatusOK, nil), nil
25+
}
2826

29-
if _, err := w.Write(b); err != nil {
30-
log.Error(ctx, "error writing permissions bundle response", err)
31-
http.Error(w, err.Error(), http.StatusInternalServerError)
32-
return
33-
}
34-
log.Info(ctx, "successfully retrieved permissions bundle")
27+
func handleGetPermissionsBundleError(ctx context.Context, err error) *models.ErrorResponse {
28+
return models.NewErrorResponse(http.StatusInternalServerError,
29+
nil,
30+
models.NewError(ctx, err, models.GetPermissionBundleError, models.GetPermissionBundleErrorDescription, nil),
31+
)
3532
}

api/bundle_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import (
44
"context"
55
"encoding/json"
66
"errors"
7-
"github.com/ONSdigital/dp-permissions-api/api/mock"
8-
"github.com/ONSdigital/dp-permissions-api/models"
9-
. "github.com/smartystreets/goconvey/convey"
107
"io/ioutil"
118
"net/http"
129
"net/http/httptest"
1310
"testing"
11+
12+
"github.com/ONSdigital/dp-permissions-api/api/mock"
13+
"github.com/ONSdigital/dp-permissions-api/models"
14+
. "github.com/smartystreets/goconvey/convey"
1415
)
1516

1617
func TestAPI_GetPermissionsBundleHandler(t *testing.T) {

0 commit comments

Comments
 (0)