From b1e7aa2d59307805ca5ccebd4dba3dacb97bdff5 Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Tue, 10 Dec 2024 21:55:07 -0800 Subject: [PATCH] feat(openapi): add operationIds add valid operationIDs, which improves the openapi-generator generated clients. --- pkg/api/openapi.go | 15 ++++--- pkg/api/openapi_test.go | 99 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 97 insertions(+), 17 deletions(-) diff --git a/pkg/api/openapi.go b/pkg/api/openapi.go index 5e3b810..078bdb7 100644 --- a/pkg/api/openapi.go +++ b/pkg/api/openapi.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/aep-dev/aep-lib-go/pkg/cases" "github.com/aep-dev/aep-lib-go/pkg/constants" "github.com/aep-dev/aep-lib-go/pkg/openapi" ) @@ -92,7 +93,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) { } } addMethodToPath(paths, listPath, "get", openapi.Operation{ - OperationID: r.Singular + ".list", + OperationID: fmt.Sprintf("List%s", cases.UpperFirst(r.Singular)), Description: fmt.Sprintf("List method for %s", r.Singular), Parameters: append(pwp.Params, openapi.Parameter{ @@ -139,7 +140,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) { }) } addMethodToPath(paths, createPath, "post", openapi.Operation{ - OperationID: r.Singular + ".create", + OperationID: fmt.Sprintf("Create%s", cases.UpperFirst(r.Singular)), Description: fmt.Sprintf("Create method for %s", r.Singular), Parameters: params, RequestBody: &bodyParam, @@ -150,7 +151,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) { } if r.GetMethod != nil { addMethodToPath(paths, resourcePath, "get", openapi.Operation{ - OperationID: r.Singular + ".get", + OperationID: fmt.Sprintf("Get%s", cases.UpperFirst(r.Singular)), Description: fmt.Sprintf("Get method for %s", r.Singular), Parameters: append(pwp.Params, idParam), Responses: map[string]openapi.Response{ @@ -160,7 +161,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) { } if r.UpdateMethod != nil { addMethodToPath(paths, resourcePath, "patch", openapi.Operation{ - OperationID: r.Singular + ".update", + OperationID: fmt.Sprintf("Update%s", cases.UpperFirst(r.Singular)), Description: fmt.Sprintf("Update method for %s", r.Singular), Parameters: append(pwp.Params, idParam), RequestBody: &bodyParam, @@ -182,7 +183,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) { }) } addMethodToPath(paths, resourcePath, "delete", openapi.Operation{ - OperationID: r.Singular + ".delete", + OperationID: fmt.Sprintf("Delete%s", cases.UpperFirst(r.Singular)), Description: fmt.Sprintf("Delete method for %s", r.Singular), Parameters: params, Responses: map[string]openapi.Response{ @@ -199,7 +200,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) { } if r.ApplyMethod != nil { addMethodToPath(paths, resourcePath, "put", openapi.Operation{ - OperationID: r.Singular + ".apply", + OperationID: fmt.Sprintf("Apply%s", cases.UpperFirst(r.Singular)), Description: fmt.Sprintf("Apply method for %s", r.Singular), Parameters: append(pwp.Params, idParam), RequestBody: &bodyParam, @@ -215,7 +216,7 @@ func ConvertToOpenAPI(api *API) (*openapi.OpenAPI, error) { } cmPath := fmt.Sprintf("%s:%s", resourcePath, custom.Name) methodInfo := openapi.Operation{ - OperationID: r.Singular + ":" + custom.Name, + OperationID: fmt.Sprintf(":%s%s", cases.UpperFirst(custom.Name), cases.UpperFirst(r.Singular)), Description: fmt.Sprintf("Custom method %s for %s", custom.Name, r.Singular), Parameters: append(pwp.Params, idParam), Responses: map[string]openapi.Response{ diff --git a/pkg/api/openapi_test.go b/pkg/api/openapi_test.go index 5a17b6e..e5453a4 100644 --- a/pkg/api/openapi_test.go +++ b/pkg/api/openapi_test.go @@ -42,6 +42,22 @@ func TestToOpenAPI(t *testing.T) { CreateMethod: &CreateMethod{}, UpdateMethod: &UpdateMethod{}, DeleteMethod: &DeleteMethod{}, + CustomMethods: []*CustomMethod{ + { + Name: "archive", + Method: "POST", + Request: &openapi.Schema{ + Type: "object", + Properties: map[string]openapi.Schema{}, + }, + Response: &openapi.Schema{ + Type: "object", + Properties: map[string]openapi.Schema{ + "archived": {Type: "boolean"}, + }, + }, + }, + }, } publisher.Children = append(publisher.Children, book) exampleAPI := &API{ @@ -80,20 +96,63 @@ func TestToOpenAPI(t *testing.T) { }, expectedOperations: map[string]openapi.PathItem{ "/publishers": { - Get: &openapi.Operation{}, - Post: &openapi.Operation{}, + Get: &openapi.Operation{ + OperationID: "ListPublisher", + }, + Post: &openapi.Operation{ + OperationID: "CreatePublisher", + }, }, "/publishers/{publisher}": { - Get: &openapi.Operation{}, + Get: &openapi.Operation{ + OperationID: "GetPublisher", + }, }, "/publishers/{publisher}/books": { - Get: &openapi.Operation{}, - Post: &openapi.Operation{}, + Get: &openapi.Operation{ + OperationID: "ListBook", + }, + Post: &openapi.Operation{ + OperationID: "CreateBook", + }, }, "/publishers/{publisher}/books/{book}": { - Get: &openapi.Operation{}, - Put: &openapi.Operation{}, - Delete: &openapi.Operation{}, + Get: &openapi.Operation{ + OperationID: "GetBook", + }, + Patch: &openapi.Operation{ + OperationID: "UpdateBook", + }, + Delete: &openapi.Operation{ + OperationID: "DeleteBook", + }, + }, + "/publishers/{publisher}/books/{book}:archive": { + Post: &openapi.Operation{ + OperationID: ":ArchiveBook", + RequestBody: &openapi.RequestBody{ + Required: true, + Content: map[string]openapi.MediaType{ + "application/json": { + Schema: &openapi.Schema{}, + }, + }, + }, + Responses: map[string]openapi.Response{ + "200": { + Content: map[string]openapi.MediaType{ + "application/json": { + Schema: &openapi.Schema{ + Type: "object", + Properties: map[string]openapi.Schema{ + "archived": {Type: "boolean"}, + }, + }, + }, + }, + }, + }, + }, }, }, expectedListSchemas: map[string]*openapi.Schema{ @@ -148,24 +207,44 @@ func TestToOpenAPI(t *testing.T) { assert.True(t, exists, "Expected schema %s not found", schema) } - // Verify operations exist + // Verify operations exist and have correct operationIds for path, operations := range tt.expectedOperations { pathItem, exists := openAPI.Paths[path] assert.True(t, exists, "Expected path %s not found", path) if operations.Get != nil { assert.NotNil(t, pathItem.Get, "expected get operation for path %s", path) + if operations.Get.OperationID != "" { + assert.Equal(t, operations.Get.OperationID, pathItem.Get.OperationID, + "expected matching operationId for GET %s", path) + } } if operations.Patch != nil { assert.NotNil(t, pathItem.Patch, "expected patch operation for path %s", path) + if operations.Patch.OperationID != "" { + assert.Equal(t, operations.Patch.OperationID, pathItem.Patch.OperationID, + "expected matching operationId for PATCH %s", path) + } } if operations.Post != nil { assert.NotNil(t, pathItem.Post, "expected post operation for path %s", path) + if operations.Post.OperationID != "" { + assert.Equal(t, operations.Post.OperationID, pathItem.Post.OperationID, + "expected matching operationId for POST %s", path) + } } if operations.Put != nil { - assert.NotNil(t, operations.Put, "expected put operation for path %s", path) + assert.NotNil(t, pathItem.Put, "expected put operation for path %s", path) + if operations.Put.OperationID != "" { + assert.Equal(t, operations.Put.OperationID, pathItem.Put.OperationID, + "expected matching operationId for PUT %s", path) + } } if operations.Delete != nil { assert.NotNil(t, pathItem.Delete, "expected delete operation for path %s", path) + if operations.Delete.OperationID != "" { + assert.Equal(t, operations.Delete.OperationID, pathItem.Delete.OperationID, + "expected matching operationId for DELETE %s", path) + } } }