Skip to content
This repository was archived by the owner on Feb 17, 2025. It is now read-only.

Commit b7e9464

Browse files
committed
UI Dev Proxy
0 parents  commit b7e9464

File tree

15 files changed

+1057
-0
lines changed

15 files changed

+1057
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ui-dev-proxy

Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
build:
2+
go build -o ui-dev-proxy main.go
3+
4+
test:
5+
go vet ./...
6+
go test -race -short ./...
7+
bash -c 'diff -u <(echo -n) <(gofmt -s -d .)'
8+
9+
fmt:
10+
go fmt ./...

README.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# UI Dev Proxy
2+
3+
Proxy tool for development of UIs.
4+
5+
## Installation
6+
7+
```
8+
go get -u github.com/JSainsburyPLC/ui-dev-proxy
9+
```
10+
11+
## Usage
12+
13+
```
14+
# start proxy server with default backend and config file
15+
ui-dev-proxy start -u https://default-backend-url.example.com -c proxy-config.json
16+
```
17+
18+
For additional options see help
19+
20+
```
21+
ui-dev-proxy start --help
22+
```
23+
24+
## How it works
25+
26+
The proxy can handle requests in 3 different ways:
27+
28+
* Pass through to backend when path matches pattern in config route
29+
* Return a mock response when request matches config route (when mocks are enabled)
30+
* Pass through to default backend, if request doesn't match any config routes
31+
32+
## Configuring your UI app
33+
34+
Routes are configured in a JSON file, which is passed to the proxy using the "-c" flag.
35+
36+
See `examples/config.json`
37+
38+
### Proxy type routes
39+
40+
```
41+
{
42+
"type": "proxy", // Required
43+
"path_pattern": "^/test-ui/.*", // regex to match request path. Required
44+
"backend": "http://localhost:3000" // backend scheme and host to proxy to. Required
45+
}
46+
```
47+
48+
### Mock type routes
49+
50+
```
51+
{
52+
"type": "mock", // Required
53+
"mock": {
54+
"request": { // parameters to match the inbound request on.
55+
"method": "GET", // match the method of the request. Optional
56+
"path": "^/api/v1/product/.*", // match the path of the request. Required
57+
"query": "include=.*" // match the query string of the request. Optional
58+
},
59+
"response": { // definition of the mock data to respond with.
60+
"status": 200, // the status code. Required
61+
"body": "mocks/product.json", // string body, or path to JSON file. Required
62+
"cookies": [ // set cookies. Optional
63+
{
64+
"name": "SOME_COOKIE",
65+
"value": "1234567890",
66+
"maxAge": 604800
67+
}
68+
]
69+
}
70+
}
71+
}
72+
```

