diff --git a/doc/api/usage_data.md b/doc/api/usage_data.md index a61bf9b9827506d8c4167dd1f21c6cc400c6e992..29854e58254eb83a226d09a2084e2ce670c39ceb 100644 --- a/doc/api/usage_data.md +++ b/doc/api/usage_data.md @@ -192,3 +192,56 @@ Sample response: }, ... ``` + +## Events Tracking API + +Tracks internal events in GitLab. Requires a personal access token with the `api` or `ai_workflows` scope. + +To track events to Snowplow, set the `send_to_snowplow` parameter to `true`. + +Example request: + +```shell +curl --header "PRIVATE-TOKEN: <your_access_token>" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "event": "mr_name_changed", + "send_to_snowplow": true, + "namespace_id": 1, + "project_id": 1, + "additional_properties": { + "lang": "eng" + } + }' \ + "https://gitlab.example.com/api/v4/usage_data/track_event" +``` + +If multiple events tracking is required, send an array of events to the `/track_events` endpoint: + +```shell +curl --header "PRIVATE-TOKEN: <your_access_token>" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '{ + "events": [ + { + "event": "mr_name_changed", + "namespace_id": 1, + "project_id": 1, + "additional_properties": { + "lang": "eng" + } + }, + { + "event": "mr_name_changed", + "namespace_id": 2, + "project_id": 2, + "additional_properties": { + "lang": "eng" + } + } + ] + }' \ + "https://gitlab.example.com/api/v4/usage_data/track_events" +``` diff --git a/doc/development/internal_analytics/internal_event_instrumentation/quick_start.md b/doc/development/internal_analytics/internal_event_instrumentation/quick_start.md index 680b71411c17237ab951f77e92d7383616e99811..7aaeb81ee5d1a74c4d4c20b094e72fd716dba45b 100644 --- a/doc/development/internal_analytics/internal_event_instrumentation/quick_start.md +++ b/doc/development/internal_analytics/internal_event_instrumentation/quick_start.md @@ -564,6 +564,11 @@ describe('DeleteApplication', () => { }); ``` +### Using Internal Events API + +You can also use our API to track events from other systems connected to a GitLab instance. +See the [Usage Data API documentation](../../../api/usage_data.md#events-tracking-api) for more information. + ### Internal Events on other systems Apart from the GitLab codebase, we are using Internal Events for the systems listed below. diff --git a/lib/api/usage_data.rb b/lib/api/usage_data.rb index 9a9ed959b7c3e56b2a5e5b385bd2b3aa52ddcb7b..e6e74cdd689c10aff2708ba2226924af38aaa541 100644 --- a/lib/api/usage_data.rb +++ b/lib/api/usage_data.rb @@ -20,6 +20,8 @@ class UsageData < ::API::Base documentation: { example: 1234 } optional :additional_properties, type: Hash, desc: 'Additional properties to be tracked', documentation: { example: { label: 'login_button', value: 1 } } + optional :send_to_snowplow, type: Boolean, desc: 'Send the tracked event to Snowplow', + documentation: { example: true, default: false } end def process_event(params) @@ -27,12 +29,13 @@ def process_event(params) namespace_id = params[:namespace_id] project_id = params[:project_id] additional_properties = params.fetch(:additional_properties, {}).symbolize_keys + send_snowplow_event = !!params[:send_to_snowplow] Gitlab::Tracking::AiTracking.track_event(event_name, additional_properties.merge(user: current_user)) track_event( event_name, - send_snowplow_event: false, + send_snowplow_event: send_snowplow_event, user: current_user, namespace_id: namespace_id, project_id: project_id, diff --git a/spec/requests/api/usage_data_spec.rb b/spec/requests/api/usage_data_spec.rb index d7f2ad3b4c76c590b50cb5a03143597afb4ca552..483137ca1dcdea9a1dec589a19892b478edaac97 100644 --- a/spec/requests/api/usage_data_spec.rb +++ b/spec/requests/api/usage_data_spec.rb @@ -280,6 +280,75 @@ end end end + + describe 'send_to_snowplow param' do + it 'does not send the event to snowplow when send_to_snowplow is false' do + expect(Gitlab::InternalEvents).to receive(:track_event) + .with( + known_event, + send_snowplow_event: false, + user: user, + namespace: namespace, + project: project, + additional_properties: additional_properties + ) + + post api(endpoint, user), params: { + event: known_event, + namespace_id: namespace.id, + project_id: project.id, + additional_properties: additional_properties, + send_to_snowplow: false + } + + expect(response).to have_gitlab_http_status(:ok) + end + + it 'sends event to Snowplow when send_to_snowplow is true' do + expect(Gitlab::InternalEvents).to receive(:track_event) + .with( + known_event, + send_snowplow_event: true, + user: user, + namespace: namespace, + project: project, + additional_properties: additional_properties + ) + + post api(endpoint, user), params: + { + event: known_event, + namespace_id: namespace.id, + project_id: project.id, + additional_properties: additional_properties, + send_to_snowplow: true + } + + expect(response).to have_gitlab_http_status(:ok) + end + + it 'does not send event to Snowplow by default' do + expect(Gitlab::InternalEvents).to receive(:track_event) + .with( + known_event, + send_snowplow_event: false, + user: user, + namespace: namespace, + project: project, + additional_properties: additional_properties + ) + + post api(endpoint, user), params: + { + event: known_event, + namespace_id: namespace.id, + project_id: project.id, + additional_properties: additional_properties + } + + expect(response).to have_gitlab_http_status(:ok) + end + end end end @@ -305,16 +374,12 @@ end context 'with the amount events greater than the limit' do - let(:params) do - { - events: Array.new(API::UsageData::MAXIMUM_TRACKED_EVENTS * 2) { { event: event } } - } - end + let(:params) { { events: Array.new(API::UsageData::MAXIMUM_TRACKED_EVENTS * 2) { { event: event } } } } it 'returns bad request' do expect(Gitlab::InternalEvents).not_to receive(:track_event) - post(api(endpoint, user), params: params) + post api(endpoint, user), params: params expect(response).to have_gitlab_http_status(:bad_request) end