feat(telemetry): send templated API endpoint and HTTP method

Description

glab api is the single largest command in the gitlab_cli_command_used stream and the event records nothing about what was called. Its endpoint is an argument rather than a subcommand, so parseCommand returns an empty subcommand and the event says only "someone called the API".

This adds api_endpoint and http_method:

projects/278964/merge_requests            -> /projects/:id/merge_requests    GET
users/phikai/projects                     -> /users/:id/projects             GET
projects/:fullpath/merge_requests         -> /projects/:id/merge_requests    GET
graphql                                   -> /graphql                        GET
/user --field a=1                         -> /user                           POST
projects/1/totally_made_up                -> unmatched                       GET

Endpoint

Raw paths are never sent: they carry project IDs, usernames, branch names and sometimes tokens in query strings. Templating them heuristically does not work either, because an identifier is not distinguishable from a route noun by shape — users/phikai/projects looks exactly like users/keys/1.

So the path is matched against client-go's route registry (gitlab.MatchRoute, added in v2.63.0) and reported as the matching template. Anything unmatched is reported as the literal unmatched rather than partially redacted, so every value emitted is either a route the client knows or that one sentinel.

unmatched is a real category, not an error: a large share of it means client-go's coverage is behind.

Three things the registry does not cover are handled here — query and fragment stripping, GraphQL (client-go reaches it through its own client, so it has no route), and the sentinel. glab's own :fullpath style placeholders need no special handling; they are not literal segments in the registry, so they match its parameter branch as-is.

Method

http_method comes from api.EffectiveMethod, which applies the same resolveMethod rule as options.run, so a --field without an explicit --method is reported as POST rather than the flag default, and the two cannot drift.

Property assembly lives in cmd/glab/telemetry.go with the rest of the hook; the api package exports only that one fact about itself.

Monolith dependency

Both properties need declaring in config/events/gitlab_cli_command_used.ymlgitlab!253241 (merged). EventsRouter#public_additional_properties slices additional_properties down to the declared keys before the split into reserved columns and extra, so undeclared properties are dropped. Merging ahead of that is harmless; they are sliced off until it deploys.

Dashboard follow-up: gitlab-data/product-analytics#3622.

Resolves #8527 Depends on gitlab-org/api/client-go!3023 (merged) (released in v2.63.0, already on main)

How has this been tested?

Templating covers the username, branch-name and file-path cases a heuristic would leak, query and fragment stripping, :placeholder values, GraphQL, and unknown paths falling back to unmatched. One test asserts every non-unmatched value is a route client-go actually knows, so no input can survive into the payload.

Method resolution covers every body-bearing flag and explicit --method winning over an implied POST, driven through the real api command so a renamed flag fails the test rather than silently misreporting.

Verified end-to-end against a local instance with a deliberately hostile path (?private_token=glpat-SECRET): payloads contained the templates above and none of the input data.

Edited by Kai Armstrong

Merge request reports

Loading
Loading