Skip to content
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

Templating 2.0 #192

Draft
wants to merge 8 commits into
base: modules
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 7 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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,7 @@ bin
pkg

# NPM artifacts
internal/hardhat/node_modules/
internal/hardhat/node_modules/

# Misc
.DS_Store
29 changes: 29 additions & 0 deletions hyperdrive-cli-2/template/adapter/data-source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package adapter

// Struct to pass into the template engine containing all necessary data and methods for populating a template.
type AdapterDataSource struct {
GetProjectName string
Copy link
Contributor Author

@huy-nodeset huy-nodeset Dec 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func() not string. Comes from hyperdrive itself

ModuleConfigDir string
ModuleSecretFile string
ModuleLogDir string
ModuleJwtKeyFile string
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably will all be functions/methods.

Add Get to beginning and update spec

}

// Create a new AdapterDataSource struct.
func NewAdapterDataSource(
getProjectName string,
moduleConfigDir string,
moduleSecretFile string,
moduleLogDir string,
moduleJwtKeyFile string,
customFields map[string]string,
) (*AdapterDataSource, error) {
return &AdapterDataSource{
GetProjectName: getProjectName,
ModuleConfigDir: moduleConfigDir,
ModuleSecretFile: moduleSecretFile,
ModuleLogDir: moduleLogDir,
ModuleJwtKeyFile: moduleJwtKeyFile,
}, nil

}
44 changes: 44 additions & 0 deletions hyperdrive-cli-2/template/adapter/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package adapter

import (
"fmt"
"os"
"path/filepath"
"text/template"

"github.com/alessio/shellescape"
)

type AdapterTemplate struct {
Src string
Dst string
}

func (t AdapterTemplate) Write(data interface{}) error {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be re-used with adapter/service

// Open the output file, creating it if it doesn't exist
runtimeFile, err := os.OpenFile(t.Dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0664)
if err != nil {
return fmt.Errorf("Could not open templated file %s for writing: %w", shellescape.Quote(t.Dst), err)
}
defer runtimeFile.Close()

// Map dynamic getters and parse the template
tmpl, err := template.New(filepath.Base(t.Src)).Funcs(template.FuncMap{}).ParseFiles(t.Src)
if err != nil {
return fmt.Errorf("Error reading template file %s: %w", shellescape.Quote(t.Src), err)
}

// Replace template variables and write the result
err = tmpl.Execute(runtimeFile, data)
if err != nil {
return fmt.Errorf("Error writing and substituting template: %w", err)
}

// If the file was newly created, 0664 may have been altered by umask, so chmod back to 0664.
err = os.Chmod(t.Dst, 0664)
if err != nil {
return fmt.Errorf("Could not set templated file (%s) permissions: %w", shellescape.Quote(t.Dst), err)
}

return nil
}
40 changes: 40 additions & 0 deletions hyperdrive-cli-2/template/metadata/data-source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package metadata

import (
"fmt"
"strings"
)

// Struct to pass into the template engine containing all necessary data and methods for populating a template.
type MetadataDataSource struct {
CustomFields map[string]string
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need (TBD)

}

// Create a new TemplateDataSource struct.
func NewMetadataDataSource(
customFields map[string]string,
) (*MetadataDataSource, error) {
return &MetadataDataSource{
CustomFields: customFields,
}, nil

}

func (t *MetadataDataSource) GetValue(fqpn string) (string, error) {
if value, exists := t.CustomFields[fqpn]; exists {
return value, nil
}
return "", fmt.Errorf("key not found: %s", fqpn)
}

func (t *MetadataDataSource) GetValueArray(fqpn string, delimiter string) ([]string, error) {
if value, exists := t.CustomFields[fqpn]; exists {
return strings.Split(value, delimiter), nil
}
return nil, fmt.Errorf("key not found: %s", fqpn)
}

func (t *MetadataDataSource) UseDefault() string {
// TODO
return "defaultValue"
}
33 changes: 33 additions & 0 deletions hyperdrive-cli-2/template/metadata/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package metadata

