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

Attempt of using generics instead of interface{} in component's configuration handling and creation. #346

Draft
wants to merge 6 commits into
base: develop
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
transform {
use "jq" {
jq_command = <<JQEOT
{
my_app_id: .app_id,
my_nested_prop: {
playback_rate: .contexts_com_snowplowanalytics_snowplow_media_player_1[0].playbackRate
}
}
JQEOT

timeout_sec = 5
snowplow_mode = true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
transform {
use "jq" {
jq_command = "[.]"
}
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,3 @@ transform {
script_path = env.JS_ERROR_PATH
}
}

transform {
use "lua" {
timeout_sec = 15
script_path = env.LUA_ADD_HELLO_PATH
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,3 @@ transform {
script_path = env.JS_ALTER_AID_2_PATH
}
}

transform {
use "lua" {
script_path = env.LUA_ADD_HELLO_PATH
}
}

This file was deleted.

11 changes: 11 additions & 0 deletions assets/test/transformconfig/TestGetTransformations/configs/jq.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
transform {
use "jq" {
jq_command = <<JQEOT
{
my_app_id: .app_id,
}
JQEOT

snowplow_mode = true
}
}

This file was deleted.

This file was deleted.

45 changes: 45 additions & 0 deletions config/generic/component.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package generic

import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
)

// Implemented by each component, like kinesis target
type configurableComponent[CONFIG any, COMPONENT any] interface {
DefaultConfig() *CONFIG
FromConfig(config *CONFIG) (*COMPONENT, error)
}

// Also implemented by each component, but generic type parameter 'OUT' specifies interface requested by consumer, e.g. 'Target' interface.
type providerOf[OUT any] interface {
Provide(input hcl.Body, ctx *hcl.EvalContext) (OUT, error)
}

func createComponent[CONFIG any, COMPONENT any](component configurableComponent[CONFIG, COMPONENT], input hcl.Body, ctx *hcl.EvalContext) (*COMPONENT, error) {
config := component.DefaultConfig()
decode[CONFIG](input, ctx, config)
readyComponent, err := component.FromConfig(config)

if err != nil {
return nil, err
}
return readyComponent, nil
}

func decode[CONFIG any](input hcl.Body, evalContext *hcl.EvalContext, configuration *CONFIG) error {
if input == nil {
return nil
}

if configuration == nil {
return nil
}

diag := gohcl.DecodeBody(input, evalContext, configuration)
if len(diag) > 0 {
return diag
}

return nil
}
26 changes: 26 additions & 0 deletions config/generic/component_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package generic

import (
"testing"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclparse"
"github.com/stretchr/testify/assert"
)

var supportedTargets = map[string]providerOf[target]{
"testTarget": &testProvider{},
}

func Test_CreatingTarget(t *testing.T) {
assert := assert.New(t)
hclFile, _ := hclparse.NewParser().ParseHCL([]byte(`test_string = "ateststring"`), "placeholder.hcl")
input := hclFile.Body

target, _ := supportedTargets["testTarget"].Provide(input, &hcl.EvalContext{})

output := target.Write("Test string")

assert.Equal("Written: Test string. This is value in config - ateststring", output)

}
34 changes: 34 additions & 0 deletions config/generic/example_component.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package generic

import (
"github.com/hashicorp/hcl/v2"
)

type myComponentConfig struct {
Test string `hcl:"test_string"`
}

type myComponent struct {
config myComponentConfig
}

// This one is used by component's clients. If you want ot use component, just use this struct
type testProvider struct{}

// Return component as interface, here as 'Target'
func (tp testProvider) Provide(input hcl.Body, ctx *hcl.EvalContext) (target, error) {
return createComponent[myComponentConfig, myComponent](tp, input, ctx)
}

func (testProvider) DefaultConfig() *myComponentConfig {
return &myComponentConfig{"some default value"}
}

func (testProvider) FromConfig(config *myComponentConfig) (*myComponent, error) {
return &myComponent{*config}, nil
}

// Implementing desired interfaces, like 'Target'
func (mc myComponent) Write(input string) string {
return "Written: " + input + ". This is value in config - " + mc.config.Test
}
5 changes: 5 additions & 0 deletions config/generic/target.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package generic

type target interface {
Write(input string) string
}
27 changes: 0 additions & 27 deletions docs/configuration_transformations_docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,12 @@ func TestScriptTransformationCustomScripts(t *testing.T) {
// Set env vars with paths to scripts
t.Setenv("JS_SCRIPT_PATH", jsScriptPath)

luaScriptPath := filepath.Join(assets.AssetsRootDir, "docs", "configuration", "transformations", "custom-scripts", "create-a-script-filter-example.lua")
t.Setenv("LUA_SCRIPT_PATH", luaScriptPath)

jsNonSnowplowScriptPath := filepath.Join(assets.AssetsRootDir, "docs", "configuration", "transformations", "custom-scripts", "examples", "js-non-snowplow-script-example.js")
t.Setenv("JS_NON_SNOWPLOW_SCRIPT_PATH", jsNonSnowplowScriptPath)

jsSnowplowScriptPath := filepath.Join(assets.AssetsRootDir, "docs", "configuration", "transformations", "custom-scripts", "examples", "js-snowplow-script-example.js")
t.Setenv("JS_SNOWPLOW_SCRIPT_PATH", jsSnowplowScriptPath)

luaNonSnowplowScriptPath := filepath.Join(assets.AssetsRootDir, "docs", "configuration", "transformations", "custom-scripts", "examples", "lua-script-example.lua")
t.Setenv("LUA_SCRIPT_EXAMPLE_PATH", luaNonSnowplowScriptPath)

baseDir := filepath.Join(assets.AssetsRootDir, "docs", "configuration", "transformations", "custom-scripts")

filesInBaseDir, err := ioutil.ReadDir(baseDir)
Expand Down Expand Up @@ -109,9 +103,6 @@ func TestScriptTransformationCustomScripts(t *testing.T) {
case ".js":
// Test that all of our JS snippets compile with the engine, pass smoke test, and successfully create a transformation function
testJSScriptCompiles(t, file)
case ".lua":
// Test that all of our Lua snippets compile with the engine, pass smoke test, and successfully create a transformation function
testLuaScriptCompiles(t, file)
case ".hcl":
isFull := strings.Contains(file, "full-example")

Expand Down Expand Up @@ -168,8 +159,6 @@ func testTransformationConfig(t *testing.T, filepath string, fullExample bool) {
configObject = &transform.GTMSSPreviewConfig{}
case "js":
configObject = &engine.JSEngineConfig{}
case "lua":
configObject = &engine.LuaEngineConfig{}
default:
assert.Fail(fmt.Sprint("Source not recognised: ", use.Name))
}
Expand Down Expand Up @@ -212,19 +201,3 @@ func testJSScriptCompiles(t *testing.T, scriptPath string) {
}

}

func testLuaScriptCompiles(t *testing.T, scriptPath string) {
assert := assert.New(t)

luaConfig := &engine.LuaEngineConfig{
ScriptPath: scriptPath,
RunTimeout: 5, // This is needed here as we're providing config directly, not using defaults.
}

// LuaConfigFunction validates and smoke tests the function, and only returns valid transformation functions.
luaTransformationFunction, err := engine.LuaConfigFunction(luaConfig)
assert.NotNil(luaTransformationFunction, scriptPath)
if err != nil {
t.Fatalf("NewLuaEngine failed with error: %s. Script: %s", err.Error(), string(scriptPath))
}
}
9 changes: 4 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ require (
github.com/xdg/scram v1.0.5
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/oauth2 v0.19.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/oauth2 v0.19.0
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/api v0.172.0 // indirect
google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda
Expand All @@ -50,12 +50,10 @@ require (
github.com/davecgh/go-spew v1.1.1
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204
github.com/hashicorp/hcl/v2 v2.20.1
github.com/itchyny/gojq v0.12.16
github.com/json-iterator/go v1.1.12
github.com/snowplow/snowplow-golang-tracker/v2 v2.4.1
github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7
github.com/yuin/gopher-lua v1.1.1
github.com/zclconf/go-cty v1.14.4
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf
)

require (
Expand Down Expand Up @@ -91,6 +89,7 @@ require (
github.com/hashicorp/go-memdb v1.3.4 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/itchyny/timefmt-go v0.1.6 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/gofork v1.7.6 // indirect
Expand Down
Loading
Loading