Skip to content

Commit

Permalink
Fixes #37228 - Replace Ansible/Roles page with React component
Browse files Browse the repository at this point in the history
  • Loading branch information
Thorben-D committed Jun 12, 2024
1 parent 6cd6fe5 commit e263308
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 44 deletions.
9 changes: 9 additions & 0 deletions app/controllers/ui_ansible_roles_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
class UiAnsibleRolesController < ::Api::V2::BaseController

before_action :find_resource, :only => [:destroy]

def resource_name(resource = 'AnsibleRole')
super resource
end
Expand All @@ -7,6 +10,12 @@ def index
@ui_ansible_roles = resource_scope_for_index(:permission => :view_ansible_roles)
end

api :DELETE, '/ui_ansible_roles/:id', N_('Deletes Ansible role')
param :id, :identifier, :required => true
def destroy
AnsibleRole.find_by(id: params[:id]).destroy
end

# restore original method from find_common to ignore resource nesting
def resource_scope(**kwargs)
@resource_scope ||= scope_for(resource_class, **kwargs)
Expand Down
44 changes: 3 additions & 41 deletions app/views/ansible_roles/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,47 +1,9 @@
<%= webpacked_plugins_js_for :foreman_ansible %>
<%= webpacked_plugins_css_for :foreman_ansible %>
<% title _("Ansible Roles") %>
<% title_actions ansible_proxy_import(hash_for_import_ansible_roles_path),
documentation_button('#4.1ImportingRoles', :root_url => ansible_doc_url) %>
<table class="<%= table_css_classes 'table-fixed' %>">
<thead>
<tr>
<th class="col-md-6"><%= sort :name, :as => s_("Role|Name") %></th>
<th class="col-md-2"><%= _("Hostgroups") %></th>
<th class="col-md-2"><%= _("Hosts") %></th>
<th class="col-md-2"><%= _("Variables") %></th>
<th class="col-md-2"><%= sort :updated_at, :as => _("Imported at") %></th>
<th class="col-md-2"><%= _("Actions") %></th>
</tr>
</thead>
<tbody>
<% @ansible_roles.each do |role| %>
<tr>
<td class="ellipsis"><%= role.name %></td>
<td class="ellipsis"><%= link_to role.hostgroups.count, hostgroups_path(:search => "ansible_role = #{role.name}") %></td>
<td class="ellipsis"><%= link_to role.hosts.count, hosts_path(:search => "ansible_role = #{role.name}")%></td>
<td class="ellipsis"><%= link_to(role.ansible_variables.count, ansible_variables_path(:search => "ansible_role = #{role}")) %></td>
<td class="ellipsis"><%= import_time role %></td>
<td>
<%
links = [
link_to(
_('Variables'),
ansible_variables_path(:search => "ansible_role = #{role}")
),
display_delete_if_authorized(
hash_for_ansible_role_path(:id => role).
merge(:auth_object => role, :authorizer => authorizer),
:data => { :confirm => _("Delete %s?") % role.name },
:action => :delete
)
]
%>
<%= action_buttons(*links) %>
</td>
</tr>
<% end %>
</tbody>
</table>

<%= will_paginate_with_info @ansible_roles %>
17 changes: 16 additions & 1 deletion app/views/ui_ansible_roles/show.json.rabl
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
object @ansible_role

extends "api/v2/ansible_roles/show"
attributes :id, :name
code :hostgroups_count do |role|
role.hostgroups.count
end
code :hosts_count do |role|
role.hosts.count
end
code :variables_count do |role|
role.ansible_variables.count
end
code :import_time do |role|
import_time(role)
end
code :can_delete do |role|
authorized_for(:controller => UiAnsibleRolesController, :action => :destroy)
end
3 changes: 2 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
end
end

resources :ui_ansible_roles, :only => [:index]
resources :ui_ansible_roles, :only => [:index, :destroy]

resources :ansible_variables, :except => [:show] do
resources :lookup_values, :only => [:index, :create, :update, :destroy]
Expand Down Expand Up @@ -117,4 +117,5 @@

