diff --git a/ee/katc/case.go b/ee/katc/case.go new file mode 100644 index 000000000..308f1d939 --- /dev/null +++ b/ee/katc/case.go @@ -0,0 +1,18 @@ +package katc + +import ( + "context" + "log/slog" + + "github.com/serenize/snaker" +) + +func camelToSnake(_ context.Context, _ *slog.Logger, row map[string][]byte) (map[string][]byte, error) { + snakeCaseRow := make(map[string][]byte) + for k, v := range row { + snakeCaseKey := snaker.CamelToSnake(k) + snakeCaseRow[snakeCaseKey] = v + } + + return snakeCaseRow, nil +} diff --git a/ee/katc/case_test.go b/ee/katc/case_test.go new file mode 100644 index 000000000..868c8caeb --- /dev/null +++ b/ee/katc/case_test.go @@ -0,0 +1,41 @@ +package katc + +import ( + "context" + "testing" + + "github.com/kolide/launcher/pkg/log/multislogger" + "github.com/stretchr/testify/require" +) + +func Test_camelToSnake(t *testing.T) { + t.Parallel() + + for _, tt := range []struct { + testCaseName string + input string + expectedOutput string + }{ + { + testCaseName: "basic camelcase column name", + input: "emailAddress", + expectedOutput: "email_address", + }, + { + testCaseName: "already snake case", + input: "email_address", + expectedOutput: "email_address", + }, + } { + tt := tt + t.Run(tt.testCaseName, func(t *testing.T) { + t.Parallel() + + outputRows, err := camelToSnake(context.TODO(), multislogger.NewNopLogger(), map[string][]byte{ + tt.input: nil, + }) + require.NoError(t, err) + require.Contains(t, outputRows, tt.expectedOutput) + }) + } +} diff --git a/ee/katc/config.go b/ee/katc/config.go index 6d4470167..18083c84d 100644 --- a/ee/katc/config.go +++ b/ee/katc/config.go @@ -59,6 +59,7 @@ type rowTransformStep struct { const ( snappyDecodeTransformStep = "snappy" deserializeFirefoxTransformStep = "deserialize_firefox" + camelToSnakeTransformStep = "camel_to_snake" ) func (r *rowTransformStep) UnmarshalJSON(data []byte) error { @@ -77,6 +78,10 @@ func (r *rowTransformStep) UnmarshalJSON(data []byte) error { r.name = deserializeFirefoxTransformStep r.transformFunc = deserializeFirefox return nil + case camelToSnakeTransformStep: + r.name = camelToSnakeTransformStep + r.transformFunc = camelToSnake + return nil default: return fmt.Errorf("unknown data processing step %s", s) } diff --git a/ee/katc/config_test.go b/ee/katc/config_test.go index e7ce530e1..fc29db539 100644 --- a/ee/katc/config_test.go +++ b/ee/katc/config_test.go @@ -49,7 +49,7 @@ func TestConstructKATCTables(t *testing.T) { "columns": ["col1", "col2"], "source": "/some/path/to/a/different/db.sqlite", "query": "SELECT col1, col2 FROM some_table;", - "row_transform_steps": [] + "row_transform_steps": ["camel_to_snake"] }`, runtime.GOOS), }, expectedPluginCount: 2,