Skip to content

Commit e666a30

Browse files
committed
feat: add support for a serverURL
sometimes, the OAS description does not have a server URL. Give the ablity to set that if so. fixes #11
1 parent 68a9c97 commit e666a30

4 files changed

Lines changed: 94 additions & 77 deletions

File tree

cmd/aepcli/main.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@ func aepcli(args []string) error {
4242

4343
var rawHeaders []string
4444
var pathPrefix string
45+
var serverURL string
4546
rootCmd.Flags().SetInterspersed(false) // allow sub parsers to parse subsequent flags after the resource
4647
rootCmd.PersistentFlags().StringArrayVar(&rawHeaders, "header", []string{}, "Specify headers in the format key=value")
4748
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "Set the logging level (debug, info, warn, error)")
4849
rootCmd.PersistentFlags().StringVar(&pathPrefix, "path-prefix", "", "Specify a path prefix that is prepended to all paths in the openapi schema. This will strip them when evaluating the resource hierarchy paths.")
50+
rootCmd.PersistentFlags().StringVar(&serverURL, "server-url", "", "Specify a URL to use for the server. If not specified, the first server URL in the OpenAPI definition will be used.")
4951

5052
rootCmd.SetArgs(args)
5153
if err := rootCmd.Execute(); err != nil {
@@ -72,13 +74,14 @@ func aepcli(args []string) error {
7274
pathPrefix = api.PathPrefix
7375
}
7476
rawHeaders = append(rawHeaders, api.Headers...)
77+
serverURL = api.ServerURL
7578
}
7679

7780
openapi, err := openapi.FetchOpenAPI(fileOrAlias)
7881
if err != nil {
7982
return fmt.Errorf("unable to fetch openapi: %w", err)
8083
}
81-
serviceDefinition, err := service.GetServiceDefinition(openapi, pathPrefix)
84+
serviceDefinition, err := service.GetServiceDefinition(openapi, serverURL, pathPrefix)
8285
if err != nil {
8386
return fmt.Errorf("unable to get service definition: %w", err)
8487
}

