Skip to content

Commit

Permalink
Merge pull request #12 from DavyJ0nes/11-add-configurable-headers
Browse files Browse the repository at this point in the history
Add configurable response headers
  • Loading branch information
DavyJ0nes authored Nov 6, 2019
2 parents 849b9d2 + 8497e20 commit 2b31c82
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:
- docker

go:
- '1.12.6'
- '1.13'

env:
- GO111MODULE=on
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ARG APP_NAME=stubby
ARG MAIN_PATH=cmd/main.go

# -- Builder Image
FROM golang:1.12.6-stretch As Builder
FROM golang:1.13-stretch As Builder

ARG ORG_NAME
ARG REPO_NAME
Expand All @@ -23,7 +23,7 @@ WORKDIR /go/src/github.com/${ORG_NAME}/${REPO_NAME}
# Set up dependencies
COPY ./go.mod go.mod
COPY ./go.sum go.sum
RUN go mod vendor
RUN go mod download

# Copy rest of the package code
COPY . .
Expand Down
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ all: help
USERNAME = davyj0nes
APP_NAME = stubby

APP_PORT = 8080
LOCAL_PORT = 8080

VERSION = $(shell git describe --exact-match --tags 2>/dev/null)
COMMIT = $(shell git rev-parse HEAD | cut -c 1-6)
BUILD_TIME = $(shell date -u '+%Y-%m-%d_%I:%M:%S%p')

APP_PORT = 8080
LOCAL_PORT = 8080

BUILD_PREFIX = CGO_ENABLED=0 GOOS=linux
BUILD_FLAGS = -a -tags netgo --installsuffix netgo
LDFLAGS = -ldflags "-s -w -X ${GO_PROJECT_PATH}/cmd.Release=${VERSION} -X ${GO_PROJECT_PATH}/cmd.Commit=${COMMIT} -X ${GO_PROJECT_PATH}/cmd.BuildTime=${BUILD_TIME}"
Expand Down Expand Up @@ -49,7 +49,7 @@ publish:
.PHONY: run_image
run_image:
$(call blue, "# Running Docker Image Locally...")
@docker run -it --rm --name ${APP_NAME} -v ${PWD}/config.yaml:/config.yaml -p ${LOCAL_PORT}:${APP_PORT} ${USERNAME}/${APP_NAME}:${VERSION}
@docker run -it --rm --name ${APP_NAME} -v ${PWD}/config.yaml:/config.yaml -p ${LOCAL_PORT}:${APP_PORT} ${USERNAME}/${APP_NAME}

## test: run test suites
.PHONY: test
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Return stubbed HTTP responses defined in a config file

### Configuration

### Basic

Add the routes and the responses that you want in the [config file](./comfig.yaml).

