Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add list CORS configurations endpoint #463

Merged
merged 3 commits into from
Jun 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}