Skip to content

Commit 6b17fe2

Browse files
authored
Merge pull request #89 from mutablelogic/v5
Added API Prefixes
2 parents 7480245 + 567e39d commit 6b17fe2

File tree

8 files changed

+150
-37
lines changed

8 files changed

+150
-37
lines changed

README.md

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,26 @@ features and functionality as needed. More documentation soon on how to do that.
1616

1717
## Running
1818

19-
You can run the server in a docker container or build it from source. To run the latest
20-
released version as a docker container:
19+
The binary includes both the *server*-side application and the *client*-side
20+
command-line tools. Both can be run as docker containers.
21+
22+
To run the latest released version as a docker container:
2123

2224
```bash
2325
docker run ghcr.io/mutablelogic/go-server:latest
2426
```
2527

26-
This will print out the help message.
28+
This will print out the help message and provide insight into running both the server application
29+
and interacting with the server through commands. To run the server, you'll need a PostgreSQL
30+
database, and you can set the environment variables `PG_HOST`, `PG_DATABASE`, `PG_USER` and `PG_PASS`.
31+
32+
### Bootstrapping the database server and roles
33+
34+
More information about setting up the databses TODO
35+
36+
### Creating a new database
37+
38+
Information about setting up a new database
2739

2840
## Building
2941

@@ -41,10 +53,10 @@ The plugins and the `server` binary will be built in the `build` directory.
4153

4254
You need the following three tools installed to build the server:
4355

