8000 refactor(cosmosgen): generate openapi for external modules by julienrbrt · Pull Request #4741 · ignite/cli · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

refactor(cosmosgen): generate openapi for external modules #4741

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jul 1, 2025
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
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

- [#4717](https://github.com/ignite/cli/pull/4717) Bump Cosmos SDK to `v0.53.2`.
- [#4718](https://github.com/ignite/cli/pull/4718) Bump default Ignite Apps.
- [#4741](https://github.com/ignite/cli/pull/4741) Let `generate openapi` generate external modules OpenAPI spec.
- [#4747](https://github.com/ignite/cli/pull/4747) Improve Ignite UI.

### Fixes
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/08-configuration/01-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ paths where the client-side code is generated.
```yml
client:
openapi:
path: "docs/static/openapi.yml"
path: "docs/static/openapi.json"
typescript:
path: "ts-client"
composables:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ paths where the client-side code is generated.
```yml
client:
openapi:
path: "docs/static/openapi.yml"
path: "docs/static/openapi.json"
typescript:
path: "ts-client"
composables:
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
github.com/charmbracelet/fang v0.2.0
github.com/charmbracelet/glow v1.5.1
github.com/charmbracelet/lipgloss v1.1.0
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1
github.com/cockroachdb/errors v1.12.0
github.com/cometbft/cometbft v0.38.17
github.com/cosmos/cosmos-sdk v0.53.2
Expand Down Expand Up @@ -143,7 +144,6 @@ require (
github.com/charmbracelet/charm v0.8.7 // indirect
github.com/charmbracelet/colorprofile v0.3.0 // indirect
github.com/charmbracelet/glamour v0.6.0 // indirect
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1 // indirect
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250603201427-c31516f43444 // indirect
Expand Down Expand Up @@ -281,6 +281,7 @@ require (
github.com/gostaticanalysis/nilerr v0.1.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect
Expand Down Expand Up @@ -505,6 +506,7 @@ require (
tool (
github.com/bufbuild/buf/cmd/buf
github.com/golangci/golangci-lint/cmd/golangci-lint
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
github.com/tbruyelle/mdgofmt/cmd/mdgofmt
github.com/vektra/mockery/v2
golang.org/x/tools/cmd/goimports
Expand Down
2 changes: 1 addition & 1 deletion ignite/cmd/scaffold.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const (
flagDescription = "desc"
flagProtoDir = "proto-dir"

msgCommitPrefix = "Your saved project changes have not been committed.\nTo enable reverting to your current state, commit your saved changes."
msgCommitPrefix = "Your project changes have not been committed.\nTo enable reverting to your current state, commit your saved changes."
msgCommitPrompt = "Do you want to proceed without committing your saved changes"

statusScaffolding = "Scaffolding..."
Expand Down
2 changes: 1 addition & 1 deletion ignite/config/chain/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ var (

// DefaultOpenAPIPath defines the default relative path to use when generating an OpenAPI schema.
// The path is relative to the app's directory.
DefaultOpenAPIPath = "docs/static/openapi.yml"
DefaultOpenAPIPath = "docs/static/openapi.json"

// LatestVersion defines the latest version of the config.
LatestVersion version.Version = 1
Expand Down
1 change: 1 addition & 0 deletions ignite/internal/tools/gen-mig-diffs/pkg/diff/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var diffIgnoreGlobs = []string{
"**.pulsar.go",
"**/node_modules/**",
"**/openapi.yml",
"**/openapi.json",
".gitignore",
".github/**",
"**.html",
Expand Down
22 changes: 9 additions & 13 deletions ignite/pkg/cosmosgen/generate_openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,21 +127,17 @@ func (g *generator) generateOpenAPISpec(ctx context.Context) error {

doneMods := make(map[string]struct{})
for _, modules := range g.thirdModules {
if len(modules) == 0 {
continue
}
var (
m = modules[0]
path = extractRootModulePath(m.Pkg.Path)
)
for _, m := range modules {
path := extractRootModulePath(m.Pkg.Path)

if _, ok := doneMods[path]; ok {
continue
}
doneMods[path] = struct{}{}
if _, ok := doneMods[path]; ok {
continue
}
doneMods[path] = struct{}{}

if err := gen(path, "", m.Name); err != nil {
return err
if err := gen(path, "proto", m.Name); err != nil {
return err
}
}
}

Expand Down
51 changes: 51 additions & 0 deletions ignite/pkg/cosmosgen/generate_openapi_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package cosmosgen

import (
"os"
"path/filepath"
"testing"

"github.com/ignite/cli/v29/ignite/pkg/cache"
"github.com/ignite/cli/v29/ignite/pkg/cosmosanalysis/module"
"github.com/ignite/cli/v29/ignite/pkg/cosmosbuf"
"github.com/ignite/cli/v29/ignite/pkg/dirchange"
"github.com/ignite/cli/v29/ignite/pkg/errors"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -55,3 +62,47 @@ func Test_extractRootModulePath(t *testing.T) {
})
}
}

func TestGenerateOpenAPI(t *testing.T) {
require := require.New(t)
testdataDir := "testdata"
appDir := filepath.Join(testdataDir, "testchain")
openAPIFile := filepath.Join(appDir, "docs", "static", "openapi.json")

cacheStorage, err := cache.NewStorage(filepath.Join(t.TempDir(), "cache.db"))
require.NoError(err)

buf, err := cosmosbuf.New(cacheStorage, t.Name())
require.NoError(err)

// Use module discovery to collect test module proto.
m, err := module.Discover(t.Context(), appDir, appDir, module.WithProtoDir("proto"))
require.NoError(err, "failed to discover module")
require.Len(m, 1, "expected exactly one module to be discovered")

g := &generator{
appPath: appDir,
protoDir: "proto",
goModPath: "go.mod",
cacheStorage: cacheStorage,
buf: buf,
appModules: m,
opts: &generateOptions{
specOut: openAPIFile,
},
}

err = g.generateOpenAPISpec(t.Context())
if err != nil && !errors.Is(err, dirchange.ErrNoFile) {
require.NoError(err, "failed to generate OpenAPI spec")
}

// compare generated OpenAPI spec with golden files
goldenFile := filepath.Join(testdataDir, "expected_files", "openapi", "openapi.json")
gold, err := os.ReadFile(goldenFile)
require.NoError(err, "failed to read golden file: %s", goldenFile)

gotBytes, err := os.ReadFile(openAPIFile)
require.NoError(err, "failed to read generated file: %s", openAPIFile)
require.Equal(string(gold), string(gotBytes), "generated OpenAPI spec does not match golden file")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"go.mod","consumes":["application/json"],"produces":["application/json"],"swagger":"2.0","info":{"description":"Chain go.mod REST API","title":"HTTP API Console","contact":{"name":"go.mod"},"version":"version not set"},"paths":{"/ignite.planet.mars.Msg/Bar":{"post":{"tags":["Msg"],"operationId":"GoModMsg_Bar","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/ignite.planet.mars.MsgBarRequest"}}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/ignite.planet.mars.MsgBarResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/ignite.planet.mars.Msg/MyMessage":{"post":{"tags":["Msg"],"operationId":"GoModMsg_MyMessage","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/ignite.planet.mars.MsgMyMessageRequest"}}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/ignite.planet.mars.MsgMyMessageResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/ignite/mars/query_simple":{"get":{"tags":["Query"],"operationId":"GoModQuery_QuerySimple","responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/ignite.planet.mars.QuerySimpleResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/ignite/mars/query_simple/{mytypefield}":{"get":{"tags":["Query"],"operationId":"GoModQuery_QuerySimpleParams","parameters":[{"type":"string","name":"mytypefield","in":"path","required":true}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/ignite.planet.mars.QuerySimpleParamsResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/ignite/mars/query_with_params/{mytypefield}":{"get":{"tags":["Query"],"operationId":"GoModQuery_QueryParamsWithPagination","parameters":[{"type":"string","name":"mytypefield","in":"path","required":true},{"type":"string","format":"byte","description":"key is a value returned in PageResponse.next_key to begin\nquerying the next page most efficiently. Only one of offset or key\nshould be set.","name":"pagination.key","in":"query"},{"type":"string","format":"uint64","description":"offset is a numeric offset that can be used when key is unavailable.\nIt is less efficient than using key. Only one of offset or key should\nbe set.","name":"pagination.offset","in":"query"},{"type":"string","format":"uint64","description":"limit is the total number of results to be returned in the result page.\nIf left empty it will default to a value to be set by each app.","name":"pagination.limit","in":"query"},{"type":"boolean","description":"count_total is set to true to indicate that the result set should include\na count of the total number of items available for pagination in UIs.\ncount_total is only respected when offset is used. It is ignored when key\nis set.","name":"pagination.count_total","in":"query"},{"type":"boolean","description":"reverse is set to true if results are to be returned in the descending order.","name":"pagination.reverse","in":"query"}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/ignite.planet.mars.QueryWithPaginationResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/ignite/mars/query_with_query_params/{mytypefield}":{"get":{"tags":["Query"],"operationId":"GoModQuery_QueryWithQueryParamsWithPagination","parameters":[{"type":"string","name":"mytypefield","in":"path","required":true},{"type":"string","name":"query_param","in":"query"},{"type":"string","format":"byte","description":"key is a value returned in PageResponse.next_key to begin\nquerying the next page most efficiently. Only one of offset or key\nshould be set.","name":"pagination.key","in":"query"},{"type":"string","format":"uint64","description":"offset is a numeric offset that can be used when key is unavailable.\nIt is less efficient than using key. Only one of offset or key should\nbe set.","name":"pagination.offset","in":"query"},{"type":"string","format":"uint64","description":"limit is the total number of results to be returned in the result page.\nIf left empty it will default to a value to be set by each app.","name":"pagination.limit","in":"query"},{"type":"boolean","description":"count_total is set to true to indicate that the result set should include\na count of the total number of items available for pagination in UIs.\ncount_total is only respected when offset is used. It is ignored when key\nis set.","name":"pagination.count_total","in":"query"},{"type":"boolean","description":"reverse is set to true if results are to be returned in the descending order.","name":"pagination.reverse","in":"query"}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/ignite.planet.mars.QueryWithQueryParamsWithPaginationResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}},"/ignite/mars/query_with_query_params/{mytypefield}/{mybool}":{"get":{"tags":["Query"],"operationId":"GoModQuery_QueryWithQueryParams","parameters":[{"type":"string","name":"mytypefield","in":"path","required":true},{"type":"boolean","name":"mybool","in":"path","required":true},{"type":"string","name":"query_param","in":"query"},{"type":"array","items":{"type":"boolean"},"collectionFormat":"multi","name":"myrepeatedbool","in":"query"}],"responses":{"200":{"description":"A successful response.","schema":{"$ref":"#/definitions/ignite.planet.mars.QueryWithQueryParamsResponse"}},"default":{"description":"An unexpected error response.","schema":{"$ref":"#/definitions/google.rpc.Status"}}}}}},"definitions":{"cosmos.base.query.v1beta1.PageRequest":{"description":"message SomeRequest {\n Foo some_parameter = 1;\n PageRequest pagination = 2;\n }","type":"object","title":"PageRequest is to be embedded in gRPC request messages for efficient\npagination. Ex:","properties":{"count_total":{"description":"count_total is set to true to indicate that the result set should include\na count of the total number of items available for pagination in UIs.\ncount_total is only respected when offset is used. It is ignored when key\nis set.","type":"boolean"},"key":{"description":"key is a value returned in PageResponse.next_key to begin\nquerying the next page most efficiently. Only one of offset or key\nshould be set.","type":"string","format":"byte"},"limit":{"description":"limit is the total number of results to be returned in the result page.\nIf left empty it will default to a value to be set by each app.","type":"string","format":"uint64"},"offset":{"description":"offset is a numeric offset that can be used when key is unavailable.\nIt is less efficient than using key. Only one of offset or key should\nbe set.","type":"string","format":"uint64"},"reverse":{"description":"reverse is set to true if results are to be returned in the descending order.","type":"boolean"}}},"cosmos.base.query.v1beta1.PageResponse":{"description":"PageResponse is to be embedded in gRPC response messages where the\ncorresponding request message has used PageRequest.\n\n message SomeResponse {\n repeated Bar results = 1;\n PageResponse page = 2;\n }","type":"object","properties":{"next_key":{"description":"next_key is the key to be passed to PageRequest.key to\nquery the next page most efficiently. It will be empty if\nthere are no more results.","type":"string","format":"byte"},"total":{"type":"string","format":"uint64","title":"total is total number of results available if PageRequest.count_total\nwas set, its value is undefined otherwise"}}},"google.protobuf.Any":{"type":"object","properties":{"@type":{"type":"string"}},"additionalProperties":{}},"google.rpc.Status":{"type":"object","properties":{"code":{"type":"integer","format":"int32"},"details":{"type":"array","items":{"type":"object","$ref":"#/definitions/google.protobuf.Any"}},"message":{"type":"string"}}},"ignite.planet.mars.MsgBarRequest":{"type":"object","properties":{"mytypefield":{"type":"string"}}},"ignite.planet.mars.MsgBarResponse":{"type":"object","properties":{"mytypefield":{"type":"string"}}},"ignite.planet.mars.MsgMyMessageRequest":{"type":"object","properties":{"mytypefield":{"type":"string"}}},"ignite.planet.mars.MsgMyMessageResponse":{"type":"object","properties":{"mytypefield":{"type":"string"}}},"ignite.planet.mars.QuerySimpleParamsResponse":{"type":"object","properties":{"bar":{"type":"string"}}},"ignite.planet.mars.QuerySimpleResponse":{"type":"object","properties":{"bar":{"type":"string"}}},"ignite.planet.mars.QueryWithPaginationResponse":{"type":"object","properties":{"pagination":{"$ref":"#/definitions/cosmos.base.query.v1beta1.PageResponse"}}},"ignite.planet.mars.QueryWithQueryParamsResponse":{"type":"object","properties":{"bar":{"type":"string"}}},"ignite.planet.mars.QueryWithQueryParamsWithPaginationResponse":{"type":"object","properties":{"bar":{"type":"string"},"pagination":{"$ref":"#/definitions/cosmos.base.query.v1beta1.PageResponse"}}}},"tags":[{"name":"Msg"},{"name":"Query"}]}
Loading
0