-
Notifications
You must be signed in to change notification settings - Fork 5
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
base: modules
Are you sure you want to change the base?
Templating 2.0 #192
Changes from 7 commits
a2eb916
62b489c
97040f1
c445f98
0603a00
ad4bb95
7168cc8
dbfe10e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,4 +51,7 @@ bin | |
pkg | ||
|
||
# NPM artifacts | ||
internal/hardhat/node_modules/ | ||
internal/hardhat/node_modules/ | ||
|
||
# Misc | ||
.DS_Store |
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 | ||
ModuleConfigDir string | ||
ModuleSecretFile string | ||
ModuleLogDir string | ||
ModuleJwtKeyFile string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
||
} |
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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
} |
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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" | ||
} |
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 | ||
} |
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Getters There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
} |
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
} |
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}} |
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 |
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)) | ||
} | ||
|
||
} |
There was a problem hiding this comment.
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