Commit e5d0124c authored by Robert Hunt's avatar Robert Hunt

Added new audit events template to projects

- Added changelog
- Renamed group_member_token.vue to member_token.vue
- Updated member_token.vue to return the correct API endpoint based on
the config
- Updated audit_events_controller.rb to handle the temporary Author
entity type
- Updated audit_events_helper.rb with list of project filters
- Updated index.html.haml and index.js to use the audit events app
- Updated spec tests
- Updated QA tests
parent de5c202f
......@@ -13,7 +13,17 @@ export default {
return Api.user(id).then(res => res.data);
},
fetchSuggestions(term) {
return Api.groupMembers(this.config.groupId, { search: term }).then(res => res.data);
const { groupId, projectPath } = this.config;
if (groupId) {
return Api.groupMembers(groupId, { search: term }).then(res => res.data);
}
if (projectPath) {
return Api.projectUsers(projectPath, term);
}
return {};
},
getItemName({ name }) {
return name;
......
import { __, s__ } from '~/locale';
import UserToken from './components/tokens/user_token.vue';
import GroupMemberToken from './components/tokens/group_member_token.vue';
import MemberToken from './components/tokens/member_token.vue';
import ProjectToken from './components/tokens/project_token.vue';
import GroupToken from './components/tokens/group_token.vue';
......@@ -33,9 +33,9 @@ export const AUDIT_FILTER_CONFIGS = [
...DEFAULT_TOKEN_OPTIONS,
icon: 'user',
title: s__('AuditLogs|Member Events'),
type: 'group_member',
type: 'member',
entityType: ENTITY_TYPES.AUTHOR,
token: GroupMemberToken,
token: MemberToken,
},
{
...DEFAULT_TOKEN_OPTIONS,
......
import initAuditEvents from 'ee/audit_events/init_audit_events';
initAuditEvents('#js-project-audit-events-app');
......@@ -13,13 +13,17 @@ class Projects::AuditEventsController < Projects::ApplicationController
def index
level = Gitlab::Audit::Levels::Project.new(project: project)
# This is an interim change until we have proper API support within Audit Events
params = transform_author_entity_type(audit_logs_params)
events = AuditLogFinder
.new(level: level, params: audit_logs_params)
.new(level: level, params: params)
.execute
.page(params[:page])
.without_count
@events = Gitlab::Audit::Events::Preloader.preload!(events)
@table_events = AuditEventSerializer.new.represent(@events)
end
private
......@@ -27,4 +31,12 @@ class Projects::AuditEventsController < Projects::ApplicationController
def check_audit_events_available!
render_404 unless @project.feature_available?(:audit_events) || LicenseHelper.show_promotions?(current_user)
end
def transform_author_entity_type(params)
return params unless params[:entity_type] == 'Author'
params[:author_id] = params[:entity_id]
params.except(:entity_type, :entity_id)
end
end
......@@ -5,7 +5,7 @@ module AuditEventsHelper
user: :user,
group: :group,
project: :project,
group_member: :group_member
member: :member
}.freeze
def admin_audit_event_tokens
......@@ -13,7 +13,11 @@ module AuditEventsHelper
end
def group_audit_event_tokens(group_id)
[{ type: FILTER_TOKEN_TYPES[:group_member], group_id: group_id }]
[{ type: FILTER_TOKEN_TYPES[:member], group_id: group_id }]
end
def project_audit_event_tokens(project_path)
[{ type: FILTER_TOKEN_TYPES[:member], project_path: project_path }]
end
def human_text(details)
......
......@@ -7,7 +7,12 @@
%p.light
= _('Events in %{project_path}') % { project_path: @project.full_path }
= render 'shared/audit_events/event_filter', path: project_audit_events_path(@project)
= render 'shared/audit_events/event_table', events: @events
#js-project-audit-events-app{ data: { form_path: project_audit_events_path(@project),
events: @table_events.to_json,
is_last_page: @events.last_page?.to_json,
filter_qa_selector: 'project_audit_log_filter',
table_qa_selector: 'project_audit_log_table',
filter_token_options: project_audit_event_tokens(@project.full_path).to_json } }
- elsif show_promotions?
= render 'shared/promotions/promote_audit_events'
---
title: Update project audit events to use the new searchable table
merge_request: 34176
author:
type: changed
......@@ -9,9 +9,11 @@ RSpec.describe Projects::AuditEventsController do
describe 'GET #index' do
let(:sort) { nil }
let(:entity_type) { nil }
let(:entity_id) { nil }
let(:request) do
get :index, params: { project_id: project.to_param, namespace_id: project.namespace.to_param, sort: sort }
get :index, params: { project_id: project.to_param, namespace_id: project.namespace.to_param, sort: sort, entity_type: entity_type, entity_id: entity_id }
end
context 'authorized' do
......@@ -22,7 +24,7 @@ RSpec.describe Projects::AuditEventsController do
context 'when audit_events feature is available' do
let(:level) { Gitlab::Audit::Levels::Project.new(project: project) }
let(:audit_logs_params) { ActionController::Parameters.new(sort: '').permit! }
let(:audit_logs_params) { ActionController::Parameters.new(sort: '', entity_type: '', entity_id: '').permit! }
before do
stub_licensed_features(audit_events: true)
......@@ -31,6 +33,16 @@ RSpec.describe Projects::AuditEventsController do
allow(AuditLogFinder).to receive(:new).and_call_original
end
shared_examples 'AuditLogFinder params' do
it 'has the correct params' do
request
expect(AuditLogFinder).to have_received(:new).with(
level: level, params: audit_logs_params
)
end
end
it 'renders index with 200 status code' do
request
......@@ -38,12 +50,22 @@ RSpec.describe Projects::AuditEventsController do
expect(response).to render_template(:index)
end
it 'invokes AuditLogFinder with correct arguments' do
request
context 'invokes AuditLogFinder with correct arguments' do
it_behaves_like 'AuditLogFinder params'
end
context 'author' do
context 'when no author entity type is specified' do
it_behaves_like 'AuditLogFinder params'
end
expect(AuditLogFinder).to have_received(:new).with(
level: level, params: audit_logs_params
)
context 'when the author entity type is specified' do
let(:entity_type) { 'Author' }
let(:entity_id) { 1 }
let(:audit_logs_params) { ActionController::Parameters.new(sort: '', author_id: '1').permit! }
it_behaves_like 'AuditLogFinder params'
end
end
context 'ordering' do
......@@ -87,6 +109,14 @@ RSpec.describe Projects::AuditEventsController do
end
end
context 'pagination' do
it 'paginates audit events, without casting a count query' do
request
expect(assigns(:events)).to be_kind_of(Kaminari::PaginatableWithoutCount)
end
end
context 'when audit_events feature is not available' do
before do
stub_licensed_features(audit_events: false)
......
......@@ -106,32 +106,10 @@ RSpec.describe 'Admin::AuditLogs', :js do
let_it_be(:audit_event_1) { create(:user_audit_event, created_at: 5.days.ago) }
let_it_be(:audit_event_2) { create(:user_audit_event, created_at: 3.days.ago) }
let_it_be(:audit_event_3) { create(:user_audit_event, created_at: 1.day.ago) }
let!(:events_path) { :admin_audit_logs_path }
let!(:entity) { nil }
it 'shows only 2 days old events' do
visit admin_audit_logs_path(created_after: 4.days.ago.to_date, created_before: 2.days.ago.to_date)
find('[data-testid="audit-events-table"] td', match: :first)
expect(page).not_to have_content(audit_event_1.present.date)
expect(page).to have_content(audit_event_2.present.date)
expect(page).not_to have_content(audit_event_3.present.date)
end
it 'shows only yesterday events' do
visit admin_audit_logs_path(created_after: 2.days.ago.to_date)
find('[data-testid="audit-events-table"] td', match: :first)
expect(page).not_to have_content(audit_event_1.present.date)
expect(page).not_to have_content(audit_event_2.present.date)
expect(page).to have_content(audit_event_3.present.date)
end
it 'shows a message if provided date is invalid' do
visit admin_audit_logs_path(created_after: '12-345-6789')
expect(page).to have_content('Invalid date format. Please use UTC format as YYYY-MM-DD')
end
it_behaves_like 'audit events date filter'
end
describe 'impersonated events' do
......
......@@ -68,31 +68,9 @@ RSpec.describe 'Groups > Audit Events', :js do
let!(:audit_event_1) { create(:group_audit_event, entity_type: 'Group', entity_id: group.id, created_at: 5.days.ago) }
let!(:audit_event_2) { create(:group_audit_event, entity_type: 'Group', entity_id: group.id, created_at: 3.days.ago) }
let!(:audit_event_3) { create(:group_audit_event, entity_type: 'Group', entity_id: group.id, created_at: 1.day.ago) }
let!(:events_path) { :group_audit_events_path }
let!(:entity) { group }
it 'shows only 2 days old events' do
visit group_audit_events_path(group, created_after: 4.days.ago.to_date, created_before: 2.days.ago.to_date)
find('.audit-log-table td', match: :first)
expect(page).not_to have_content(audit_event_1.present.date)
expect(page).to have_content(audit_event_2.present.date)
expect(page).not_to have_content(audit_event_3.present.date)
end
it 'shows only yesterday events' do
visit group_audit_events_path(group, created_after: 2.days.ago.to_date)
find('.audit-log-table td', match: :first)
expect(page).not_to have_content(audit_event_1.present.date)
expect(page).not_to have_content(audit_event_2.present.date)
expect(page).to have_content(audit_event_3.present.date)
end
it 'shows a message if provided date is invalid' do
visit group_audit_events_path(group, created_after: '12-345-6789')
expect(page).to have_content('Invalid date format. Please use UTC format as YYYY-MM-DD')
end
it_behaves_like 'audit events date filter'
end
end
......@@ -86,7 +86,7 @@ RSpec.describe 'Projects > Audit Events', :js do
visit project_audit_events_path(project)
expect(page).to have_content('Add deploy key')
expect(page).to have_content('Added deploy key')
visit project_deploy_keys_path(project)
......@@ -97,7 +97,7 @@ RSpec.describe 'Projects > Audit Events', :js do
visit project_audit_events_path(project)
wait_for('Audit event background creation job is done', polling_interval: 0.5, reload: true) do
page.has_content?('Remove deploy key', wait: 0)
page.has_content?('Removed deploy key', wait: 0)
end
end
end
......@@ -121,8 +121,8 @@ RSpec.describe 'Projects > Audit Events', :js do
click_link 'Audit Events'
page.within('#audits') do
expect(page).to have_content 'Change access level from developer to maintainer'
page.within('.audit-log-table') do
expect(page).to have_content 'Changed access level from Developer to Maintainer'
expect(page).to have_content(project.owner.name)
expect(page).to have_content('Pete')
end
......@@ -154,28 +154,22 @@ RSpec.describe 'Projects > Audit Events', :js do
wait_for_all_requests
page.within('#audits') do
page.within('.audit-log-table') do
expect(page).to have_content(project.owner.name)
expect(page).to have_content('Change prevent merge request approval from authors')
expect(page).to have_content('Change prevent merge request approval from reviewers')
expect(page).to have_content('Changed prevent merge request approval from authors')
expect(page).to have_content('Changed prevent merge request approval from reviewers')
expect(page).to have_content(project.name)
end
end
end
it_behaves_like 'audit event contains custom message' do
let(:audit_events_url) { project_audit_events_path(project) }
end
describe 'filter by date', js: false do
describe 'filter by date' do
let!(:audit_event_1) { create(:project_audit_event, entity_type: 'Project', entity_id: project.id, created_at: 5.days.ago) }
let!(:audit_event_2) { create(:project_audit_event, entity_type: 'Project', entity_id: project.id, created_at: 3.days.ago) }
let!(:audit_event_3) { create(:project_audit_event, entity_type: 'Project', entity_id: project.id, created_at: 1.day.ago) }
let!(:events_path) { :project_audit_events_path }
let!(:entity) { project }
before do
visit project_audit_events_path(project)
end
it_behaves_like 'audit events filter'
it_behaves_like 'audit events date filter'
end
end
......@@ -96,7 +96,7 @@ RSpec.describe 'User creates feature flag', :js do
it 'records audit event' do
visit(project_audit_events_path(project))
expect(page).to have_text("Created feature flag ci_live_trace with description \"For live trace\".")
expect(page).to have_text("Created feature flag <strong>ci_live_trace</strong> with description <strong>\"For live trace\"</strong>.")
end
end
......
......@@ -33,6 +33,6 @@ RSpec.describe 'User deletes feature flag', :js do
it 'records audit event' do
visit(project_audit_events_path(project))
expect(page).to have_text("Deleted feature flag ci_live_trace.")
expect(page).to have_text("Deleted feature flag <strong>ci_live_trace</strong>.")
end
end
......@@ -80,7 +80,7 @@ RSpec.describe 'User sees feature flag list', :js do
visit(project_audit_events_path(project))
expect(page).to(
have_text('Updated feature flag ci_live_trace. Updated active from "false" to "true".')
have_text('Updated feature flag <strong>ci_live_trace</strong>. Updated active from <strong>"false"</strong> to <strong>"true"</strong>.')
)
end
end
......
......@@ -116,7 +116,7 @@ RSpec.describe 'User updates feature flag', :js do
visit(project_audit_events_path(project))
expect(page).to(
have_text("Updated feature flag ci_live_trace. Updated rule review/* active state from true to false.")
have_text("Updated feature flag <strong>ci_live_trace</strong>. Updated rule <strong>review/*</strong> active state from <strong>true</strong> to <strong>false</strong>.")
)
end
end
......@@ -147,7 +147,7 @@ RSpec.describe 'User updates feature flag', :js do
visit(project_audit_events_path(project))
expect(page).to(
have_text("Updated feature flag ci_live_trace")
have_text("Updated feature flag <strong>ci_live_trace</strong>")
)
end
end
......@@ -175,7 +175,7 @@ RSpec.describe 'User updates feature flag', :js do
visit(project_audit_events_path(project))
expect(page).to(
have_text("Updated feature flag ci_live_trace")
have_text("Updated feature flag <strong>ci_live_trace</strong>")
)
end
end
......
......@@ -26,11 +26,11 @@ describe('AuditEventsFilter', () => {
});
describe.each`
type | title
${'project'} | ${'Project Events'}
${'group'} | ${'Group Events'}
${'user'} | ${'User Events'}
${'group_member'} | ${'Member Events'}
type | title
${'project'} | ${'Project Events'}
${'group'} | ${'Group Events'}
${'user'} | ${'User Events'}
${'member'} | ${'Member Events'}
`('for the list of available tokens', ({ type, title }) => {
it(`creates a unique token for ${type}`, () => {
initComponent();
......
......@@ -47,7 +47,7 @@ describe('Audit Event Utils', () => {
describe('getEntityTypeFromType', () => {
it('returns the correct entity type when given a valid type', () => {
expect(getEntityTypeFromType('group_member')).toEqual('Author');
expect(getEntityTypeFromType('member')).toEqual('Author');
});
it('returns `undefined` when given an invalid type', () => {
......
......@@ -16,10 +16,20 @@ RSpec.describe AuditEventsHelper do
let(:group_id) { 1 }
it 'returns the available tokens' do
available_tokens = [{ type: AuditEventsHelper::FILTER_TOKEN_TYPES[:group_member], group_id: group_id }]
available_tokens = [{ type: AuditEventsHelper::FILTER_TOKEN_TYPES[:member], group_id: group_id }]
expect(group_audit_event_tokens(group_id)).to eq(available_tokens)
end
end
describe '#project_audit_event_tokens' do
let(:project_path) { '/abc' }
it 'returns the available tokens' do
available_tokens = [{ type: AuditEventsHelper::FILTER_TOKEN_TYPES[:member], project_path: project_path }]
expect(project_audit_event_tokens(project_path)).to eq(available_tokens)
end
end
describe '#human_text' do
let(:target_type) { 'User' }
let(:details) do
......
# frozen_string_literal: true
RSpec.shared_examples 'audit event contains custom message' do
let(:custom_message) { "Message_with_spaces" }
let(:details) do
{
custom_message: custom_message,
author_name: 'John Doe',
target_id: 1,
target_type: 'User',
target_details: 'Michael'
}
end
let!(:security_event) do
::AuditEventService.new(user, project, details).security_event
end
before do
visit audit_events_url
end
it 'user sees this message' do
expect(page).to have_content('Message_with_spaces')
end
context 'when it contains tags' do
let(:custom_message) { 'Message <strong>with</strong> <i>deleted</i> tags' }
it 'allows only <strong> tag' do
message_row = find('.js-audit-action', text: 'Message with deleted tags')
expect(message_row).to have_selector('strong')
expect(message_row).to have_no_selector('i')
end
end
end
# frozen_string_literal: true
RSpec.shared_examples_for 'audit events filter' do
RSpec.shared_examples_for 'audit events date filter' do
it 'shows only 2 days old events' do
page.within '.content' do
fill_in 'From', with: 4.days.ago
fill_in 'To', with: 2.days.ago
click_button 'Search'
end
visit method(events_path).call(entity, created_after: 4.days.ago.to_date, created_before: 2.days.ago.to_date)
find('.audit-log-table td', match: :first)
expect(page).to have_content(audit_event_2.present.date)
expect(page).not_to have_content(audit_event_1.present.date)
expect(page).to have_content(audit_event_2.present.date)
expect(page).not_to have_content(audit_event_3.present.date)
end
it 'shows only yesterday events' do
page.within '.content' do
fill_in 'From', with: 2.days.ago
click_button 'Search'
end
visit method(events_path).call(entity, created_after: 2.days.ago.to_date)
find('.audit-log-table td', match: :first)
expect(page).to have_content(audit_event_3.present.date)
expect(page).not_to have_content(audit_event_1.present.date)
expect(page).not_to have_content(audit_event_2.present.date)
expect(page).to have_content(audit_event_3.present.date)
end
it 'shows a message if provided date is invalid' do
page.within '.content' do
fill_in 'From', with: '12-345-6789'
click_button 'Search'
end
visit method(events_path).call(entity, created_after: '12-345-6789')
expect(page).to have_content('Invalid date format. Please use UTC format as YYYY-MM-DD')
end
......
......@@ -32,7 +32,7 @@ module QA
project.initialize_with_readme = true
end.visit!
end
it_behaves_like 'audit event', ["Add project"]
it_behaves_like 'audit event', ["Added project"]
end
context "Add user access as guest" do
......@@ -45,7 +45,7 @@ module QA
end
end
it_behaves_like 'audit event', ["Add user access as guest"]
it_behaves_like 'audit event', ["Added user access as Guest"]
end
context "Add deploy key" do
......@@ -61,7 +61,7 @@ module QA
end
end
it_behaves_like 'audit event', ["Add deploy key"]
it_behaves_like 'audit event', ["Added deploy key"]
end
context "Change visibility" do
......@@ -77,7 +77,7 @@ module QA
end
end
it_behaves_like 'audit event', ["Change visibility from public to private"]
it_behaves_like 'audit event', ["Changed visibility from Public to Private"]
end
context "Export file download", quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/217949', type: :investigating } 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