Add Remote field to SmartContractCreateRequest and GrpcConnectionInfo struct to SmartContract model for remote smart contract execution via gRPC.
222 lines
5.6 KiB
Go
Executable File
222 lines
5.6 KiB
Go
Executable File
package credentials
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// ChainConfig represents a single chain configuration
|
|
type ChainConfig struct {
|
|
Name string `yaml:"name"`
|
|
PublicId string `yaml:"publicId"`
|
|
AuthKeyId string `yaml:"authKeyId"`
|
|
AuthKey string `yaml:"authKey"`
|
|
Endpoint string `yaml:"endpoint"`
|
|
}
|
|
|
|
// Config represents the entire configuration file structure
|
|
type Config struct {
|
|
Default string `yaml:"default"`
|
|
Chains []ChainConfig `yaml:"chains"`
|
|
node *yaml.Node // Store the original node for preserving formatting
|
|
}
|
|
|
|
// LoadConfig reads and parses a YAML configuration file
|
|
func LoadConfig(filePath string) (*Config, error) {
|
|
// Expand the file path (handles ~/, ./, and environment variables)
|
|
expandedPath, err := expandPath(filePath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to expand path %s: %w", filePath, err)
|
|
}
|
|
|
|
// Read the file
|
|
data, err := ioutil.ReadFile(expandedPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read config file %s: %w", expandedPath, err)
|
|
}
|
|
|
|
// Parse YAML with Node to preserve formatting
|
|
var node yaml.Node
|
|
if err := yaml.Unmarshal(data, &node); err != nil {
|
|
return nil, fmt.Errorf("failed to parse YAML config: %w", err)
|
|
}
|
|
|
|
var config Config
|
|
if err := node.Decode(&config); err != nil {
|
|
return nil, fmt.Errorf("failed to decode YAML config: %w", err)
|
|
}
|
|
|
|
// Store the node for later use when saving
|
|
config.node = &node
|
|
|
|
return &config, nil
|
|
}
|
|
|
|
// LoadConfigFromString parses YAML configuration from a string
|
|
func LoadConfigFromString(yamlContent string) (*Config, error) {
|
|
var node yaml.Node
|
|
if err := yaml.Unmarshal([]byte(yamlContent), &node); err != nil {
|
|
return nil, fmt.Errorf("failed to parse YAML config: %w", err)
|
|
}
|
|
|
|
var config Config
|
|
if err := node.Decode(&config); err != nil {
|
|
return nil, fmt.Errorf("failed to decode YAML config: %w", err)
|
|
}
|
|
|
|
config.node = &node
|
|
return &config, nil
|
|
}
|
|
|
|
// GetDefaultChain returns the chain configuration for the default publicId
|
|
func (c *Config) GetDefaultChain() *ChainConfig {
|
|
for i := range c.Chains {
|
|
if c.Chains[i].PublicId == c.Default {
|
|
return &c.Chains[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetChainByPublicId returns the chain configuration for the specified publicId
|
|
func (c *Config) GetChainByPublicId(publicId string) *ChainConfig {
|
|
for i := range c.Chains {
|
|
if c.Chains[i].PublicId == publicId {
|
|
return &c.Chains[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ListChains returns all chain names
|
|
func (c *Config) ListChains() []string {
|
|
names := make([]string, len(c.Chains))
|
|
for i, chain := range c.Chains {
|
|
names[i] = chain.Name
|
|
}
|
|
return names
|
|
}
|
|
|
|
// AddChain adds a chain configuration to the configuration
|
|
func (c *Config) AddChain(chain *ChainConfig) {
|
|
c.Chains = append(c.Chains, *chain)
|
|
}
|
|
|
|
// SetDefault sets the default chain publicId
|
|
func (c *Config) SetDefault(publicId string) {
|
|
c.Default = publicId
|
|
}
|
|
|
|
// DeleteChain deletes a chain configuration from the configuration
|
|
func (c *Config) DeleteChain(publicId string) error {
|
|
if publicId == c.Default {
|
|
return fmt.Errorf("cannot delete default chain")
|
|
}
|
|
for i, chain := range c.Chains {
|
|
if chain.PublicId == publicId {
|
|
c.Chains = append(c.Chains[:i], c.Chains[i+1:]...)
|
|
return nil
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SaveConfig writes the configuration to a YAML file
|
|
func (c *Config) SaveConfig(filePath string) error {
|
|
expandedPath, err := expandPath(filePath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to expand path %s: %w", filePath, err)
|
|
}
|
|
|
|
var data []byte
|
|
if c.node != nil {
|
|
// Update the node with current config values while preserving formatting
|
|
if err := c.node.Encode(c); err != nil {
|
|
return fmt.Errorf("failed to encode config to node: %w", err)
|
|
}
|
|
|
|
// Ensure newlines between chain entries
|
|
c.ensureChainSeparation()
|
|
|
|
// Marshal the node to preserve comments and formatting
|
|
data, err = yaml.Marshal(c.node)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal config to YAML: %w", err)
|
|
}
|
|
} else {
|
|
// Fallback to regular marshaling if no node is available
|
|
data, err = yaml.Marshal(c)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal config to YAML: %w", err)
|
|
}
|
|
}
|
|
|
|
if err := ioutil.WriteFile(expandedPath, data, 0644); err != nil {
|
|
return fmt.Errorf("failed to write config file %s: %w", expandedPath, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ensureChainSeparation adds blank lines between chain entries in the YAML node
|
|
func (c *Config) ensureChainSeparation() {
|
|
if c.node == nil || len(c.node.Content) == 0 {
|
|
return
|
|
}
|
|
|
|
// Navigate to the document node, then the mapping node
|
|
docNode := c.node
|
|
if docNode.Kind == yaml.DocumentNode && len(docNode.Content) > 0 {
|
|
docNode = docNode.Content[0]
|
|
}
|
|
|
|
if docNode.Kind != yaml.MappingNode {
|
|
return
|
|
}
|
|
|
|
// Find the "chains" key in the mapping
|
|
for i := 0; i < len(docNode.Content); i += 2 {
|
|
if i+1 >= len(docNode.Content) {
|
|
break
|
|
}
|
|
|
|
keyNode := docNode.Content[i]
|
|
valueNode := docNode.Content[i+1]
|
|
|
|
if keyNode.Value == "chains" && valueNode.Kind == yaml.SequenceNode {
|
|
// Add HeadComment (newline before) to each chain entry except the first
|
|
for j := 1; j < len(valueNode.Content); j++ {
|
|
chainNode := valueNode.Content[j]
|
|
if chainNode.HeadComment == "" {
|
|
chainNode.HeadComment = "\n"
|
|
}
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// expandPath is a simple path expansion function (you can replace this with your utils.ExpandPath)
|
|
func expandPath(path string) (string, error) {
|
|
// Expand environment variables
|
|
path = os.ExpandEnv(path)
|
|
|
|
// Handle home directory
|
|
if len(path) > 0 && path[0] == '~' {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(path) == 1 {
|
|
path = home
|
|
} else if path[1] == '/' {
|
|
path = home + path[1:]
|
|
}
|
|
}
|
|
|
|
return path, nil
|
|
}
|