Skip to content
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
6 changes: 3 additions & 3 deletions examples/resource-definitions/bookstore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ resources:
properties:
success:
type: boolean
book-edition:
singular: "book-edition"
plural: "book-editions"
book_edition:
singular: "book_edition"
plural: "book_editions"
parents: ["book"]
schema:
type: object
Expand Down
6 changes: 3 additions & 3 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ func GetAPI(api *openapi.OpenAPI, serverURL, pathPrefix string) (*API, error) {
if err != nil {
return nil, fmt.Errorf("error dereferencing schema %q: %v", sRef.Ref, err)
}
singular := cases.PascalCaseToKebabCase(key)
singular := cases.PascalToSnakeCase(key)
pattern := strings.Split(path, "/")[1:]
if !p.IsResourcePattern {
// deduplicate the singular, if applicable
Expand All @@ -213,10 +213,10 @@ func GetAPI(api *openapi.OpenAPI, serverURL, pathPrefix string) (*API, error) {
parent = pattern[len(pattern)-3]
parent = parent[0 : len(parent)-1] // strip curly surrounding
if strings.HasPrefix(singular, parent) {
finalSingular = strings.TrimPrefix(singular, parent+"-")
finalSingular = strings.TrimPrefix(singular, parent+"_")
}
}
pattern = append(pattern, fmt.Sprintf("{%s}", finalSingular))
pattern = append(pattern, fmt.Sprintf("{%s_id}", finalSingular))
}
r2, err := getOrPopulateResource(singular, pattern, dereferencedSchema, resourceBySingular, api)
if err != nil {
Expand Down
24 changes: 12 additions & 12 deletions pkg/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ var basicOpenAPI = &openapi.OpenAPI{
},
},
},
"/widgets/{widget}": {
"/widgets/{widget_id}": {
Get: &openapi.Operation{
Responses: map[string]openapi.Response{
"200": {
Expand Down Expand Up @@ -87,7 +87,7 @@ var basicOpenAPI = &openapi.OpenAPI{
},
},
},
"/widgets/{widget}:start": {
"/widgets/{widget_id}:start": {
Post: &openapi.Operation{
RequestBody: &openapi.RequestBody{
Content: map[string]openapi.MediaType{
Expand Down Expand Up @@ -121,7 +121,7 @@ var basicOpenAPI = &openapi.OpenAPI{
},
},
},
"/widgets/{widget}:stop": {
"/widgets/{widget_id}:stop": {
Post: &openapi.Operation{
RequestBody: &openapi.RequestBody{
Content: map[string]openapi.MediaType{
Expand Down Expand Up @@ -180,7 +180,7 @@ func TestGetAPI(t *testing.T) {

widget, ok := sd.Resources["widget"]
assert.True(t, ok, "widget resource should exist")
assert.Equal(t, widget.PatternElems(), []string{"widgets", "{widget}"})
assert.Equal(t, widget.PatternElems(), []string{"widgets", "{widget_id}"})
assert.Equal(t, sd.ServerURL, "https://api.example.com")
assert.NotNil(t, widget.Methods.Get, "should have GET method")
assert.NotNil(t, widget.Methods.List, "should have LIST method")
Expand Down Expand Up @@ -213,7 +213,7 @@ func TestGetAPI(t *testing.T) {
api: &openapi.OpenAPI{
OpenAPI: "3.1.0",
Paths: map[string]*openapi.PathItem{
"/widgets/{widget}": {
"/widgets/{widget_id}": {
Get: &openapi.Operation{
Responses: map[string]openapi.Response{
"200": {
Expand All @@ -240,7 +240,7 @@ func TestGetAPI(t *testing.T) {
XAEPResource: &openapi.XAEPResource{
Singular: "widget",
Plural: "widgets",
Patterns: []string{"/widgets/{widget}"},
Patterns: []string{"/widgets/{widget_id}"},
},
},
},
Expand All @@ -251,7 +251,7 @@ func TestGetAPI(t *testing.T) {
assert.True(t, ok, "widget resource should exist")
assert.Equal(t, "widget", widget.Singular)
assert.Equal(t, "widgets", widget.Plural)
assert.Equal(t, []string{"widgets", "{widget}"}, widget.PatternElems())
assert.Equal(t, []string{"widgets", "{widget_id}"}, widget.PatternElems())
},
},
{
Expand Down Expand Up @@ -308,7 +308,7 @@ func TestGetAPI(t *testing.T) {
Swagger: "2.0",
Servers: []openapi.Server{{URL: "https://api.example.com"}},
Paths: map[string]*openapi.PathItem{
"/widgets/{widget}": {
"/widgets/{widget_id}": {
Get: &openapi.Operation{
Responses: map[string]openapi.Response{
"200": {
Expand All @@ -333,7 +333,7 @@ func TestGetAPI(t *testing.T) {
widget, ok := sd.Resources["widget"]
assert.True(t, ok, "widget resource should exist")
assert.NotNil(t, widget.Methods.Get, "should have GET method")
assert.Equal(t, []string{"widgets", "{widget}"}, widget.PatternElems())
assert.Equal(t, []string{"widgets", "{widget_id}"}, widget.PatternElems())
},
},
{
Expand Down Expand Up @@ -442,7 +442,7 @@ func TestGetAPI(t *testing.T) {
},
},
},
"/widgets/{widget}": {
"/widgets/{widget_id}": {
Get: &openapi.Operation{
Responses: map[string]openapi.Response{
"200": {
Expand Down Expand Up @@ -486,7 +486,7 @@ func TestGetAPI(t *testing.T) {
},
},
},
"/widgets/{widget}:customOp": {
"/widgets/{widget_id}:customOp": {
Post: &openapi.Operation{
XAEPLongRunningOperation: &openapi.XAEPLongRunningOperation{
Response: openapi.XAEPLongRunningOperationResponse{
Expand Down Expand Up @@ -603,7 +603,7 @@ func TestLoadFromJsonBookstore(t *testing.T) {
assert.NotEmpty(t, apiResult.Resources, "Resources map should be populated")
assert.Contains(t, apiResult.Resources, "publisher", "Resources map should contain 'publisher'")
assert.Contains(t, apiResult.Resources, "book", "Resources map should contain 'book'")
assert.Contains(t, apiResult.Resources, "book-edition", "Resources map should contain 'book-edition'")
assert.Contains(t, apiResult.Resources, "book_edition", "Resources map should contain 'book-edition'")
assert.Contains(t, apiResult.Resources, "isbn", "Resources map should contain 'isbn'")

// Check some details of a resource
Expand Down
14 changes: 13 additions & 1 deletion pkg/api/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ package api
import (
"encoding/json"
"fmt"
"regexp"

"github.com/aep-dev/aep-lib-go/pkg/constants"
"github.com/aep-dev/aep-lib-go/pkg/openapi"
)

var singularPluralRegex = regexp.MustCompile("^[a-z][a-z0-9_]*[a-z0-9]$")

func LoadAPIFromJson(data []byte) (*API, error) {
api := &API{}
err := json.Unmarshal(data, api)
Expand All @@ -25,7 +28,16 @@ func LoadAPIFromJson(data []byte) (*API, error) {
// such as the "path" variable in the resource.
func AddImplicitFieldsAndValidate(api *API) error {
// add the path variable to the resource
for _, r := range api.Resources {
for name, r := range api.Resources {
if !singularPluralRegex.MatchString(name) {
return fmt.Errorf("resource name %s does not match the regex %s", name, singularPluralRegex.String())
}
if !singularPluralRegex.MatchString(r.Singular) {
return fmt.Errorf("singular resource name %s does not match the regex %s", r.Singular, singularPluralRegex.String())
}
if !singularPluralRegex.MatchString(r.Plural) {
return fmt.Errorf("plural resource name %s does not match the regex %s", r.Plural, singularPluralRegex.String())
}
r.API = api
if r.Schema.Properties == nil {
r.Schema.Properties = make(map[string]openapi.Schema)
Expand Down
27 changes: 16 additions & 11 deletions pkg/api/openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) {
}
idParam := openapi.Parameter{
In: "path",
Name: singular,
Name: singular + "_id",
Required: true,
Schema: &openapi.Schema{
Type: "string",
Expand All @@ -83,7 +83,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) {
},
}
for _, pwp := range *parentPWPS {
resourcePath := fmt.Sprintf("%s%s/{%s}", pwp.Pattern, collection, singular)
resourcePath := fmt.Sprintf("%s%s/{%s_id}", pwp.Pattern, collection, singular)
patterns = append(patterns, resourcePath[1:])
if r.Methods.List != nil {
listPath := fmt.Sprintf("%s%s", pwp.Pattern, collection)
Expand Down Expand Up @@ -144,7 +144,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) {
})
}
methodInfo := openapi.Operation{
OperationID: fmt.Sprintf("List%s", cases.KebabToPascalCase(r.Singular)),
OperationID: fmt.Sprintf("List%s", cases.SnakeToPascalCase(r.Singular)),
Description: fmt.Sprintf("List method for %s", r.Singular),
Parameters: params,
Responses: map[string]openapi.Response{
Expand Down Expand Up @@ -177,7 +177,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) {
})
}
methodInfo := openapi.Operation{
OperationID: fmt.Sprintf("Create%s", cases.KebabToPascalCase(r.Singular)),
OperationID: fmt.Sprintf("Create%s", cases.SnakeToPascalCase(r.Singular)),
Description: fmt.Sprintf("Create method for %s", r.Singular),
Parameters: params,
RequestBody: &bodyParam,
Expand Down Expand Up @@ -208,7 +208,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) {
}
if r.Methods.Get != nil {
methodInfo := openapi.Operation{
OperationID: fmt.Sprintf("Get%s", cases.KebabToPascalCase(r.Singular)),
OperationID: fmt.Sprintf("Get%s", cases.SnakeToPascalCase(r.Singular)),
Description: fmt.Sprintf("Get method for %s", r.Singular),
Parameters: append(pwp.Params, idParam),
Responses: map[string]openapi.Response{
Expand All @@ -219,7 +219,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) {
}
if r.Methods.Update != nil {
methodInfo := openapi.Operation{
OperationID: fmt.Sprintf("Update%s", cases.KebabToPascalCase(r.Singular)),
OperationID: fmt.Sprintf("Update%s", cases.SnakeToPascalCase(r.Singular)),
Description: fmt.Sprintf("Update method for %s", r.Singular),
Parameters: append(pwp.Params, idParam),
RequestBody: &openapi.RequestBody{
Expand Down Expand Up @@ -280,7 +280,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) {
})
}
methodInfo := openapi.Operation{
OperationID: fmt.Sprintf("Delete%s", cases.KebabToPascalCase(r.Singular)),
OperationID: fmt.Sprintf("Delete%s", cases.SnakeToPascalCase(r.Singular)),
Description: fmt.Sprintf("Delete method for %s", r.Singular),
Parameters: params,
Responses: map[string]openapi.Response{
Expand Down Expand Up @@ -317,7 +317,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) {
}
if r.Methods.Apply != nil {
methodInfo := openapi.Operation{
OperationID: fmt.Sprintf("Apply%s", cases.KebabToPascalCase(r.Singular)),
OperationID: fmt.Sprintf("Apply%s", cases.SnakeToPascalCase(r.Singular)),
Description: fmt.Sprintf("Apply method for %s", r.Singular),
Parameters: append(pwp.Params, idParam),
RequestBody: &bodyParam,
Expand Down Expand Up @@ -364,7 +364,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) {
}
cmPath := fmt.Sprintf("%s:%s", resourcePath, custom.Name)
methodInfo := openapi.Operation{
OperationID: fmt.Sprintf(":%s%s", cases.KebabToPascalCase(custom.Name), cases.KebabToPascalCase(r.Singular)),
OperationID: fmt.Sprintf(":%s%s", cases.SnakeToPascalCase(custom.Name), cases.SnakeToPascalCase(r.Singular)),
Description: fmt.Sprintf("Custom method %s for %s", custom.Name, r.Singular),
Parameters: append(pwp.Params, idParam),
Responses: map[string]openapi.Response{
Expand Down Expand Up @@ -481,9 +481,14 @@ func generateParentPatternsWithParams(r *Resource) (string, *[]PathWithParams) {
params := []openapi.Parameter{}
for i := 0; i < len(r.patternElems)-2; i += 2 {
pElem := r.patternElems[i+1]
// Extract the parameter name without the _id suffix
paramName := pElem[1 : len(pElem)-1]
if strings.HasSuffix(paramName, "_id") {
paramName = paramName[:len(paramName)-3]
}
params = append(params, openapi.Parameter{
In: "path",
Name: pElem[1 : len(pElem)-1],
Name: paramName,
Required: true,
Schema: &openapi.Schema{
Type: "string",
Expand All @@ -503,7 +508,7 @@ func generateParentPatternsWithParams(r *Resource) (string, *[]PathWithParams) {
pwps := []PathWithParams{}
for _, parent := range r.ParentResources() {
singular := parent.Singular
basePattern := fmt.Sprintf("/%s/{%s}", CollectionName(parent), singular)
basePattern := fmt.Sprintf("/%s/{%s_id}", CollectionName(parent), singular)
baseParam := openapi.Parameter{
In: "path",
Name: singular,
Expand Down
Loading