match '/ansible/hostgroups' => 'react#index', :via => [:get]
match '/ansible/hostgroups/*page' => 'react#index', :via => [:get]
match '/ansible/ansible_roles' => 'react#index', :via => [:get]
end
3 changes: 2 additions & 1 deletion lib/foreman_ansible/register.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@
:resource_type => 'AnsibleRole'
permission :destroy_ansible_roles,
{ :ansible_roles => [:destroy],
:'api/v2/ansible_roles' => [:destroy, :obsolete] },
:'api/v2/ansible_roles' => [:destroy, :obsolete],
:ui_ansible_roles => [:destroy] },
:resource_type => 'AnsibleRole'
permission :import_ansible_roles,
{ :ansible_roles => [:import, :confirm_import],
Expand Down
34 changes: 34 additions & 0 deletions webpack/components/AnsibleRoles/AnsibleRolesPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import TableIndexPage from 'foremanReact/components/PF4/TableIndexPage/TableIndexPage';
import { translate as __ } from 'foremanReact/common/I18n';
import { getDocsURL } from 'foremanReact/common/helpers';
import { AnsibleRolesImportButton } from './components/AnsibleRolesImportButton';
import { getActions } from '../AnsibleRoles/AnsibleRolesPageHelpers';

export const AnsibleRolesPage = () => (
<TableIndexPage
header={__('Ansible Roles')}
controller="ansible_roles"
apiUrl="/ansible/ui_ansible_roles"
apiOptions={{ key: 'WEBHOOKS_API_REQUEST_KEY' }}
hasHelpPage
creatable={false}
isDeleteable
searchable
customToolbarItems={
<AnsibleRolesImportButton>Primary</AnsibleRolesImportButton>
}
customHelpURL={getDocsURL(
'Managing_Configurations_Ansible',
'Importing_Ansible_Roles_and_Variables_ansible'
)}
rowKebabItems={getActions}
columns={{
import_time: { title: __('Imported at'), isSorted: true },
variables_count: { title: __('Variables') },
hosts_count: { title: __('Hosts') },
hostgroups_count: { title: __('Hostgroups') },
name: { title: __('Name'), isSorted: true },
}}
/>
);
38 changes: 38 additions & 0 deletions webpack/components/AnsibleRoles/AnsibleRolesPageHelpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { translate as __ } from 'foremanReact/common/I18n';

export const getActions = ({ id, name, canDelete, ...item }) => [
{
title: (
<a href={hostgroupsUrl(name)} target="_blank" rel="noreferrer">
{__('View Hostgroups')}
</a>
),
isDisabled: false,
},
{
title: (
<a href={hostsUrl(name)} target="_blank" rel="noreferrer">
{__('View Hosts')}
</a>
),
isDisabled: false,
},
{
title: (
<a href={variablesUrl(name)} target="_blank" rel="noreferrer">
{__('View Variables')}
</a>
),
isDisabled: false,
},
];

const hostgroupsUrl = roleName => `/hostgroups${searchString(roleName)}`;
const hostsUrl = roleName => `/hosts${searchString(roleName)}`;
const variablesUrl = roleName => `ansible_variables${searchString(roleName)}`;
const searchString = roleName =>
`?search=${encodeURIComponent(`ansible_role = ${roleName}`)}`;

export const importUrl = (proxyName, proxyId) =>
`ansible_roles/import?proxy=${encodeURIComponent(`${proxyId}-${proxyName}`)}`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from 'react';
import { Dropdown, DropdownToggle, DropdownItem } from '@patternfly/react-core';
import { translate as __ } from 'foremanReact/common/I18n';
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
import { importUrl } from '../AnsibleRolesPageHelpers';

export const AnsibleRolesImportButton = () => {
let dropdownItems;

const {
response: { results },
status,
} = useAPI('get', '/api/v2/smart_proxies');

const [isOpen, setIsOpen] = React.useState(false);
const onToggle = isToggleOpen => {
setIsOpen(isToggleOpen);
};
const onFocus = () => {
const element = document.getElementById('toggle-primary');
element.focus();
};
const onSelect = () => {
setIsOpen(false);
onFocus();
};

if (status !== 'RESOLVED') {
dropdownItems = [
<DropdownItem key="loading_item">{__('Loading...')}</DropdownItem>,
];
} else {
dropdownItems = results
.filter(result =>
result.features.some(feature => feature.name === 'Ansible')
)
.map(proxy => (
<DropdownItem key={proxy.name} href={importUrl(proxy.name, proxy.id)}>
{proxy.name}
</DropdownItem>
));
if (dropdownItems.length === 0) {
dropdownItems = [
<DropdownItem isDisabled key="none_found">
{__('No Smart Proxies found')}
</DropdownItem>,
];
}
}
return (
<Dropdown
onSelect={onSelect}
toggle={
<DropdownToggle
id="toggle-primary"
toggleVariant="primary"
onToggle={onToggle}
>
{__('Import from')}
</DropdownToggle>
}
isOpen={isOpen}
dropdownItems={dropdownItems}
/>
);
};
16 changes: 16 additions & 0 deletions webpack/routes/AnsibleRoles/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import { Helmet } from 'react-helmet';
import { translate as __ } from 'foremanReact/common/I18n';

import { AnsibleRolesPage } from '../../components/AnsibleRoles/AnsibleRolesPage';

const AnsibleRoles = () => (
<React.Fragment>
<Helmet>
<title>{__('Ansible Roles')}</title>
</Helmet>
<AnsibleRolesPage />
</React.Fragment>
);

export default AnsibleRoles;
6 changes: 6 additions & 0 deletions webpack/routes/routes.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import React from 'react';
import HostgroupJobs from './HostgroupJobs';
import AnsibleRoles from './AnsibleRoles';

export default [
{
path: '/ansible/hostgroups/:id',
render: props => <HostgroupJobs {...props} />,
exact: true,
},
{
path: '/ansible/ansible_roles',
render: props => <AnsibleRoles {...props} />,
exact: true,
},
];

0 comments on commit e263308

Please sign in to comment.