internal/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type Config struct {
1515
type API struct {
1616
Name string
1717
OpenAPIPath string
18+
ServerURL string
1819
Headers []string
1920
PathPrefix string
2021
}

internal/service/service_definition.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package service
22

33
import (
4-
"errors"
54
"fmt"
65
"log/slog"
76
"strings"
@@ -17,7 +16,7 @@ type ServiceDefinition struct {
1716
Resources map[string]*Resource
1817
}
1918

20-
func GetServiceDefinition(api *openapi.OpenAPI, pathPrefix string) (*ServiceDefinition, error) {
19+
func GetServiceDefinition(api *openapi.OpenAPI, serverURL, pathPrefix string) (*ServiceDefinition, error) {
2120
slog.Debug("parsing openapi", "pathPrefix", pathPrefix)
2221
oasVersion := api.Info.Version
2322
resourceBySingular := make(map[string]*Resource)
@@ -113,12 +112,14 @@ func GetServiceDefinition(api *openapi.OpenAPI, pathPrefix string) (*ServiceDefi
113112
}
114113
}
115114
// get the first serverURL url
116-
serverURL := ""
117-
for _, s := range api.Servers {
118-
serverURL = s.URL + pathPrefix
115+
if serverURL == "" {
116+
for _, s := range api.Servers {
117+
serverURL = s.URL + pathPrefix
118+
}
119119
}
120+
120121
if serverURL == "" {
121-
return nil, errors.New("no servers found in the OpenAPI definition. Cannot find a server to send a request to")
122+
return nil, fmt.Errorf("no server URL found in openapi, and none was provided")
122123
}
123124

124125
return &ServiceDefinition{
@@ -130,7 +131,7 @@ func GetServiceDefinition(api *openapi.OpenAPI, pathPrefix string) (*ServiceDefi
130131
func (s *ServiceDefinition) GetResource(resource string) (*Resource, error) {
131132
r, ok := (*s).Resources[resource]
132133
if !ok {
133-
return nil, fmt.Errorf("Resource %s not found.", resource)
134+
return nil, fmt.Errorf("Resource %s not found", resource)
134135
}
135136
return r, nil
136137
}

internal/service/service_definition_test.go

Lines changed: 81 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -7,45 +7,20 @@ import (
77
"github.com/stretchr/testify/assert"
88
)
99

10-
func TestGetServiceDefinition(t *testing.T) {
11-
tests := []struct {
12-
name string
13-
api *openapi.OpenAPI
14-
expectedError string
15-
validateResult func(*testing.T, *ServiceDefinition)
16-
}{
17-
{
18-
name: "basic resource with CRUD operations",
19-
api: &openapi.OpenAPI{
20-
Servers: []openapi.Server{{URL: "https://api.example.com"}},
21-
Paths: map[string]openapi.PathItem{
22-
"/widgets": {
23-
Get: &openapi.Operation{
24-
Responses: map[string]openapi.Response{
25-
"200": {
26-
Content: map[string]openapi.MediaType{
27-
"application/json": {
28-
Schema: &openapi.Schema{
29-
Properties: map[string]openapi.Schema{
30-
"results": {
31-
Type: "array",
32-
Items: &openapi.Schema{
33-
Ref: "#/components/schemas/Widget",
34-
},
35-
},
36-
},
37-
},
38-
},
39-
},
40-
},
41-
},
42-
},
43-
Post: &openapi.Operation{
44-
Responses: map[string]openapi.Response{
45-
"200": {
46-
Content: map[string]openapi.MediaType{
47-
"application/json": {
48-
Schema: &openapi.Schema{
10+
var basicOpenAPI = &openapi.OpenAPI{
11+
Servers: []openapi.Server{{URL: "https://api.example.com"}},
12+
Paths: map[string]openapi.PathItem{
13+
"/widgets": {
14+
Get: &openapi.Operation{
15+
Responses: map[string]openapi.Response{
16+
"200": {
17+
Content: map[string]openapi.MediaType{
18+
"application/json": {
19+
Schema: &openapi.Schema{
20+
Properties: map[string]openapi.Schema{
21+
"results": {
22+
Type: "array",
23+
Items: &openapi.Schema{
4924
Ref: "#/components/schemas/Widget",
5025
},
5126
},
@@ -54,53 +29,82 @@ func TestGetServiceDefinition(t *testing.T) {
5429
},
5530
},
5631
},
57-
"/widgets/{widget}": {
58-
Get: &openapi.Operation{
59-
Responses: map[string]openapi.Response{
60-
"200": {
61-
Content: map[string]openapi.MediaType{
62-
"application/json": {
63-
Schema: &openapi.Schema{
64-
Ref: "#/components/schemas/Widget",
65-
},
66-
},
67-
},
32+
},
33+
},
34+
Post: &openapi.Operation{
35+
Responses: map[string]openapi.Response{
36+
"200": {
37+
Content: map[string]openapi.MediaType{
38+
"application/json": {
39+
Schema: &openapi.Schema{
40+
Ref: "#/components/schemas/Widget",
6841
},
6942
},
7043
},
71-
Delete: &openapi.Operation{},
72-
Patch: &openapi.Operation{
73-
Responses: map[string]openapi.Response{
74-
"200": {
75-
Content: map[string]openapi.MediaType{
76-
"application/json": {
77-
Schema: &openapi.Schema{
78-
Ref: "#/components/schemas/Widget",
79-
},
80-
},
81-
},
44+
},
45+
},
46+
},
47+
},
48+
"/widgets/{widget}": {
49+
Get: &openapi.Operation{
50+
Responses: map[string]openapi.Response{
51+
"200": {
52+
Content: map[string]openapi.MediaType{
53+
"application/json": {
54+
Schema: &openapi.Schema{
55+
Ref: "#/components/schemas/Widget",
8256
},
8357
},
8458
},
8559
},
8660
},
87-
Components: openapi.Components{
88-
Schemas: map[string]openapi.Schema{
89-
"Widget": {
90-
Type: "object",
91-
Properties: map[string]openapi.Schema{
92-
"name": {Type: "string"},
61+
},
62+
Delete: &openapi.Operation{},
63+
Patch: &openapi.Operation{
64+
Responses: map[string]openapi.Response{
65+
"200": {
66+
Content: map[string]openapi.MediaType{
67+
"application/json": {
68+
Schema: &openapi.Schema{
69+
Ref: "#/components/schemas/Widget",
70+
},
9371
},
9472
},
9573
},
9674
},
9775
},
76+
},
77+
},
78+
Components: openapi.Components{
79+
Schemas: map[string]openapi.Schema{
80+
"Widget": {
81+
Type: "object",
82+
Properties: map[string]openapi.Schema{
83+
"name": {Type: "string"},
84+
},
85+
},
86+
},
87+
},
88+
}
89+
90+
func TestGetServiceDefinition(t *testing.T) {
91+
tests := []struct {
92+
name string
93+
api *openapi.OpenAPI
94+
serverURL string
95+
expectedError string
96+
validateResult func(*testing.T, *ServiceDefinition)
97+
}{
98+
{
99+
name: "basic resource with CRUD operations",
100+
api: basicOpenAPI,
98101
validateResult: func(t *testing.T, sd *ServiceDefinition) {
99102
assert.Equal(t, "https://api.example.com", sd.ServerURL)
100103

101104
widget, ok := sd.Resources["widget"]
102105
assert.True(t, ok, "widget resource should exist")
103106
assert.Equal(t, widget.Pattern, []string{"widgets", "{widget}"})
107+
assert.Equal(t, sd.ServerURL, "https://api.example.com")
104108
assert.NotNil(t, widget.GetMethod, "should have GET method")
105109
assert.NotNil(t, widget.ListMethod, "should have LIST method")
106110
assert.NotNil(t, widget.CreateMethod, "should have CREATE method")
@@ -111,6 +115,14 @@ func TestGetServiceDefinition(t *testing.T) {
111115
assert.NotNil(t, widget.DeleteMethod, "should have DELETE method")
112116
},
113117
},
118+
{
119+
name: "empty openapi with server url override",
120+
api: basicOpenAPI,
121+
serverURL: "https://override.example.com",
122+
validateResult: func(t *testing.T, sd *ServiceDefinition) {
123+
assert.Equal(t, "https://override.example.com", sd.ServerURL)
124+
},
125+
},
114126
{
115127
name: "resource with x-aep-resource annotation",
116128
api: &openapi.OpenAPI{
@@ -161,7 +173,7 @@ func TestGetServiceDefinition(t *testing.T) {
161173
api: &openapi.OpenAPI{
162174
Servers: []openapi.Server{},
163175
},
164-
expectedError: "no servers found in the OpenAPI definition",
176+
expectedError: "no server URL found in openapi, and none was provided",
165177
},
166178
{
167179
name: "resource with user-settable create ID",
@@ -242,7 +254,7 @@ func TestGetServiceDefinition(t *testing.T) {
242254

243255
for _, tt := range tests {
244256
t.Run(tt.name, func(t *testing.T) {
245-
result, err := GetServiceDefinition(tt.api, "")
257+
result, err := GetServiceDefinition(tt.api, tt.serverURL, "")
246258

247259
if tt.expectedError != "" {
248260
assert.Error(t, err)

0 commit comments

Comments
 (0)