44-
- [Go](https://golang.org/doc/install/source) (1.23 or later, not required for docker builds)
45-
- [Make](https://www.gnu.org/software/make/)
46-
- [Docker](https://docs.docker.com/get-docker/)
47-
- [NPM](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
56+
* [Go](https://golang.org/doc/install/source) (1.23 or later, not required for docker builds)
57+
* [Make](https://www.gnu.org/software/make/)
58+
* [Docker](https://docs.docker.com/get-docker/)
59+
* [NPM](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
4860

4961
### Makefile targets
5062

@@ -68,15 +80,14 @@ Binaries are placed in the `build` directory.
6880
You can also affect the build by setting the following environment variables. For example,
6981

7082
```bash
71-
GOOS=linux GOARCH=amd64 make
83+
OS=linux ARCH=amd64 make
7284
```
7385

7486
| Variable | Description |
7587
|----------|-------------|
76-
| `GOOS` | The target operating system for the build |
77-
| `GOARCH` | The target architecture for the build |
88+
| `OS` | The target operating system for the build |
89+
| `ARCH` | The target architecture for the build |
7890
| `BUILD_DIR` | The target architecture for the build |
7991
| `VERBOSE` | Setting this flag will provide verbose output for unit tests |
8092
| `VERSION` | Explicitly set the version |
8193
| `DOCKER_REPO` | The docker repository to push to. Defaults to `ghcr.io/mutablelogic/go-server` |
82-

cmd/server/main.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ import (
99
kong "github.com/alecthomas/kong"
1010
server "github.com/mutablelogic/go-server"
1111
auth "github.com/mutablelogic/go-server/pkg/auth/cmd"
12+
auth_schema "github.com/mutablelogic/go-server/pkg/auth/schema"
1213
certmanager "github.com/mutablelogic/go-server/pkg/cert/cmd"
14+
cert_schema "github.com/mutablelogic/go-server/pkg/cert/schema"
1315
pgmanager "github.com/mutablelogic/go-server/pkg/pgmanager/cmd"
16+
pgmanager_schema "github.com/mutablelogic/go-server/pkg/pgmanager/schema"
1417
pgqueue "github.com/mutablelogic/go-server/pkg/pgqueue/cmd"
18+
pgqueue_schema "github.com/mutablelogic/go-server/pkg/pgqueue/schema"
1519
)
1620

1721
///////////////////////////////////////////////////////////////////////////////
@@ -51,10 +55,10 @@ func main() {
5155
kong.Vars{
5256
"HOST": hostName(),
5357
"USER": userName(),
54-
"CERT_PREFIX": "/cert/v1",
55-
"PG_PREFIX": "/pg/v1",
56-
"AUTH_PREFIX": "/auth/v1",
57-
"QUEUE_PREFIX": "/queue/v1",
58+
"CERT_PREFIX": cert_schema.APIPrefix,
59+
"PG_PREFIX": pgmanager_schema.APIPrefix,
60+
"AUTH_PREFIX": auth_schema.APIPrefix,
61+
"QUEUE_PREFIX": pgqueue_schema.APIPrefix,
5862
},
5963
)
6064

cmd/server/service.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import (
99
// Packages
1010
server "github.com/mutablelogic/go-server"
1111
helloworld "github.com/mutablelogic/go-server/npm/helloworld"
12+
auth_schema "github.com/mutablelogic/go-server/pkg/auth/schema"
13+
cert_schema "github.com/mutablelogic/go-server/pkg/cert/schema"
1214
httpresponse "github.com/mutablelogic/go-server/pkg/httpresponse"
15+
pgmanager_schema "github.com/mutablelogic/go-server/pkg/pgmanager/schema"
16+
pgqueue_schema "github.com/mutablelogic/go-server/pkg/pgqueue/schema"
1317
provider "github.com/mutablelogic/go-server/pkg/provider"
1418
ref "github.com/mutablelogic/go-server/pkg/ref"
1519
types "github.com/mutablelogic/go-server/pkg/types"
@@ -104,6 +108,7 @@ func (cmd *ServiceRunCommand) Run(app server.Cmd) error {
104108
pgpool := config.(*pg.Config)
105109

106110
// Set router
111+
pgpool.Prefix = pgmanager_schema.APIPrefix
107112
if router, ok := provider.Task(ctx, "httprouter.main").(server.HTTPRouter); !ok || router == nil {
108113
return httpresponse.ErrInternalError.With("Invalid router")
109114
} else {
@@ -128,14 +133,15 @@ func (cmd *ServiceRunCommand) Run(app server.Cmd) error {
128133
auth := config.(*auth.Config)
129134

130135
// Set the router
131-
if router, ok := ref.Provider(ctx).Task(ctx, "httprouter").(server.HTTPRouter); !ok || router == nil {
136+
auth.Prefix = auth_schema.APIPrefix
137+
if router, ok := ref.Provider(ctx).Task(ctx, "httprouter.main").(server.HTTPRouter); !ok || router == nil {
132138
return httpresponse.ErrInternalError.With("Invalid router")
133139
} else {
134140
auth.Router = router
135141
}
136142

137143
// Set the connection pool
138-
if pool, ok := ref.Provider(ctx).Task(ctx, "pgpool").(server.PG); !ok || pool == nil {
144+
if pool, ok := ref.Provider(ctx).Task(ctx, "pgpool.main").(server.PG); !ok || pool == nil {
139145
return httpresponse.ErrInternalError.With("Invalid connection pool")
140146
} else {
141147
auth.Pool = pool
@@ -148,14 +154,15 @@ func (cmd *ServiceRunCommand) Run(app server.Cmd) error {
148154
pgqueue := config.(*pgqueue.Config)
149155

150156
// Set the router
151-
if router, ok := ref.Provider(ctx).Task(ctx, "httprouter").(server.HTTPRouter); !ok || router == nil {
157+
pgqueue.Prefix = pgqueue_schema.APIPrefix
158+
if router, ok := ref.Provider(ctx).Task(ctx, "httprouter.main").(server.HTTPRouter); !ok || router == nil {
152159
return httpresponse.ErrInternalError.With("Invalid router")
153160
} else {
154161
pgqueue.Router = router
155162
}
156163

157164
// Set the connection pool
158-
if pool, ok := ref.Provider(ctx).Task(ctx, "pgpool").(server.PG); !ok || pool == nil {
165+
if pool, ok := ref.Provider(ctx).Task(ctx, "pgpool.main").(server.PG); !ok || pool == nil {
159166
return httpresponse.ErrInternalError.With("Invalid connection pool")
160167
} else {
161168
pgqueue.Pool = pool
@@ -167,21 +174,22 @@ func (cmd *ServiceRunCommand) Run(app server.Cmd) error {
167174
certmanager := config.(*cert.Config)
168175

169176
// Set the router
170-
if router, ok := ref.Provider(ctx).Task(ctx, "httprouter").(server.HTTPRouter); !ok || router == nil {
177+
certmanager.Prefix = cert_schema.APIPrefix
178+
if router, ok := ref.Provider(ctx).Task(ctx, "httprouter.main").(server.HTTPRouter); !ok || router == nil {
171179
return httpresponse.ErrInternalError.With("Invalid router")
172180
} else {
173181
certmanager.Router = router
174182
}
175183

176184
// Set the connection pool
177-
if pool, ok := ref.Provider(ctx).Task(ctx, "pgpool").(server.PG); !ok || pool == nil {
185+
if pool, ok := ref.Provider(ctx).Task(ctx, "pgpool.main").(server.PG); !ok || pool == nil {
178186
return httpresponse.ErrInternalError.With("Invalid connection pool")
179187
} else {
180188
certmanager.Pool = pool
181189
}
182190

183191
// Set the queue
184-
if queue, ok := ref.Provider(ctx).Task(ctx, "pgqueue").(server.PGQueue); !ok || queue == nil {
192+
if queue, ok := ref.Provider(ctx).Task(ctx, "pgqueue.main").(server.PGQueue); !ok || queue == nil {
185193
return httpresponse.ErrInternalError.With("Invalid task queue")
186194
} else {
187195
certmanager.Queue = queue

pkg/auth/schema/schema.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const (
1515
RootUserName = "root"
1616
UserListLimit = 50
1717
AuthHashAlgorithm = "sha256"
18+
APIPrefix = "/auth/v1"
1819
)
1920

2021
const (

pkg/cert/schema/schema.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
const (
1414
SchemaName = "certmanager"
15+
APIPrefix = "/cert/v1"
1516
)
1617

1718
const (

pkg/pgmanager/schema/globals.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
const (
1313
CatalogSchema = "pg_catalog"
14+
APIPrefix = "/pg/v1"
1415
)
1516

1617
const (

pkg/pgqueue/schema/schema.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const (
1515
SchemaName = "pgqueue"
1616
DefaultNamespace = "default"
1717
CleanupNamespace = "cleanup"
18-
DefaultPrefix = "/queue/v1"
18+
APIPrefix = "/queue/v1"
1919
TopicQueueInsert = "queue_insert"
2020
QueueListLimit = 100
2121
TickerListLimit = 100

pkg/provider/meta/meta.go

Lines changed: 101 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ import (
55
"fmt"
66
"io"
77
"net/url"
8+
"os"
89
"reflect"
9-
"strings"
10+
"strconv"
1011
"time"
1112

1213
// Packages
13-
"github.com/mutablelogic/go-server"
14+
server "github.com/mutablelogic/go-server"
1415
httpresponse "github.com/mutablelogic/go-server/pkg/httpresponse"
1516
ast "github.com/mutablelogic/go-server/pkg/parser/ast"
17+
types "github.com/mutablelogic/go-server/pkg/types"
1618
)
1719

1820
////////////////////////////////////////////////////////////////////////////////
@@ -21,7 +23,9 @@ import (
2123
type Meta struct {
2224
Name string
2325
Description string
26+
Default string
2427
Type reflect.Type
28+
Index []int
2529
Fields []*Meta
2630
}
2731

@@ -93,6 +97,9 @@ func (m *Meta) Write(w io.Writer) error {
9397
buf.WriteString(" // ")
9498
buf.WriteString(field.Description)
9599
}
100+
if field.Default != "" {
101+
buf.WriteString(" (default: " + types.Quote(field.Default) + ")")
102+
}
96103

97104
buf.WriteString("\n")
98105
}
@@ -114,16 +121,20 @@ func (m *Meta) Validate(values any) error {
114121
}
115122

116123
func (m *Meta) New() server.Plugin {
117-
return reflect.New(m.Type).Interface().(server.Plugin)
124+
obj := reflect.New(m.Type)
125+
for _, field := range m.Fields {
126+
// Expand field for env
127+
setValue(obj.Elem().FieldByIndex(field.Index), os.ExpandEnv(field.Default))
128+
}
129+
return obj.Interface().(server.Plugin)
118130
}
119131

120132
////////////////////////////////////////////////////////////////////////////////
121133
// PRIVATE METHODS
122134

123135
func newMetaField(rf reflect.StructField) (*Meta, error) {
124136
meta := new(Meta)
125-
126-
//fmt.Println("newMetaField", rf.Name, rf.Type)
137+
meta.Index = rf.Index
127138

128139
// Name
129140
if name := nameForField(rf, "json", "yaml", "name"); name == "" {
@@ -134,7 +145,16 @@ func newMetaField(rf reflect.StructField) (*Meta, error) {
134145
}
135146

136147
// Description
137-
meta.Description = rf.Tag.Get("help")
148+
if description, _ := valueForField(rf, "description", "help"); description != "" {
149+
meta.Description = description
150+
}
151+
152+
// Env - needs to be an identififer
153+
if env, _ := valueForField(rf, "env"); types.IsIdentifier(env) {
154+
meta.Default = "${" + env + "}"
155+
} else if def, _ := valueForField(rf, "default"); def != "" {
156+
meta.Default = def
157+
}
138158

139159
// Type
140160
if t := typeName(rf.Type); t == "" {
@@ -152,6 +172,67 @@ var (
152172
durationType = reflect.TypeOf(time.Duration(0))
153173
)
154174

175+
func setValue(rv reflect.Value, str string) error {
176+
switch rv.Kind() {
177+
case reflect.Bool:
178+
// Zero value
179+
if str == "" {
180+
rv.SetZero()
181+
}
182+
// Bool
183+
if v, err := strconv.ParseBool(str); err != nil {
184+
return httpresponse.ErrBadRequest.Withf("invalid value for %s: %q", rv.Type(), str)
185+
} else {
186+
rv.SetBool(v)
187+
}
188+
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
189+
// Zero value
190+
if str == "" {
191+
rv.SetZero()
192+
}
193+
// Duration
194+
if rv.Type() == durationType {
195+
if v, err := time.ParseDuration(str); err != nil {
196+
return httpresponse.ErrBadRequest.Withf("invalid value for %s: %q", rv.Type(), str)
197+
} else {
198+
rv.Set(reflect.ValueOf(v))
199+
}
200+
}
201+
// Int
202+
if v, err := strconv.ParseInt(str, 10, 64); err != nil {
203+
return httpresponse.ErrBadRequest.Withf("invalid value for %s: %q", rv.Type(), str)
204+
} else {
205+
rv.SetInt(v)
206+
}
207+
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
208+
// Zero value
209+
if str == "" {
210+
rv.SetZero()
211+
}
212+
// Uint
213+
if v, err := strconv.ParseUint(str, 10, 64); err != nil {
214+
return httpresponse.ErrBadRequest.Withf("invalid value for %s: %q", rv.Type(), str)
215+
} else {
216+
rv.SetUint(v)
217+
}
218+
case reflect.Float32, reflect.Float64:
219+
// Zero value
220+
if str == "" {
221+
rv.SetZero()
222+
}
223+
// Float
224+
if v, err := strconv.ParseFloat(str, 64); err != nil {
225+
return httpresponse.ErrBadRequest.Withf("invalid value for %s: %q", rv.Type(), str)
226+
} else {
227+
rv.SetFloat(v)
228+
}
229+
case reflect.String:
230+
// String
231+
rv.SetString(str)
232+
}
233+
return httpresponse.ErrBadRequest.Withf("invalid value for %s: %q", rv.Type(), str)
234+
}
235+
155236
func typeName(rt reflect.Type) string {
156237
if rt.Kind() == reflect.Ptr {
157238
rt = rt.Elem()
@@ -189,20 +270,26 @@ func typeName(rt reflect.Type) string {
189270
return "ref"
190271
}
191272

192-
func nameForField(rt reflect.StructField, tags ...string) string {
273+
func valueForField(rf reflect.StructField, tags ...string) (string, bool) {
193274
for _, tag := range tags {
194-
tag, ok := rt.Tag.Lookup(tag)
275+
tag, ok := rf.Tag.Lookup(tag)
195276
if !ok {
196277
continue
197278
}
198-
if tag == "-" || tag == "" {
279+
if tag == "-" {
199280
// Ignore
200-
return ""
201-
}
202-
name := strings.Split(tag, ",")
203-
if len(name) > 0 && name[0] != "" {
204-
return name[0]
281+
return "", true
282+
} else {
283+
return tag, true
205284
}
206285
}
286+
return "", false
287+
}
288+
289+
func nameForField(rt reflect.StructField, tags ...string) string {
290+
value, exists := valueForField(rt, tags...)
291+
if exists {
292+
return value
293+
}
207294
return rt.Name
208295
}

0 commit comments

Comments
 (0)