commands/start.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package commands
2+
3+
import (
4+
"github.com/JSainsburyPLC/ui-dev-proxy/domain"
5+
"github.com/JSainsburyPLC/ui-dev-proxy/proxy"
6+
"github.com/urfave/cli"
7+
"log"
8+
"net/url"
9+
)
10+
11+
func StartCommand(logger *log.Logger, confProvider domain.ConfigProvider) cli.Command {
12+
return cli.Command{
13+
Name: "start",
14+
Usage: "Start the proxy",
15+
Flags: []cli.Flag{
16+
cli.StringFlag{
17+
Name: "default-backend-url, u",
18+
Usage: "the default backend to use",
19+
Required: true,
20+
},
21+
cli.StringFlag{
22+
Name: "config, c",
23+
Usage: "Load configuration from 'FILE'",
24+
Required: true,
25+
},
26+
cli.IntFlag{
27+
Name: "port, p",
28+
Usage: "The port to start proxy on",
29+
Value: 8080,
30+
},
31+
cli.BoolFlag{
32+
Name: "enable-mocks, m",
33+
Usage: "Turn on mocks",
34+
},
35+
cli.BoolFlag{
36+
Name: "tls-enabled",
37+
Usage: "Turn on TLS (tls-certfile and tls-keyfile both required if this is true)",
38+
},
39+
cli.StringFlag{
40+
Name: "tls-certfile",
41+
Usage: "Path to TLS certificate file",
42+
},
43+
cli.StringFlag{
44+
Name: "tls-keyfile",
45+
Usage: "Path to TLS key file",
46+
},
47+
},
48+
Action: startAction(logger, confProvider),
49+
}
50+
}
51+
52+
func startAction(logger *log.Logger, confProvider domain.ConfigProvider) cli.ActionFunc {
53+
return func(c *cli.Context) error {
54+
logger.Println("Starting UI Dev Proxy...")
55+
56+
defaultBackendUrl := c.String("default-backend-url")
57+
confFile := c.String("config")
58+
port := c.Int("port")
59+
mocksEnabled := c.Bool("enable-mocks")
60+
tlsEnabled := c.Bool("tls-enabled")
61+
tlsCertfile := c.String("tls-certfile")
62+
tlsKeyfile := c.String("tls-keyfile")
63+
64+
logger.Printf("Default backend URL: %s\n", defaultBackendUrl)
65+
logger.Printf("Config file: %s\n", confFile)
66+
logger.Printf("Port: %d\n", port)
67+
logger.Printf("Mocks enabled: %t\n", mocksEnabled)
68+
logger.Printf("TLS enabled: %t\n", tlsEnabled)
69+
if tlsEnabled {
70+
logger.Printf("TLS certfile: %s\n", tlsCertfile)
71+
logger.Printf("TLS keyfile: %s\n", tlsKeyfile)
72+
}
73+
74+
conf, err := confProvider(confFile)
75+
if err != nil {
76+
return cli.NewExitError(err, 1)
77+
}
78+
79+
defaultBackend, err := url.Parse(defaultBackendUrl)
80+
if err != nil {
81+
return cli.NewExitError(err, 1)
82+
}
83+
84+
p := proxy.NewProxy(port, conf, defaultBackend, mocksEnabled, logger)
85+
86+
if tlsEnabled {
87+
p.TlsEnabled = true
88+
p.TlsCertFile = tlsCertfile
89+
p.TlsKeyFile = tlsKeyfile
90+
}
91+
92+
p.Start()
93+
94+
return nil
95+
}
96+
}

domain/config.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package domain
2+
3+
import (
4+
"encoding/json"
5+
"net/url"
6+
"regexp"
7+
)
8+
9+
const (
10+
RouteTypeProxy = "proxy"
11+
RouteTypeMock = "mock"
12+
)
13+
14+
type Config struct {
15+
Routes []Route `json:"routes"`
16+
}
17+
18+
type Route struct {
19+
Type string `json:"type"`
20+
PathPattern *PathPattern `json:"path_pattern"`
21+
Backend *Backend `json:"backend"`
22+
Mock *Mock `json:"mock"`
23+
}
24+
25+
type PathPattern struct {
26+
*regexp.Regexp
27+
}
28+
29+
func (p *PathPattern) UnmarshalJSON(data []byte) error {
30+
var s string
31+
err := json.Unmarshal(data, &s)
32+
if err != nil {
33+
return err
34+
}
35+
36+
r, err := regexp.Compile(s)
37+
if err != nil {
38+
return err
39+
}
40+
41+
p.Regexp = r
42+
43+
return nil
44+
}
45+
46+
type Backend struct {
47+
*url.URL
48+
}
49+
50+
func (p *Backend) UnmarshalJSON(data []byte) error {
51+
var s string
52+
err := json.Unmarshal(data, &s)
53+
if err != nil {
54+
return err
55+
}
56+
57+
u, err := url.Parse(s)
58+
if err != nil {
59+
return err
60+
}
61+
62+
p.URL = u
63+
64+
return nil
65+
}
66+
67+
// TODO: the path arg here is a leaky abstraction - fix it
68+
type ConfigProvider func(path string) (Config, error)

0 commit comments

Comments
 (0)