Some checks are pending
Docs Deploy / build_and_deploy (push) Waiting to run
Generate Docs / cli (push) Waiting to run
Generate Config Doc / cli (push) Waiting to run
Go formatting / go-formatting (push) Waiting to run
Check links / markdown-link-check (push) Waiting to run
Integration / pre-test (push) Waiting to run
Integration / test on (push) Blocked by required conditions
Integration / status (push) Blocked by required conditions
Lint / Lint Go code (push) Waiting to run
Test / test (ubuntu-latest) (push) Waiting to run
278 lines
7.4 KiB
Go
278 lines
7.4 KiB
Go
package chain
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
chainconfig "github.com/ignite/cli/v29/ignite/config/chain"
|
|
"github.com/ignite/cli/v29/ignite/config/chain/base"
|
|
"github.com/ignite/cli/v29/ignite/pkg/cache"
|
|
"github.com/ignite/cli/v29/ignite/pkg/cliui/icons"
|
|
"github.com/ignite/cli/v29/ignite/pkg/cosmosgen"
|
|
"github.com/ignite/cli/v29/ignite/pkg/errors"
|
|
"github.com/ignite/cli/v29/ignite/pkg/events"
|
|
)
|
|
|
|
type generateOptions struct {
|
|
useCache bool
|
|
isProtoVendorEnabled bool
|
|
isGoEnabled bool
|
|
isTSClientEnabled bool
|
|
isComposablesEnabled bool
|
|
isOpenAPIEnabled bool
|
|
openAPIExcludeList []string
|
|
tsClientPath string
|
|
composablesPath string
|
|
}
|
|
|
|
// GenerateTarget is a target to generate code for from proto files.
|
|
type GenerateTarget func(*generateOptions)
|
|
|
|
// GenerateGo enables generating proto based Go code needed for the chain's source code.
|
|
func GenerateGo() GenerateTarget {
|
|
return func(o *generateOptions) {
|
|
o.isGoEnabled = true
|
|
}
|
|
}
|
|
|
|
// GenerateTSClient enables generating proto based Typescript Client.
|
|
// The path assigns the output path to use for the generated Typescript client
|
|
// overriding the configured or default path. Path can be an empty string.
|
|
func GenerateTSClient(path string, useCache bool) GenerateTarget {
|
|
return func(o *generateOptions) {
|
|
o.isTSClientEnabled = true
|
|
o.tsClientPath = path
|
|
o.useCache = useCache
|
|
}
|
|
}
|
|
|
|
// GenerateComposables enables generating proto based Typescript Client and Vue 3 composables.
|
|
func GenerateComposables(path string) GenerateTarget {
|
|
return func(o *generateOptions) {
|
|
o.isTSClientEnabled = true
|
|
o.isComposablesEnabled = true
|
|
o.composablesPath = path
|
|
}
|
|
}
|
|
|
|
// GenerateOpenAPI enables generating OpenAPI spec for your chain.
|
|
func GenerateOpenAPI(excludeList []string) GenerateTarget {
|
|
return func(o *generateOptions) {
|
|
o.isOpenAPIEnabled = true
|
|
o.openAPIExcludeList = excludeList
|
|
}
|
|
}
|
|
|
|
// GenerateProtoVendor enables `proto_vendor` folder generation.
|
|
// Proto vendor is generated from Go dependencies that contain proto files that
|
|
// are not included in the app's Buf config.
|
|
// Enabling proto vendoring might update Buf config with missing dependencies
|
|
// if a Go dependency contains proto files and a Buf config with a name that is
|
|
// not listed in the Buf dependencies.
|
|
func GenerateProtoVendor() GenerateTarget {
|
|
return func(o *generateOptions) {
|
|
o.isProtoVendorEnabled = true
|
|
}
|
|
}
|
|
|
|
// generateFromConfig makes code generation from proto files from the given config.
|
|
func (c *Chain) generateFromConfig(ctx context.Context, cacheStorage cache.Storage, generateClients bool) error {
|
|
conf, err := c.Config()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Additional code generation targets
|
|
var targets []GenerateTarget
|
|
|
|
if conf.Client.OpenAPI.Path != "" {
|
|
targets = append(targets, GenerateOpenAPI(conf.Client.OpenAPI.ExcludeList))
|
|
}
|
|
|
|
if generateClients {
|
|
if p := conf.Client.Typescript.Path; p != "" {
|
|
targets = append(targets, GenerateTSClient(p, true))
|
|
}
|
|
|
|
if p := conf.Client.Composables.Path; p != "" {
|
|
targets = append(targets, GenerateComposables(p))
|
|
}
|
|
}
|
|
|
|
// Generate proto based code for Go and optionally for any optional targets
|
|
return c.Generate(ctx, cacheStorage, GenerateGo(), targets...)
|
|
}
|
|
|
|
// Generate makes code generation from proto files for given target and additionalTargets.
|
|
func (c *Chain) Generate(
|
|
ctx context.Context,
|
|
cacheStorage cache.Storage,
|
|
target GenerateTarget,
|
|
additionalTargets ...GenerateTarget,
|
|
) error {
|
|
var targetOptions generateOptions
|
|
|
|
for _, apply := range append(additionalTargets, target) {
|
|
apply(&targetOptions)
|
|
}
|
|
|
|
conf, err := c.Config()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c.ev.Send("Building proto...", events.ProgressUpdate())
|
|
|
|
options := []cosmosgen.Option{cosmosgen.CollectEvents(c.ev)}
|
|
|
|
if targetOptions.isGoEnabled {
|
|
options = append(options, cosmosgen.WithGoGeneration())
|
|
}
|
|
|
|
if targetOptions.isProtoVendorEnabled {
|
|
options = append(options, cosmosgen.UpdateBufModule())
|
|
}
|
|
|
|
var (
|
|
openAPIPath, tsClientPath, composablesPath string
|
|
updateConfig bool
|
|
)
|
|
|
|
if targetOptions.isOpenAPIEnabled {
|
|
openAPIPath = conf.Client.OpenAPI.Path
|
|
if openAPIPath == "" {
|
|
openAPIPath = chainconfig.DefaultOpenAPIPath
|
|
}
|
|
|
|
// Non-absolute OpenAPI paths must be treated as relative to the app directory
|
|
if !filepath.IsAbs(openAPIPath) {
|
|
openAPIPath = filepath.Join(c.app.Path, openAPIPath)
|
|
}
|
|
|
|
options = append(options, cosmosgen.WithOpenAPIGeneration(openAPIPath, targetOptions.openAPIExcludeList))
|
|
}
|
|
|
|
if targetOptions.isTSClientEnabled {
|
|
tsClientPath = targetOptions.tsClientPath
|
|
if tsClientPath == "" {
|
|
tsClientPath = chainconfig.TSClientPath(*conf)
|
|
|
|
// When TS client is generated make sure the config is updated
|
|
// with the output path when the client path option is empty.
|
|
if conf.Client.Typescript.Path == "" {
|
|
conf.Client.Typescript.Path = tsClientPath
|
|
updateConfig = true
|
|
}
|
|
}
|
|
|
|
// Non-absolute TS client output paths must be treated as relative to the app directory
|
|
if !filepath.IsAbs(tsClientPath) {
|
|
tsClientPath = filepath.Join(c.app.Path, tsClientPath)
|
|
}
|
|
|
|
options = append(options,
|
|
cosmosgen.WithTSClientGeneration(
|
|
cosmosgen.TypescriptModulePath(tsClientPath),
|
|
tsClientPath,
|
|
targetOptions.useCache,
|
|
),
|
|
)
|
|
}
|
|
|
|
if targetOptions.isComposablesEnabled {
|
|
composablesPath = targetOptions.composablesPath
|
|
|
|
if composablesPath == "" {
|
|
composablesPath = chainconfig.ComposablesPath(conf)
|
|
|
|
if conf.Client.Composables.Path == "" {
|
|
conf.Client.Composables.Path = composablesPath
|
|
updateConfig = true
|
|
}
|
|
}
|
|
|
|
// Non-absolute Composables output paths must be treated as relative to the app directory
|
|
if !filepath.IsAbs(composablesPath) {
|
|
composablesPath = filepath.Join(c.app.Path, composablesPath)
|
|
}
|
|
|
|
options = append(options,
|
|
cosmosgen.WithComposablesGeneration(
|
|
cosmosgen.ComposableModulePath(composablesPath),
|
|
composablesPath,
|
|
),
|
|
)
|
|
}
|
|
|
|
if err := cosmosgen.Generate(
|
|
ctx,
|
|
cacheStorage,
|
|
c.app.Path,
|
|
conf.Build.Proto.Path,
|
|
c.app.ImportPath,
|
|
chainconfig.DefaultVuePath,
|
|
options...,
|
|
); err != nil {
|
|
return &CannotBuildAppError{err}
|
|
}
|
|
|
|
// Check if the client config options have to be updated with the paths of the generated code
|
|
if updateConfig {
|
|
if err := c.saveClientConfig(conf.Client); err != nil {
|
|
return errors.Errorf("error adding generated paths to config file: %w", err)
|
|
}
|
|
}
|
|
|
|
if c.options.printGeneratedPaths {
|
|
if targetOptions.isTSClientEnabled {
|
|
c.ev.Send(
|
|
fmt.Sprintf("Typescript client path: %s", tsClientPath),
|
|
events.Icon(icons.Bullet),
|
|
events.ProgressFinish(),
|
|
)
|
|
}
|
|
|
|
if targetOptions.isComposablesEnabled {
|
|
c.ev.Send(
|
|
fmt.Sprintf("Vue composables path: %s", composablesPath),
|
|
events.Icon(icons.Bullet),
|
|
events.ProgressFinish(),
|
|
)
|
|
}
|
|
|
|
if targetOptions.isOpenAPIEnabled {
|
|
c.ev.Send(
|
|
fmt.Sprintf("OpenAPI path: %s", openAPIPath),
|
|
events.Icon(icons.Bullet),
|
|
events.ProgressFinish(),
|
|
)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c Chain) saveClientConfig(client base.Client) error {
|
|
path := c.ConfigPath()
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
// Initialize the config to the file values ignoring empty
|
|
// values that otherwise would be initialized to defaults.
|
|
// Defaults must be ignored to avoid writing them to the
|
|
// YAML config file when they are not present.
|
|
var cfg chainconfig.Config
|
|
if err := cfg.Decode(file); err != nil {
|
|
return err
|
|
}
|
|
|
|
cfg.Client = client
|
|
|
|
return chainconfig.Save(cfg, path)
|
|
}
|