A basic route definition would look like:
Expand All @@ -37,6 +39,8 @@ routes:
}
```
### URL Query Params
If the response has URL parameters then these need to be defined as follows:
```yaml
Expand All @@ -57,6 +61,24 @@ The reason for having them defined in a list rather than as a key/value pair
is due to how the (Queries](https://www.gorillatoolkit.org/pkg/mux#Route.Queries)
method is defined in the router package used ([gorilla mux](https://www.gorillatoolkit.org)).
### Custom Response Headers
If you want the response to include a header then you can add it as such:
```yaml
routes:
- path: /foo
status: 200
headers:
X-Custom: Header
X-Request-Id: ef835eaf-a658-458b-86ae-d2d771f5e745
respose: >-
{
"id": 987,
"message": "bar"
}
```
### Docker
The artifact is stored as a docker image and is located on [Docker Hub](https://hub.docker.com/r/davyj0nes/stubby)
Expand Down
36 changes: 34 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package main

import (
"context"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"

"github.com/davyj0nes/stubby/config"
"github.com/davyj0nes/stubby/router"
Expand All @@ -22,6 +27,33 @@ func main() {
r := router.NewRouter(cfg.Routes)
addr := fmt.Sprintf(":%d", cfg.Port)

log.Println("starting server on ", addr)
log.Fatal(http.ListenAndServe(addr, r))
srv := http.Server{
Addr: addr,
Handler: r,
ReadTimeout: 1 * time.Second,
WriteTimeout: 5 * time.Second,
}

interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)

log.Println("starting stubby on ", addr)
go func() {
log.Fatal(srv.ListenAndServe())

}()

log.Print("stubby is ready to serve...")

killSignal := <-interrupt
switch killSignal {
case os.Interrupt:
log.Println("got SIGINT...")
log.Println("stubby is shutting down...")
case syscall.SIGTERM:
log.Println("got SIGTERM...")
log.Println("stubby is shutting down...")
}

log.Fatal(srv.Shutdown(context.Background()))
}
14 changes: 12 additions & 2 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
port: 8888
port: 8080
routes:
- path: /foo
status: 200
Expand All @@ -13,7 +13,7 @@ routes:
- show_deleted
- true
status: 200
respose: >-
response: >-
{
"id": 987,
"message": "bar"
Expand All @@ -24,3 +24,13 @@ routes:
{
"message": "unauthorized"
}
- path: /withResponseHeaders
headers:
X-Request-Id: ef835eaf-a658-458b-86ae-d2d771f5e745
CustomHeader: booyah
status: 200
response: >-
{
"id": 123,
"message": "booyah"
}
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module github.com/davyj0nes/stubby

go 1.12
go 1.13

require (
github.com/gorilla/mux v1.7.2
github.com/spf13/viper v1.4.0
github.com/gorilla/mux v1.7.3
github.com/spf13/viper v1.5.0
)
18 changes: 10 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I=
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
Expand All @@ -53,8 +53,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
Expand Down Expand Up @@ -86,11 +86,13 @@ github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4=
github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
Expand Down Expand Up @@ -136,6 +138,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
1 change: 1 addition & 0 deletions route.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ type Route struct {
Path string
Response string
Status int
Headers map[string]string
Queries []string
}
13 changes: 11 additions & 2 deletions router/router.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package router

import (
"fmt"
"log"
"net/http"

Expand All @@ -17,6 +16,7 @@ func NewRouter(routes []stubby.Route) *mux.Router {
h := Handler{
Response: route.Response,
Status: checkStatus(route.Status),
Headers: route.Headers,
}

r.NewRoute().
Expand All @@ -32,16 +32,25 @@ func NewRouter(routes []stubby.Route) *mux.Router {
type Handler struct {
Response string
Status int
Headers map[string]string
}

// ServeHTTP is used to adhere to the http.Handler interface
func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
log.Printf("received (%s) request to %s", req.Method, req.URL.String())

for k, v := range h.Headers {
w.Header().Add(k, v)
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(h.Status)

fmt.Fprintf(w, h.Response)
_, err := w.Write([]byte(h.Response))
if err != nil {
log.Printf("err writing response: %s", err)
}

}

func checkStatus(status int) int {
Expand Down
38 changes: 35 additions & 3 deletions router/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ import (
"github.com/davyj0nes/stubby/router"
)

type customHeader struct {
Key string
Value string
}

type expected struct {
body string
status int
body string
customHeader customHeader
status int
}

func TestNewRouter(t *testing.T) {
Expand Down Expand Up @@ -74,6 +80,22 @@ func TestNewRouter(t *testing.T) {
status: http.StatusOK,
},
},
{
name: "supplied route with headers matches the right handler",
path: "/head",
routes: []stubby.Route{
{
Path: "/head",
Headers: map[string]string{"Custom": "custom"},
Response: "at the head",
},
},
want: expected{
body: "at the head",
status: http.StatusOK,
customHeader: customHeader{Key: "Custom", Value: "custom"},
},
},
}

for _, tt := range tests {
Expand All @@ -91,8 +113,12 @@ func TestNewRouter(t *testing.T) {
t.Errorf("expected: (%d), got: (%d)", tt.want.status, res.StatusCode)
}

body := getResponseBody(t, res)
headerVal := getResponseHeader(t, res, tt.want.customHeader.Key)
if headerVal != tt.want.customHeader.Value {
t.Errorf("expected: (%s), got: (%s)", tt.want.customHeader.Value, headerVal)
}

body := getResponseBody(t, res)
if body != tt.want.body {
t.Errorf("expected: (%s), got: (%s)", tt.want.body, body)
}
Expand All @@ -110,3 +136,9 @@ func getResponseBody(t *testing.T, r *http.Response) string {

return string(body)
}

func getResponseHeader(t *testing.T, r *http.Response, wantHeader string) string {
t.Helper()

return r.Header.Get(wantHeader)
}

0 comments on commit 2b31c82

Please sign in to comment.