Commit 81a1e155 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch 'al-214347-web-ide-pipelines-usage' into 'master'

Add Web IDE pipelines usage counts

See merge request gitlab-org/gitlab!31658
parents 45b695d5 c81232ea
...@@ -10,6 +10,8 @@ import Tab from '../../../vue_shared/components/tabs/tab.vue'; ...@@ -10,6 +10,8 @@ import Tab from '../../../vue_shared/components/tabs/tab.vue';
import EmptyState from '../../../pipelines/components/empty_state.vue'; import EmptyState from '../../../pipelines/components/empty_state.vue';
import JobsList from '../jobs/list.vue'; import JobsList from '../jobs/list.vue';
import IDEServices from '~/ide/services';
export default { export default {
components: { components: {
Icon, Icon,
...@@ -47,6 +49,7 @@ export default { ...@@ -47,6 +49,7 @@ export default {
}, },
created() { created() {
this.fetchLatestPipeline(); this.fetchLatestPipeline();
IDEServices.pingUsage(this.currentProject.path_with_namespace);
}, },
methods: { methods: {
...mapActions('pipelines', ['fetchLatestPipeline']), ...mapActions('pipelines', ['fetchLatestPipeline']),
......
...@@ -96,4 +96,8 @@ export default { ...@@ -96,4 +96,8 @@ export default {
const commitSha = getters.lastCommit.id; const commitSha = getters.lastCommit.id;
return Api.commitPipelines(getters.currentProject.path_with_namespace, commitSha); return Api.commitPipelines(getters.currentProject.path_with_namespace, commitSha);
}, },
pingUsage(projectPath) {
const url = `${gon.relative_url_root}/${projectPath}/usage_ping/web_ide_pipelines_count`;
return axios.post(url);
},
}; };
...@@ -10,4 +10,10 @@ class Projects::UsagePingController < Projects::ApplicationController ...@@ -10,4 +10,10 @@ class Projects::UsagePingController < Projects::ApplicationController
head(200) head(200)
end end
def web_ide_pipelines_count
Gitlab::UsageDataCounters::WebIdeCounter.increment_pipelines_count
head(200)
end
end end
---
title: Add Web IDE pipelines usage counter
merge_request: 31658
author:
type: added
...@@ -468,6 +468,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -468,6 +468,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
scope :usage_ping, controller: :usage_ping do scope :usage_ping, controller: :usage_ping do
post :web_ide_clientside_preview post :web_ide_clientside_preview
post :web_ide_pipelines_count
end end
# Deprecated unscoped routing. # Deprecated unscoped routing.
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
module UsageDataCounters module UsageDataCounters
class WebIdeCounter class WebIdeCounter
extend RedisCounter extend RedisCounter
KNOWN_EVENTS = %i[commits views merge_requests previews terminals].freeze KNOWN_EVENTS = %i[commits views merge_requests previews terminals pipelines].freeze
PREFIX = 'web_ide' PREFIX = 'web_ide'
class << self class << self
...@@ -24,6 +24,10 @@ module Gitlab ...@@ -24,6 +24,10 @@ module Gitlab
increment(redis_key('terminals')) increment(redis_key('terminals'))
end end
def increment_pipelines_count
increment(redis_key('pipelines'))
end
def increment_previews_count def increment_previews_count
return unless Gitlab::CurrentSettings.web_ide_clientside_preview_enabled? return unless Gitlab::CurrentSettings.web_ide_clientside_preview_enabled?
......
...@@ -6,45 +6,52 @@ describe Projects::UsagePingController do ...@@ -6,45 +6,52 @@ describe Projects::UsagePingController do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
describe 'POST #web_ide_clientside_preview' do before do
subject { post :web_ide_clientside_preview, params: { namespace_id: project.namespace, project_id: project } } sign_in(user) if user
end
before do shared_examples 'counter is not increased' do
sign_in(user) if user context 'when the user is not authenticated' do
end let(:user) { nil }
context 'when web ide clientside preview is enabled' do it 'returns 302' do
before do subject
stub_application_setting(web_ide_clientside_preview_enabled: true)
end
context 'when the user is not authenticated' do expect(response).to have_gitlab_http_status(:found)
let(:user) { nil } end
end
it 'returns 302' do context 'when the user does not have access to the project' do
subject it 'returns 404' do
subject
expect(response).to have_gitlab_http_status(:found) expect(response).to have_gitlab_http_status(:not_found)
end
end end
end
end
context 'when the user does not have access to the project' do shared_examples 'counter is increased' do |counter|
it 'returns 404' do context 'when the authenticated user has access to the project' do
subject let(:user) { project.owner }
expect(response).to have_gitlab_http_status(:not_found) it 'increments the usage counter' do
end expect do
subject
end.to change { Gitlab::UsageDataCounters::WebIdeCounter.total_count(counter) }.by(1)
end end
end
end
context 'when the user has access to the project' do describe 'POST #web_ide_clientside_preview' do
let(:user) { project.owner } subject { post :web_ide_clientside_preview, params: { namespace_id: project.namespace, project_id: project } }
it 'increments the counter' do context 'when web ide clientside preview is enabled' do
expect do before do
subject stub_application_setting(web_ide_clientside_preview_enabled: true)
end.to change { Gitlab::UsageDataCounters::WebIdeCounter.total_count('WEB_IDE_PREVIEWS_COUNT') }.by(1)
end
end end
it_behaves_like 'counter is not increased'
it_behaves_like 'counter is increased', 'WEB_IDE_PREVIEWS_COUNT'
end end
context 'when web ide clientside preview is not enabled' do context 'when web ide clientside preview is not enabled' do
...@@ -61,4 +68,11 @@ describe Projects::UsagePingController do ...@@ -61,4 +68,11 @@ describe Projects::UsagePingController do
end end
end end
end end
describe 'POST #web_ide_pipelines_count' do
subject { post :web_ide_pipelines_count, params: { namespace_id: project.namespace, project_id: project } }
it_behaves_like 'counter is not increased'
it_behaves_like 'counter is increased', 'WEB_IDE_PIPELINES_COUNT'
end
end end
...@@ -7,10 +7,15 @@ import JobsList from '~/ide/components/jobs/list.vue'; ...@@ -7,10 +7,15 @@ import JobsList from '~/ide/components/jobs/list.vue';
import Tab from '~/vue_shared/components/tabs/tab.vue'; import Tab from '~/vue_shared/components/tabs/tab.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue';
import { pipelines } from '../../../../javascripts/ide/mock_data'; import { pipelines } from '../../../../javascripts/ide/mock_data';
import IDEServices from '~/ide/services';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
jest.mock('~/ide/services', () => ({
pingUsage: jest.fn(),
}));
describe('IDE pipelines list', () => { describe('IDE pipelines list', () => {
let wrapper; let wrapper;
...@@ -25,14 +30,18 @@ describe('IDE pipelines list', () => { ...@@ -25,14 +30,18 @@ describe('IDE pipelines list', () => {
}; };
const fetchLatestPipelineMock = jest.fn(); const fetchLatestPipelineMock = jest.fn();
const pingUsageMock = jest.fn();
const failedStagesGetterMock = jest.fn().mockReturnValue([]); const failedStagesGetterMock = jest.fn().mockReturnValue([]);
const fakeProjectPath = 'alpha/beta';
const createComponent = (state = {}) => { const createComponent = (state = {}) => {
const { pipelines: pipelinesState, ...restOfState } = state; const { pipelines: pipelinesState, ...restOfState } = state;
const { defaultPipelines, ...defaultRestOfState } = defaultState; const { defaultPipelines, ...defaultRestOfState } = defaultState;
const fakeStore = new Vuex.Store({ const fakeStore = new Vuex.Store({
getters: { currentProject: () => ({ web_url: 'some/url ' }) }, getters: {
currentProject: () => ({ web_url: 'some/url ', path_with_namespace: fakeProjectPath }),
},
state: { state: {
...defaultRestOfState, ...defaultRestOfState,
...restOfState, ...restOfState,
...@@ -46,6 +55,7 @@ describe('IDE pipelines list', () => { ...@@ -46,6 +55,7 @@ describe('IDE pipelines list', () => {
}, },
actions: { actions: {
fetchLatestPipeline: fetchLatestPipelineMock, fetchLatestPipeline: fetchLatestPipelineMock,
pingUsage: pingUsageMock,
}, },
getters: { getters: {
jobsCount: () => 1, jobsCount: () => 1,
...@@ -77,6 +87,11 @@ describe('IDE pipelines list', () => { ...@@ -77,6 +87,11 @@ describe('IDE pipelines list', () => {
expect(fetchLatestPipelineMock).toHaveBeenCalled(); expect(fetchLatestPipelineMock).toHaveBeenCalled();
}); });
it('pings pipeline usage', () => {
createComponent();
expect(IDEServices.pingUsage).toHaveBeenCalledWith(fakeProjectPath);
});
describe('when loading', () => { describe('when loading', () => {
let defaultPipelinesLoadingState; let defaultPipelinesLoadingState;
beforeAll(() => { beforeAll(() => {
......
...@@ -254,4 +254,34 @@ describe('IDE services', () => { ...@@ -254,4 +254,34 @@ describe('IDE services', () => {
}); });
}); });
}); });
describe('pingUsage', () => {
let mock;
let relativeUrlRoot;
const TEST_RELATIVE_URL_ROOT = 'blah-blah';
beforeEach(() => {
jest.spyOn(axios, 'post');
relativeUrlRoot = gon.relative_url_root;
gon.relative_url_root = TEST_RELATIVE_URL_ROOT;
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
gon.relative_url_root = relativeUrlRoot;
});
it('posts to usage endpoint', () => {
const TEST_PROJECT_PATH = 'foo/bar';
const axiosURL = `${TEST_RELATIVE_URL_ROOT}/${TEST_PROJECT_PATH}/usage_ping/web_ide_pipelines_count`;
mock.onPost(axiosURL).reply(200);
return services.pingUsage(TEST_PROJECT_PATH).then(() => {
expect(axios.post).toHaveBeenCalledWith(axiosURL);
});
});
});
}); });
...@@ -30,6 +30,10 @@ describe Gitlab::UsageDataCounters::WebIdeCounter, :clean_gitlab_redis_shared_st ...@@ -30,6 +30,10 @@ describe Gitlab::UsageDataCounters::WebIdeCounter, :clean_gitlab_redis_shared_st
it_behaves_like 'counter examples', 'terminals' it_behaves_like 'counter examples', 'terminals'
end end
describe 'pipelines counter' do
it_behaves_like 'counter examples', 'pipelines'
end
describe 'previews counter' do describe 'previews counter' do
let(:setting_enabled) { true } let(:setting_enabled) { true }
...@@ -61,6 +65,7 @@ describe Gitlab::UsageDataCounters::WebIdeCounter, :clean_gitlab_redis_shared_st ...@@ -61,6 +65,7 @@ describe Gitlab::UsageDataCounters::WebIdeCounter, :clean_gitlab_redis_shared_st
views = 2 views = 2
previews = 4 previews = 4
terminals = 1 terminals = 1
pipelines = 2
before do before do
stub_application_setting(web_ide_clientside_preview_enabled: true) stub_application_setting(web_ide_clientside_preview_enabled: true)
...@@ -70,6 +75,7 @@ describe Gitlab::UsageDataCounters::WebIdeCounter, :clean_gitlab_redis_shared_st ...@@ -70,6 +75,7 @@ describe Gitlab::UsageDataCounters::WebIdeCounter, :clean_gitlab_redis_shared_st
views.times { described_class.increment_views_count } views.times { described_class.increment_views_count }
previews.times { described_class.increment_previews_count } previews.times { described_class.increment_previews_count }
terminals.times { described_class.increment_terminals_count } terminals.times { described_class.increment_terminals_count }
pipelines.times { described_class.increment_pipelines_count }
end end
it 'can report all totals' do it 'can report all totals' do
......
...@@ -818,6 +818,10 @@ describe 'project routing' do ...@@ -818,6 +818,10 @@ describe 'project routing' do
it 'routes to usage_ping#web_ide_clientside_preview' do it 'routes to usage_ping#web_ide_clientside_preview' do
expect(post('/gitlab/gitlabhq/usage_ping/web_ide_clientside_preview')).to route_to('projects/usage_ping#web_ide_clientside_preview', namespace_id: 'gitlab', project_id: 'gitlabhq') expect(post('/gitlab/gitlabhq/usage_ping/web_ide_clientside_preview')).to route_to('projects/usage_ping#web_ide_clientside_preview', namespace_id: 'gitlab', project_id: 'gitlabhq')
end end
it 'routes to usage_ping#web_ide_pipelines_count' do
expect(post('/gitlab/gitlabhq/usage_ping/web_ide_pipelines_count')).to route_to('projects/usage_ping#web_ide_pipelines_count', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
end end
describe Projects::StaticSiteEditorController, 'routing' do describe Projects::StaticSiteEditorController, 'routing' do
......
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