Commit 790b5903 authored by Miguel Rincon's avatar Miguel Rincon

Add runner read only admin view

This change adds a new read only admin view for runners.

The view is empty and behind a feature flag, no user-facing changes
done.
parent ef09594c
import { initRunnerDetail } from '~/runner/runner_details'; import { initAdminRunnerEdit } from '~/runner/admin_runner_edit';
initRunnerDetail(); initAdminRunnerEdit();
...@@ -9,7 +9,7 @@ import getRunnerQuery from '../graphql/get_runner.query.graphql'; ...@@ -9,7 +9,7 @@ import getRunnerQuery from '../graphql/get_runner.query.graphql';
import { captureException } from '../sentry_utils'; import { captureException } from '../sentry_utils';
export default { export default {
name: 'RunnerDetailsApp', name: 'AdminRunnerEditApp',
components: { components: {
RunnerHeader, RunnerHeader,
RunnerUpdateForm, RunnerUpdateForm,
......
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import RunnerDetailsApp from './runner_details_app.vue'; import AdminRunnerEditApp from './admin_runner_edit_app.vue';
Vue.use(VueApollo); Vue.use(VueApollo);
export const initRunnerDetail = (selector = '#js-runner-details') => { export const initAdminRunnerEdit = (selector = '#js-admin-runner-edit') => {
const el = document.querySelector(selector); const el = document.querySelector(selector);
if (!el) { if (!el) {
...@@ -22,7 +22,7 @@ export const initRunnerDetail = (selector = '#js-runner-details') => { ...@@ -22,7 +22,7 @@ export const initRunnerDetail = (selector = '#js-runner-details') => {
el, el,
apolloProvider, apolloProvider,
render(h) { render(h) {
return h(RunnerDetailsApp, { return h(AdminRunnerEditApp, {
props: { props: {
runnerId, runnerId,
}, },
......
...@@ -162,9 +162,9 @@ export default { ...@@ -162,9 +162,9 @@ export default {
See https://gitlab.com/gitlab-org/gitlab/-/issues/334802 See https://gitlab.com/gitlab-org/gitlab/-/issues/334802
--> -->
<gl-button <gl-button
v-if="canUpdate && runner.adminUrl" v-if="canUpdate && runner.editAdminUrl"
v-gl-tooltip.hover.viewport="$options.I18N_EDIT" v-gl-tooltip.hover.viewport="$options.I18N_EDIT"
:href="runner.adminUrl" :href="runner.editAdminUrl"
:aria-label="$options.I18N_EDIT" :aria-label="$options.I18N_EDIT"
icon="pencil" icon="pencil"
data-testid="edit-runner" data-testid="edit-runner"
......
...@@ -10,7 +10,7 @@ import { ...@@ -10,7 +10,7 @@ import {
import { import {
modelToUpdateMutationVariables, modelToUpdateMutationVariables,
runnerToModel, runnerToModel,
} from 'ee_else_ce/runner/runner_details/runner_update_form_utils'; } from 'ee_else_ce/runner/runner_update_form_utils';
import { createAlert, VARIANT_SUCCESS } from '~/flash'; import { createAlert, VARIANT_SUCCESS } from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
......
...@@ -26,6 +26,7 @@ query getRunners( ...@@ -26,6 +26,7 @@ query getRunners(
nodes { nodes {
...RunnerNode ...RunnerNode
adminUrl adminUrl
editAdminUrl
} }
pageInfo { pageInfo {
...PageInfo ...PageInfo
......
...@@ -15,7 +15,7 @@ class Admin::RunnersController < Admin::ApplicationController ...@@ -15,7 +15,7 @@ class Admin::RunnersController < Admin::ApplicationController
# future iterations. For now, this route will have a # future iterations. For now, this route will have a
# redirect until this new view is developed. See more: # redirect until this new view is developed. See more:
# https://gitlab.com/gitlab-org/gitlab/-/issues/347856 # https://gitlab.com/gitlab-org/gitlab/-/issues/347856
redirect_to edit_admin_runner_path(runner) redirect_to edit_admin_runner_path(runner) unless Feature.enabled?(:runner_read_only_admin_view, default_enabled: :yaml)
end end
def edit def edit
......
- add_page_specific_style 'page_bundles/ci_status' - add_page_specific_style 'page_bundles/ci_status'
- title = "##{@runner.id} (#{@runner.short_sha})" - runner_name = "##{@runner.id} (#{@runner.short_sha})"
- breadcrumb_title title - if Feature.enabled?(:runner_read_only_admin_view)
- page_title title - breadcrumb_title _('Edit')
- add_to_breadcrumbs _('Runners'), admin_runners_path - page_title _('Edit'), runner_name
- add_to_breadcrumbs _('Runners'), admin_runners_path
- add_to_breadcrumbs runner_name, admin_runner_path(@runner)
- else
- breadcrumb_title runner_name
- page_title runner_name
#js-runner-details{ data: {runner_id: @runner.id} } #js-admin-runner-edit{ data: {runner_id: @runner.id} }
.row .row
.col-md-6 .col-md-6
......
- add_page_specific_style 'page_bundles/ci_status'
- title = "##{@runner.id} (#{@runner.short_sha})"
- breadcrumb_title title
- page_title title
- add_to_breadcrumbs _('Runners'), admin_runners_path
-# Empty view in development behind feature flag runner_read_only_admin_view
---
name: runner_read_only_admin_view
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77682
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350164
milestone: '14.7'
type: development
group: group::runner
default_enabled: false
import { import {
modelToUpdateMutationVariables as ceModelToUpdateMutationVariables, modelToUpdateMutationVariables as ceModelToUpdateMutationVariables,
runnerToModel as ceRunnerToModel, runnerToModel as ceRunnerToModel,
} from '~/runner/runner_details/runner_update_form_utils'; } from '~/runner/runner_update_form_utils';
export const runnerToModel = (runner) => { export const runnerToModel = (runner) => {
return { return {
......
import { import { modelToUpdateMutationVariables, runnerToModel } from 'ee/runner/runner_update_form_utils';
modelToUpdateMutationVariables,
runnerToModel,
} from 'ee/runner/runner_details/runner_update_form_utils';
const mockRunnerId = 'gid://gitlab/Ci::Runner/1'; const mockRunnerId = 'gid://gitlab/Ci::Runner/1';
const mockPrivateFactor = 1; const mockPrivateFactor = 1;
const mockPublicFactor = 0.5; const mockPublicFactor = 0.5;
describe('ee/runner/runner_details/runner_update_form_utils', () => { describe('ee/runner/runner_update_form_utils', () => {
describe('runnerToModel', () => { describe('runnerToModel', () => {
it('collects project minutes factor', () => { it('collects project minutes factor', () => {
expect( expect(
......
...@@ -31,7 +31,16 @@ RSpec.describe Admin::RunnersController do ...@@ -31,7 +31,16 @@ RSpec.describe Admin::RunnersController do
create(:ci_build, runner: runner, project: project) create(:ci_build, runner: runner, project: project)
end end
it 'redirects to the runner edit page' do it 'shows a runner show page' do
get :show, params: { id: runner.id }
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:show)
end
it 'when runner_read_only_admin_view is off, redirects to the runner edit page' do
stub_feature_flags(runner_read_only_admin_view: false)
get :show, params: { id: runner.id } get :show, params: { id: runner.id }
expect(response).to have_gitlab_http_status(:redirect) expect(response).to have_gitlab_http_status(:redirect)
......
...@@ -463,7 +463,8 @@ RSpec.describe "Admin Runners" do ...@@ -463,7 +463,8 @@ RSpec.describe "Admin Runners" do
describe 'runner page breadcrumbs' do describe 'runner page breadcrumbs' do
it 'contains the current runner id and token' do it 'contains the current runner id and token' do
page.within '[data-testid="breadcrumb-links"]' do page.within '[data-testid="breadcrumb-links"]' do
expect(page.find('h2')).to have_content("##{runner.id} (#{runner.short_sha})") expect(page).to have_link("##{runner.id} (#{runner.short_sha})")
expect(page.find('h2')).to have_content("Edit")
end end
end end
end end
......
...@@ -7,7 +7,7 @@ import { createAlert } from '~/flash'; ...@@ -7,7 +7,7 @@ import { createAlert } from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RunnerHeader from '~/runner/components/runner_header.vue'; import RunnerHeader from '~/runner/components/runner_header.vue';
import getRunnerQuery from '~/runner/graphql/get_runner.query.graphql'; import getRunnerQuery from '~/runner/graphql/get_runner.query.graphql';
import RunnerDetailsApp from '~/runner/runner_details/runner_details_app.vue'; import AdminRunnerEditApp from '~//runner/admin_runner_edit/admin_runner_edit_app.vue';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
import { runnerData } from '../mock_data'; import { runnerData } from '../mock_data';
...@@ -21,14 +21,14 @@ const mockRunnerId = `${getIdFromGraphQLId(mockRunnerGraphqlId)}`; ...@@ -21,14 +21,14 @@ const mockRunnerId = `${getIdFromGraphQLId(mockRunnerGraphqlId)}`;
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(VueApollo); localVue.use(VueApollo);
describe('RunnerDetailsApp', () => { describe('AdminRunnerEditApp', () => {
let wrapper; let wrapper;
let mockRunnerQuery; let mockRunnerQuery;
const findRunnerHeader = () => wrapper.findComponent(RunnerHeader); const findRunnerHeader = () => wrapper.findComponent(RunnerHeader);
const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => { const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
wrapper = mountFn(RunnerDetailsApp, { wrapper = mountFn(AdminRunnerEditApp, {
localVue, localVue,
apolloProvider: createMockApollo([[getRunnerQuery, mockRunnerQuery]]), apolloProvider: createMockApollo([[getRunnerQuery, mockRunnerQuery]]),
propsData: { propsData: {
...@@ -77,7 +77,7 @@ describe('RunnerDetailsApp', () => { ...@@ -77,7 +77,7 @@ describe('RunnerDetailsApp', () => {
it('error is reported to sentry', () => { it('error is reported to sentry', () => {
expect(captureException).toHaveBeenCalledWith({ expect(captureException).toHaveBeenCalledWith({
error: new Error('Network error: Error!'), error: new Error('Network error: Error!'),
component: 'RunnerDetailsApp', component: 'AdminRunnerEditApp',
}); });
}); });
......
...@@ -47,7 +47,7 @@ describe('RunnerTypeCell', () => { ...@@ -47,7 +47,7 @@ describe('RunnerTypeCell', () => {
runner: { runner: {
id: mockRunner.id, id: mockRunner.id,
shortSha: mockRunner.shortSha, shortSha: mockRunner.shortSha,
adminUrl: mockRunner.adminUrl, editAdminUrl: mockRunner.editAdminUrl,
userPermissions: mockRunner.userPermissions, userPermissions: mockRunner.userPermissions,
active: mockRunner.active, active: mockRunner.active,
...runner, ...runner,
...@@ -103,7 +103,7 @@ describe('RunnerTypeCell', () => { ...@@ -103,7 +103,7 @@ describe('RunnerTypeCell', () => {
it('Displays the runner edit link with the correct href', () => { it('Displays the runner edit link with the correct href', () => {
createComponent(); createComponent();
expect(findEditBtn().attributes('href')).toBe(mockRunner.adminUrl); expect(findEditBtn().attributes('href')).toBe(mockRunner.editAdminUrl);
}); });
it('Does not render the runner edit link when user cannot update', () => { it('Does not render the runner edit link when user cannot update', () => {
...@@ -117,9 +117,9 @@ describe('RunnerTypeCell', () => { ...@@ -117,9 +117,9 @@ describe('RunnerTypeCell', () => {
expect(findEditBtn().exists()).toBe(false); expect(findEditBtn().exists()).toBe(false);
}); });
it('Does not render the runner edit link when adminUrl is not provided', () => { it('Does not render the runner edit link when editAdminUrl is not provided', () => {
createComponent({ createComponent({
adminUrl: null, editAdminUrl: null,
}); });
expect(findEditBtn().exists()).toBe(false); expect(findEditBtn().exists()).toBe(false);
......
import { ACCESS_LEVEL_NOT_PROTECTED } from '~/runner/constants'; import { ACCESS_LEVEL_NOT_PROTECTED } from '~/runner/constants';
import { import { modelToUpdateMutationVariables, runnerToModel } from '~/runner/runner_update_form_utils';
modelToUpdateMutationVariables,
runnerToModel,
} from '~/runner/runner_details/runner_update_form_utils';
const mockId = 'gid://gitlab/Ci::Runner/1'; const mockId = 'gid://gitlab/Ci::Runner/1';
const mockDescription = 'Runner Desc.'; const mockDescription = 'Runner Desc.';
...@@ -23,7 +20,7 @@ const mockModel = { ...@@ -23,7 +20,7 @@ const mockModel = {
tagList: 'tag-1, tag-2', tagList: 'tag-1, tag-2',
}; };
describe('~/runner/runner_details/runner_update_form_utils', () => { describe('~/runner/runner_update_form_utils', () => {
describe('runnerToModel', () => { describe('runnerToModel', () => {
it('collects all model data', () => { it('collects all model data', () => {
expect(runnerToModel(mockRunner)).toEqual(mockModel); expect(runnerToModel(mockRunner)).toEqual(mockModel);
......
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