[bug-hunter] Heartbeat api monitor expands dotted params keys instead of preserving literals

Open Beginner friendly
#52,328 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
go
Domain
api, testing

Research direction

Start with x-pack/heartbeat/cmd/root.go:79-80 and the config path used by x-pack/heartbeat/monitors/api/api.go:44. Compare the existing dotted-key tests in x-pack/heartbeat/cmd/root_test.go:100-156 with the reproduction test described in the issue, then run the focused Go test. Done means the API monitor preserves params["subdomain.example.com"] literally without nested expansion.

Written by the indexing model from the issue text.

Description

needs_team

Impact

x-pack Heartbeat type: api monitors that pass dotted params keys (for example "subdomain.example.com") do not receive the original literal key. The key is dot-expanded into nested objects, which breaks journey code expecting params["subdomain.example.com"].

This is user-impacting because api monitors reuse the browser source-job/params pipeline, but only browser streams get dotted-key preservation during config transform.

Reproduction Steps

  1. In /home/runner/work/beats/beats/x-pack/heartbeat/cmd, add this new test file:
package cmd

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"google.golang.org/protobuf/types/known/structpb"

	"github.com/elastic/elastic-agent-client/v7/pkg/client"
	"github.com/elastic/elastic-agent-client/v7/pkg/proto"
)

func TestReproAPIParamsDottedKeys(t *testing.T) {
	var rawIn proto.UnitExpectedConfig
	err := readRawIn("testdata/simple-browser.in.json", &rawIn)
	require.NoError(t, err, "failed to read fixture")

	sourceMap := rawIn.GetSource().AsMap()
	streams, ok := sourceMap["streams"].([]interface{})
	require.True(t, ok, "expected streams slice")
	require.NotEmpty(t, streams, "expected at least one stream")

	stream, ok := streams[0].(map[string]interface{})
	require.True(t, ok, "expected stream map")
	stream["type"] = "api"
	stream["params"] = map[string]interface{}{
		"subdomain.example.com": "value2",
	}

	rawIn.Source, err = structpb.NewStruct(sourceMap)
	require.NoError(t, err, "failed to rebuild proto source")

	cfg, err := heartbeatCfg(&rawIn, &client.AgentInfo{ID: "abc7d0a8-ce04-4663-95da-ff6d537c268f", Version: "8.13.1"})
	require.NoError(t, err, "heartbeatCfg returned error")

	got, err := cfgToArrMap(cfg)
	require.NoError(t, err, "failed to unpack cfg")
	require.NotEmpty(t, got, "expected at least one monitor config")

	params, ok := got[0]["params"].(map[string]interface{})
	require.True(t, ok, "expected params map")
	assert.Equal(t, "value2", params["subdomain.example.com"], "api monitor params dotted key should be preserved literally")
	assert.NotContains(t, params, "subdomain", "api monitor params dotted key should not be expanded")
}
  1. Run:
go test -count=1 -run TestReproAPIParamsDottedKeys ./x-pack/heartbeat/cmd

Expected vs Actual

Expected: For type: api, dotted keys under params are preserved literally, so params["subdomain.example.com"] == "value2".

Actual: Dotted key is expanded to nested structure; the literal key is missing.

Actual failing output:

--- FAIL: TestReproAPIParamsDottedKeys (0.00s)
    repro_api_params_test.go:43:
        Error:       Not equal:
                     expected: string("value2")
                     actual  : <nil>(<nil>)
        Messages:    api monitor params dotted key should be preserved literally
    repro_api_params_test.go:44:
        Error:       map[string]interface {}{"subdomain":map[string]interface {}{"example":map[string]interface {}{"com":"value2"}}} should not contain "subdomain"
        Messages:    api monitor params dotted key should not be expanded
FAIL

Failing Test

(identical to the reproduction test above)

Evidence

  • x-pack/heartbeat/cmd/root.go:79-80 only extracts params for kind == "browser":
    • if kind, _ := stream["type"].(string); !ok || kind != "browser" { continue }
  • x-pack/heartbeat/monitors/api/api.go:44 reuses browser source job:
    • sj, err := browser.NewSourceJob(cfg)
  • x-pack/heartbeat/cmd/root_test.go:100-156 contains dotted-key preservation tests only for browser streams, not api streams.

What is this? | From workflow: Bug Hunter

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

  • expires on Aug 6, 2026, 11:51 AM UTC
Dominant language
Go
Stars
12.7k
Forks
5k
Avg merge
2d 1h
Merged PRs (30d)
305

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from elastic/beats

All issues in elastic/beats

Similar issues

More Go issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.