import (
"fmt"
"strings"
"text/template"

"github.com/alessio/shellescape"
)

type MetadataTemplate struct {
Content string
}

func (t MetadataTemplate) Write(data interface{}) (string, error) {
// Map dynamic getters and parse the template
tmpl, err := template.New("content").Funcs(template.FuncMap{
"GetValue": data.(*MetadataDataSource).GetValue,
"GetValueArray": data.(*MetadataDataSource).GetValueArray,
"UseDefault": data.(*MetadataDataSource).UseDefault,
}).Parse(t.Content)
if err != nil {
return "", fmt.Errorf("Error reading template file %s: %w", shellescape.Quote(t.Content), err)
}

var output strings.Builder
err = tmpl.Execute(&output, data)
if err != nil {
return "", fmt.Errorf("error executing template: %w", err)
}

return output.String(), nil
}
52 changes: 52 additions & 0 deletions hyperdrive-cli-2/template/service/data-source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package service

import (
"fmt"
"strings"
)

// Struct to pass into the template engine containing all necessary data and methods for populating a template.
type ServiceDataSource struct {
GetProjectName string
ModuleConfigDir string
ModuleDataDir string
HyperdriveDaemonUrl string
HyperdriveJwtKeyFile string
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getters

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update spec


CustomFields map[string]string
}

// Create a new ServiceDataSource struct.
func NewServiceDataSource(
getProjectName string,
moduleConfigDir string,
moduleDataDir string,
hyperdriveDaemonUrl string,
hyperdriveJwtKeyFile string,
customFields map[string]string,
) (*ServiceDataSource, error) {
return &ServiceDataSource{
GetProjectName: getProjectName,
ModuleConfigDir: moduleConfigDir,
ModuleDataDir: moduleDataDir,
HyperdriveDaemonUrl: hyperdriveDaemonUrl,
HyperdriveJwtKeyFile: hyperdriveJwtKeyFile,

CustomFields: customFields,
}, nil

}

func (t *ServiceDataSource) GetValue(fqpn string) (string, error) {
if value, exists := t.CustomFields[fqpn]; exists {
return value, nil
}
return "", fmt.Errorf("key not found: %s", fqpn)
}

func (t *ServiceDataSource) GetValueArray(fqpn string, delimiter string) ([]string, error) {
if value, exists := t.CustomFields[fqpn]; exists {
return strings.Split(value, delimiter), nil
}
return nil, fmt.Errorf("key not found: %s", fqpn)
}
47 changes: 47 additions & 0 deletions hyperdrive-cli-2/template/service/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package service

import (
"fmt"
"os"
"path/filepath"
"text/template"

"github.com/alessio/shellescape"
)

type ServiceTemplate struct {
Src string
Dst string
}

func (t ServiceTemplate) Write(data interface{}) error {
// Open the output file, creating it if it doesn't exist
runtimeFile, err := os.OpenFile(t.Dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0664)
if err != nil {
return fmt.Errorf("Could not open templated file %s for writing: %w", shellescape.Quote(t.Dst), err)
}
defer runtimeFile.Close()

// Map dynamic getters and parse the template
tmpl, err := template.New(filepath.Base(t.Src)).Funcs(template.FuncMap{
"GetValue": data.(*ServiceDataSource).GetValue,
"GetValueArray": data.(*ServiceDataSource).GetValueArray,
}).ParseFiles(t.Src)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not need this. The datatype should already be able to handle this.

if err != nil {
return fmt.Errorf("Error reading template file %s: %w", shellescape.Quote(t.Src), err)
}

// Replace template variables and write the result
err = tmpl.Execute(runtimeFile, data)
if err != nil {
return fmt.Errorf("Error writing and substituting template: %w", err)
}

// If the file was newly created, 0664 may have been altered by umask, so chmod back to 0664.
err = os.Chmod(t.Dst, 0664)
if err != nil {
return fmt.Errorf("Could not set templated file (%s) permissions: %w", shellescape.Quote(t.Dst), err)
}

return nil
}
58 changes: 58 additions & 0 deletions hyperdrive-cli-2/testing/adapter/adapter_template_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package testing

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

