Commit 9487550a authored by Vasilii Iakliushin's avatar Vasilii Iakliushin

Frontend client for increment_counter API

Contibutes to issue:
https://gitlab.com/gitlab-org/gitlab/-/issues/233994
Follow-up for:
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47309

Add a JS client to send requests to UsageData increment_counter API
parent 5326b11d
......@@ -69,6 +69,7 @@ const Api = {
issuePath: '/api/:version/projects/:id/issues/:issue_iid',
tagsPath: '/api/:version/projects/:id/repository/tags',
freezePeriodsPath: '/api/:version/projects/:id/freeze_periods',
usageDataIncrementCounterPath: '/api/:version/usage_data/increment_counter',
usageDataIncrementUniqueUsersPath: '/api/:version/usage_data/increment_unique_users',
featureFlagUserLists: '/api/:version/projects/:id/feature_flags_user_lists',
featureFlagUserList: '/api/:version/projects/:id/feature_flags_user_lists/:list_iid',
......@@ -751,6 +752,19 @@ const Api = {
return axios.post(url, freezePeriod);
},
trackRedisCounterEvent(event) {
if (!gon.features?.usageDataApi) {
return null;
}
const url = Api.buildUrl(this.usageDataIncrementCounterPath);
const headers = {
'Content-Type': 'application/json',
};
return axios.post(url, { event }, { headers });
},
trackRedisHllUserEvent(event) {
if (!gon.features?.usageDataApi) {
return null;
......
---
title: Frontend client for increment_counter API
merge_request: 47622
author:
type: added
......@@ -265,6 +265,45 @@ Examples of implementation:
- Using Redis methods [`INCR`](https://redis.io/commands/incr), [`GET`](https://redis.io/commands/get), and [`Gitlab::UsageDataCounters::WikiPageCounter`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters/wiki_page_counter.rb)
- Using Redis methods [`HINCRBY`](https://redis.io/commands/hincrby), [`HGETALL`](https://redis.io/commands/hgetall), and [`Gitlab::UsageCounters::PodLogs`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_counters/pod_logs.rb)
##### UsageData API Tracking
<!-- There's nearly identical content in `##### Adding new events`. If you fix errors here, you may need to fix the same errors in the other location. -->
1. Track event using `UsageData` API
Increment event count using ordinary Redis counter, for given event name.
Tracking events using the `UsageData` API requires the `usage_data_api` feature flag to be enabled, which is enabled by default.
API requests are protected by checking for a valid CSRF token.
In order to be able to increment the values the related feature `usage_data_<event_name>` should be enabled.
```plaintext
POST /usage_data/increment_counter
```
| Attribute | Type | Required | Description |
| :-------- | :--- | :------- | :---------- |
| `event` | string | yes | The event name it should be tracked |
Response
- `200` if event was tracked
- `400 Bad request` if event parameter is missing
- `401 Unauthorized` if user is not authenticated
- `403 Forbidden` for invalid CSRF token provided
1. Track events using JavaScript/Vue API helper which calls the API above
Note that `usage_data_api` and `usage_data_#{event_name}` should be enabled in order to be able to track events
```javascript
import api from '~/api';
api.trackRedisCounterEvent('my_already_defined_event_name'),
```
#### Redis HLL Counters
With `Gitlab::UsageDataCounters::HLLRedisCounter` we have available data structures used to count unique values.
......@@ -387,6 +426,8 @@ Implemented using Redis methods [PFADD](https://redis.io/commands/pfadd) and [PF
track_usage_event(:incident_management_incident_created, current_user.id)
```
<!-- There's nearly identical content in `##### UsageData API Tracking`. If you find / fix errors here, you may need to fix errors in that section too. -->
1. Track event using `UsageData` API
Increment unique users count using Redis HLL, for given event name.
......
......@@ -1254,6 +1254,46 @@ describe('Api', () => {
});
});
describe('trackRedisCounterEvent', () => {
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/usage_data/increment_counter`;
const event = 'dummy_event';
const postData = { event };
const headers = {
'Content-Type': 'application/json',
};
describe('when usage data increment counter is called with feature flag disabled', () => {
beforeEach(() => {
gon.features = { ...gon.features, usageDataApi: false };
});
it('returns null', () => {
jest.spyOn(axios, 'post');
mock.onPost(expectedUrl).replyOnce(httpStatus.OK, true);
expect(axios.post).toHaveBeenCalledTimes(0);
expect(Api.trackRedisCounterEvent(event)).toEqual(null);
});
});
describe('when usage data increment counter is called', () => {
beforeEach(() => {
gon.features = { ...gon.features, usageDataApi: true };
});
it('resolves the Promise', () => {
jest.spyOn(axios, 'post');
mock.onPost(expectedUrl, { event }).replyOnce(httpStatus.OK, true);
return Api.trackRedisCounterEvent(event).then(({ data }) => {
expect(data).toEqual(true);
expect(axios.post).toHaveBeenCalledWith(expectedUrl, postData, { headers });
});
});
});
});
describe('trackRedisHllUserEvent', () => {
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/usage_data/increment_unique_users`;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment