Skip to content

Commit

Permalink
Add list CORS configurations endpoint (#463)
Browse files Browse the repository at this point in the history
  • Loading branch information
mthenw authored Jun 15, 2018
1 parent 6821c0a commit 31dd993
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 6 deletions.
39 changes: 33 additions & 6 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,25 @@ This document contains the API documentation for both Events and Configuration A
1. [Crate Event Type](#create-event-type)
1. [Update Event Type](#update-event-type)
1. [Delete Event Type](#delete-event-type)
1. [Get Event Types](#get-event-types)
1. [List Event Types](#list-event-types)
1. [Get Event Type](#get-event-type)
1. [Functions](#functions)
1. [Register Function](#register-function)
1. [Update Function](#update-function)
1. [Delete Function](#delete-function)
1. [Get Functions](#get-functions)
1. [List Functions](#list-functions)
1. [Get Function](#get-function)
1. [Subscriptions](#subscriptions)
1. [Create Subscription](#create-subscription)
1. [Update Subscription](#update-subscription)
1. [Delete Subscription](#delete-subscription)
1. [Get Subscriptions](#get-subscriptions)
1. [List Subscriptions](#list-subscriptions)
1. [Get Subscription](#get-subscription)
1. [CORS](#cors-1)
1. [Create CORS Configuration](#create-cors-configuration)
1. [Update CORS Configuration](#update-cors-configuration)
1. [Delete CORS Configuration](#delete-cors-configuration)
1. [List CORS Configurations](#list-cors-configurations)
1. [Get CORS Configuration](#get-cors-configuration)
1. [Prometheus Metrics](#prometheus-metrics)
1. [Status](#status)
Expand Down Expand Up @@ -195,7 +196,7 @@ Status code:

---

#### Get Event Types
#### List Event Types

**Endpoint**

Expand Down Expand Up @@ -347,7 +348,7 @@ Status code:

---

#### Get Functions
#### List Functions

**Endpoint**

Expand Down Expand Up @@ -473,7 +474,7 @@ Status code:

---

#### Get Subscriptions
#### List Subscriptions

**Endpoint**

Expand Down Expand Up @@ -611,6 +612,32 @@ Status code:

---

#### List CORS Configurations

**Endpoint**

`GET <Configuration API URL>/v1/spaces/<space>/cors`

**Response**

Status code:

* `200 OK` on success

JSON object:

* `cors` - `array` of `object` - CORS configurations
* `space` - `string` - space name
* `corsId` - `string` - CORS configuration ID
* `method` - `string` - endpoint method
* `path` - `string` - endpoint path
* `allowedOrigins` - `array` of `string` - allowed origins
* `allowedMethods` - `array` of `string` - allowed methods
* `allowedHeaders` - `array` of `string` - allowed headers
* `allowCredentials` - `boolean` - allow credentials

---

#### Get CORS Configuration

**Endpoint**
Expand Down
23 changes: 23 additions & 0 deletions docs/openapi/openapi-config-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,22 @@ paths:

/spaces/{spaceName}/cors:
summary: "Operations about CORS configuration"
get:
summary: "List CORS configurations"
tags:
- "cors"
operationId: "ListCORS"
parameters:
- $ref: "#/components/parameters/Space"
responses:
200:
description: "CORS configurations returned"
content:
application/json:
schema:
$ref: "#/components/schemas/CORSes"
500:
$ref: '#/components/responses/Error'
post:
summary: "Create CORS configuration"
tags:
Expand Down Expand Up @@ -522,6 +538,13 @@ components:
$ref: '#/components/schemas/AllowedHeaders'
allowCredentials:
$ref: '#/components/schemas/AllowCredentials'
CORSes:
type: object
properties:
cors:
type: array
items:
$ref: '#/components/schemas/CORS'
AWSFirehose:
type: object
properties:
Expand Down
22 changes: 22 additions & 0 deletions httpapi/httpapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ type SubscriptionsResponse struct {
Subscriptions subscription.Subscriptions `json:"subscriptions"`
}

// CORSResponse is a HTTPAPI JSON response containing cors configuration.
type CORSResponse struct {
CORSes cors.CORSes `json:"cors"`
}

// RegisterRoutes register HTTP API routes
func (h HTTPAPI) RegisterRoutes(router *httprouter.Router) {
router.GET("/v1/status", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {})
Expand All @@ -59,6 +64,7 @@ func (h HTTPAPI) RegisterRoutes(router *httprouter.Router) {
router.PUT("/v1/spaces/:space/subscriptions/:id", h.updateSubscription)
router.DELETE("/v1/spaces/:space/subscriptions/:id", h.deleteSubscription)

router.GET("/v1/spaces/:space/cors", h.listCORS)
router.GET("/v1/spaces/:space/cors/*id", h.getCORS)
router.POST("/v1/spaces/:space/cors", h.createCORS)
router.PUT("/v1/spaces/:space/cors/*id", h.updateCORS)
Expand Down Expand Up @@ -473,6 +479,22 @@ func (h HTTPAPI) deleteSubscription(w http.ResponseWriter, r *http.Request, para
metricConfigRequests.WithLabelValues(space, "subscription", "delete").Inc()
}

func (h HTTPAPI) listCORS(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
w.Header().Set("Content-Type", "application/json")
encoder := json.NewEncoder(w)

space := params.ByName("space")
configs, err := h.CORSes.GetCORSes(space)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
encoder.Encode(&Response{Errors: []Error{{Message: err.Error()}}})
} else {
encoder.Encode(&CORSResponse{configs})
}

metricConfigRequests.WithLabelValues(space, "cors", "list").Inc()
}

func (h HTTPAPI) getCORS(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
w.Header().Set("Content-Type", "application/json")
encoder := json.NewEncoder(w)
Expand Down
33 changes: 33 additions & 0 deletions httpapi/httpapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,39 @@ func TestGetCORS(t *testing.T) {
})
}

func TestGetCORSes(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
router, _, _, _, corses := setup(ctrl)

t.Run("CORS configurations returned", func(t *testing.T) {
returnedList := cors.CORSes{{
Space: "default",
ID: cors.ID("GET%2Fhello"),
}}
corses.EXPECT().GetCORSes("default").Return(returnedList, nil)

resp := request(router, http.MethodGet, "/v1/spaces/default/cors", nil)

configs := &httpapi.CORSResponse{}
json.Unmarshal(resp.Body.Bytes(), configs)
assert.Equal(t, http.StatusOK, resp.Code)
assert.Equal(t, "default", configs.CORSes[0].Space)
assert.Equal(t, cors.ID("GET%2Fhello"), configs.CORSes[0].ID)
})

t.Run("internal error", func(t *testing.T) {
corses.EXPECT().GetCORSes(gomock.Any()).Return(nil, errors.New("processing failed"))

resp := request(router, http.MethodGet, "/v1/spaces/default/cors", nil)

httpresp := &httpapi.Response{}
json.Unmarshal(resp.Body.Bytes(), httpresp)
assert.Equal(t, http.StatusInternalServerError, resp.Code)
assert.Equal(t, "processing failed", httpresp.Errors[0].Message)
})
}

func TestCreateCORS(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
Expand Down
23 changes: 23 additions & 0 deletions libkv/cors.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,29 @@ func (service Service) GetCORS(space string, id cors.ID) (*cors.CORS, error) {
return &config, nil
}

// GetCORSes returns an array of all CORS configuration in the space.
func (service Service) GetCORSes(space string) (cors.CORSes, error) {
configs := []*cors.CORS{}

kvs, err := service.CORSStore.List(spacePath(space), &store.ReadOptions{Consistent: true})
if err != nil && err.Error() != errKeyNotFound {
return nil, err
}

for _, kv := range kvs {
config := &cors.CORS{}
dec := json.NewDecoder(bytes.NewReader(kv.Value))
err = dec.Decode(config)
if err != nil {
return nil, err
}

configs = append(configs, config)
}

return cors.CORSes(configs), nil
}

// UpdateCORS updates CORS configuration.
func (service Service) UpdateCORS(config *cors.CORS) (*cors.CORS, error) {
if err := validateCORS(config); err != nil {
Expand Down
40 changes: 40 additions & 0 deletions libkv/cors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,46 @@ func TestGetCORS(t *testing.T) {
})
}

func TestGetCORSes(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

testConfig := &cors.CORS{Space: "default", ID: "GET%2Fhello"}
testPayload := []byte(`{"space":"default","corsId":"GET%2Fhello"}}`)

t.Run("configurations returned", func(t *testing.T) {
kvs := []*store.KVPair{&store.KVPair{Value: testPayload}}
db := mock.NewMockStore(ctrl)
db.EXPECT().List("default/", &store.ReadOptions{Consistent: true}).Return(kvs, nil)
service := &Service{CORSStore: db, Log: zap.NewNop()}

list, err := service.GetCORSes("default")

assert.Nil(t, err)
assert.Equal(t, cors.CORSes{testConfig}, list)
})

t.Run("KV List error", func(t *testing.T) {
db := mock.NewMockStore(ctrl)
db.EXPECT().List(gomock.Any(), gomock.Any()).Return([]*store.KVPair{}, errors.New("KV list err"))
service := &Service{CORSStore: db, Log: zap.NewNop()}

_, err := service.GetCORSes("default")

assert.EqualError(t, err, "KV list err")
})

t.Run("KV List directory not found", func(t *testing.T) {
db := mock.NewMockStore(ctrl)
db.EXPECT().List(gomock.Any(), gomock.Any()).Return([]*store.KVPair{}, errors.New("Key not found in store"))
service := &Service{CORSStore: db, Log: zap.NewNop()}

list, _ := service.GetCORSes("default")

assert.Equal(t, cors.CORSes{}, list)
})
}

func TestUpdateCORS(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
Expand Down
13 changes: 13 additions & 0 deletions mock/cors.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions subscription/cors/cors.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ type CORS struct {
AllowCredentials bool `json:"allowCredentials"`
}

// CORSes is an array of CORS configurations.
type CORSes []*CORS

// MarshalLogObject is a part of zapcore.ObjectMarshaler interface
func (c CORS) MarshalLogObject(enc zapcore.ObjectEncoder) error {
enc.AddString("space", string(c.Space))
Expand Down
1 change: 1 addition & 0 deletions subscription/cors/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ type Service interface {
CreateCORS(c *CORS) (*CORS, error)
UpdateCORS(c *CORS) (*CORS, error)
GetCORS(space string, id ID) (*CORS, error)
GetCORSes(space string) (CORSes, error)
DeleteCORS(space string, id ID) error
}

0 comments on commit 31dd993

Please sign in to comment.