"github.com/nodeset-org/hyperdrive/hyperdrive-cli-2/template/adapter"
)

func TestAdapterTemplateWrite(t *testing.T) {
// Create a temporary directory to store the template and runtime files
tempDir, err := os.MkdirTemp("", "adapter_template_test")
if err != nil {
t.Fatalf("Failed to create temporary directory: %v", err)
}
defer os.RemoveAll(tempDir)

templateFile, err := filepath.Abs("example_adapter_template.tmpl")
if err != nil {
t.Fatalf("Failed to get absolute path of template file: %v", err)
}
outputFile := filepath.Join(tempDir, "output.yml")

dataSource := &adapter.AdapterDataSource{
GetProjectName: "hd2-service",
ModuleConfigDir: "/path/to/config",
ModuleSecretFile: "/path/to/secret",
ModuleLogDir: "/path/to/log",
ModuleJwtKeyFile: "/path/to/jwt",
}

tmpl := adapter.AdapterTemplate{
Src: templateFile,
Dst: outputFile,
}

err = tmpl.Write(dataSource)
if err != nil {
t.Fatalf("Failed to render template: %v", err)
}

outputContent, err := os.ReadFile(outputFile)
if err != nil {
t.Fatalf("Failed to read output file: %v", err)
}

expectedContent, err := os.ReadFile("expected_adapter_output.yml")
if err != nil {
t.Fatalf("Failed to read expected output file: %v", err)
}

if strings.TrimSpace(string(outputContent)) != strings.TrimSpace(string(expectedContent)) {
t.Errorf("Rendered output does not match expected.\nGot:\n%s\nExpected:\n%s", string(outputContent), string(expectedContent))
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
project_name: {{.GetProjectName}}
config_dir: {{.ModuleConfigDir}}
secret_file: {{.ModuleSecretFile}}
log_dir: {{.ModuleLogDir}}
jwt_key_file: {{.ModuleJwtKeyFile}}
5 changes: 5 additions & 0 deletions hyperdrive-cli-2/testing/adapter/expected_adapter_output.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
project_name: hd2-service
config_dir: /path/to/config
secret_file: /path/to/secret
log_dir: /path/to/log
jwt_key_file: /path/to/jwt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
network: {{GetValue "network"}}
addresses: {{range $index, $val := GetValueArray "addresses" ","}}{{if $index}} {{end}}{{$val}}{{end}}
default_value: {{UseDefault}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
network: mainnet
addresses: addr1 addr2 addr3
default_value: defaultValue
49 changes: 49 additions & 0 deletions hyperdrive-cli-2/testing/metadata/metadata_template_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package testing

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

"github.com/nodeset-org/hyperdrive/hyperdrive-cli-2/template/metadata"
)

func TestMetadataTemplateWrite(t *testing.T) {
templateFile, err := filepath.Abs("example_metadata_template.tmpl")
if err != nil {
t.Fatalf("Failed to get absolute path of template file: %v", err)
}
templateContent, err := os.ReadFile(templateFile)
if err != nil {
t.Fatalf("Failed to read template file: %v", err)
}

expectedContent, err := os.ReadFile("expected_metadata_output.yml")
if err != nil {
t.Fatalf("Failed to read expected output file: %v", err)
}

customFields := map[string]string{
"network": "mainnet",
"addresses": "addr1,addr2,addr3",
}

dataSource := &metadata.MetadataDataSource{
CustomFields: customFields,
}

tmpl := metadata.MetadataTemplate{
Content: string(templateContent),
}

output, err := tmpl.Write(dataSource)
if err != nil {
t.Fatalf("Failed to render template: %v", err)
}

if strings.TrimSpace(string(output)) != strings.TrimSpace(string(expectedContent)) {
t.Errorf("Rendered output does not match expected.\nGot:\n%s\nExpected:\n%s", string(output), string(expectedContent))
}

}
Loading
Loading