1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-24 15:59:41 +02:00

refactor(docker/events): migrate list view to react [EE-2228] (#11581)

This commit is contained in:
Chaim Lev-Ari 2024-08-28 13:41:15 -06:00 committed by GitHub
parent 9797201c2a
commit 33ce841040
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 71 additions and 69 deletions

View file

@ -188,8 +188,7 @@ angular.module('portainer.docker', ['portainer.app', reactModule]).config([
url: '/events',
views: {
'content@': {
templateUrl: './views/events/events.html',
controller: 'EventsController',
component: 'eventsListView',
},
},
data: {

View file

@ -1,134 +0,0 @@
import { EventMessage } from 'docker-types/generated/1.41';
type EventType = NonNullable<EventMessage['Type']>;
type Action = string;
type Attributes = {
id: string;
name: string;
exitCode: string;
};
type EventToTemplateMap = Record<EventType, ActionToTemplateMap>;
type ActionToTemplateMap = Record<Action, TemplateBuilder>;
type TemplateBuilder = (attr: Attributes) => string;
/**
* {
* [EventType]: {
* [Action]: TemplateBuilder,
* [Action]: TemplateBuilder
* },
* [EventType]: {
* [Action]: TemplateBuilder,
* }
* }
*
* EventType are known and defined by Docker specs
* Action are unknown and specific for each EventType
*/
const templates: EventToTemplateMap = {
builder: {},
config: {},
container: {
stop: ({ name }) => `Container ${name} stopped`,
destroy: ({ name }) => `Container ${name} deleted`,
create: ({ name }) => `Container ${name} created`,
start: ({ name }) => `Container ${name} started`,
kill: ({ name }) => `Container ${name} killed`,
die: ({ name, exitCode }) =>
`Container ${name} exited with status code ${exitCode}`,
commit: ({ name }) => `Container ${name} committed`,
restart: ({ name }) => `Container ${name} restarted`,
pause: ({ name }) => `Container ${name} paused`,
unpause: ({ name }) => `Container ${name} unpaused`,
attach: ({ name }) => `Container ${name} attached`,
detach: ({ name }) => `Container ${name} detached`,
copy: ({ name }) => `Container ${name} copied`,
export: ({ name }) => `Container ${name} exported`,
health_status: ({ name }) => `Container ${name} executed health status`,
oom: ({ name }) => `Container ${name} goes in out of memory`,
rename: ({ name }) => `Container ${name} renamed`,
resize: ({ name }) => `Container ${name} resized`,
top: ({ name }) => `Showed running processes for container ${name}`,
update: ({ name }) => `Container ${name} updated`,
exec_create: () => `Exec instance created`,
exec_start: () => `Exec instance started`,
exec_die: () => `Exec instance exited`,
},
daemon: {},
image: {
delete: () => `Image deleted`,
import: ({ id }) => `Image ${id} imported`,
load: ({ id }) => `Image ${id} loaded`,
tag: ({ name }) => `New tag created for ${name}`,
untag: () => `Image untagged`,
save: ({ id }) => `Image ${id} saved`,
pull: ({ id }) => `Image ${id} pulled`,
push: ({ id }) => `Image ${id} pushed`,
},
network: {
create: ({ name }) => `Network ${name} created`,
destroy: ({ name }) => `Network ${name} deleted`,
remove: ({ name }) => `Network ${name} removed`,
connect: ({ name }) => `Container connected to ${name} network`,
disconnect: ({ name }) => `Container disconnected from ${name} network`,
prune: () => `Networks pruned`,
},
node: {},
plugin: {},
secret: {},
service: {},
volume: {
create: ({ id }) => `Volume ${id} created`,
destroy: ({ id }) => `Volume ${id} deleted`,
mount: ({ id }) => `Volume ${id} mounted`,
unmount: ({ id }) => `Volume ${id} unmounted`,
},
};
function createEventDetails(event: EventMessage) {
const eventType = event.Type ?? '';
// An action can be `action:extra`
// For example `docker exec -it CONTAINER sh`
// Generates the action `exec_create: sh`
let extra = '';
let action = event.Action ?? '';
const hasColon = action?.indexOf(':') ?? -1;
if (hasColon !== -1) {
extra = action?.substring(hasColon) ?? '';
action = action?.substring(0, hasColon);
}
const attr: Attributes = {
id: event.Actor?.ID || '',
name: event.Actor?.Attributes?.name || '',
exitCode: event.Actor?.Attributes?.exitCode || '',
};
// Event types are defined by the docker API specs
// Each event has it own set of actions, which a unknown/not defined by specs
// If the received event or action has no builder associated to it
// We consider the event unsupported and we provide the raw data
const detailsBuilder = templates[eventType as EventType]?.[action];
const details = detailsBuilder
? detailsBuilder(attr)
: `Unsupported event: ${eventType} / ${action}`;
return details + extra;
}
export class EventViewModel {
Time: EventMessage['time'];
Type: EventMessage['Type'];
Details: string;
constructor(data: EventMessage) {
this.Time = data.time;
this.Type = data.Type;
this.Details = createEventDetails(data);
}
}

View file

@ -5,6 +5,7 @@ import { r2a } from '@/react-tools/react2angular';
import { withCurrentUser } from '@/react-tools/withCurrentUser';
import { withUIRouter } from '@/react-tools/withUIRouter';
import { DashboardView } from '@/react/docker/DashboardView/DashboardView';
import { ListView } from '@/react/docker/events/ListView';
import { containersModule } from './containers';
@ -14,6 +15,7 @@ export const viewsModule = angular
'dockerDashboardView',
r2a(withUIRouter(withCurrentUser(DashboardView)), [])
)
.component('eventsListView', r2a(withUIRouter(withCurrentUser(ListView)), []))
.component(
'networkDetailsView',
r2a(withUIRouter(withCurrentUser(NetworksItemView)), [])

View file

@ -1,8 +1,6 @@
import { ping } from '@/react/docker/proxy/queries/usePing';
import { getInfo } from '@/react/docker/proxy/queries/useInfo';
import { getVersion } from '@/react/docker/proxy/queries/useVersion';
import { getEvents } from '@/react/docker/proxy/queries/useEvents';
import { EventViewModel } from '../models/event';
angular.module('portainer.docker').factory('SystemService', SystemServiceFactory);
@ -14,15 +12,5 @@ function SystemServiceFactory(AngularToReact) {
info: useAxios(injectEnvironmentId(getInfo)), // dashboard + docker host view + docker host browser + swarm inspect views + stateManager (update endpoint state)
ping: useAxios(ping), // docker/__module onEnter abstract /docker subpath
version: useAxios(injectEnvironmentId(getVersion)), // docker host view + swarm inspect view + stateManager (update endpoint state)
events: useAxios(injectEnvironmentId(eventsAngularJS)), // events list
};
/**
* @param {EnvironmentId} environmentId Injected
* @param {{since: string; until: string;}} param1
*/
async function eventsAngularJS(environmentId, { since, until }) {
const data = await getEvents(environmentId, { since, until });
return data.map((e) => new EventViewModel(e));
}
}

View file

@ -1,3 +0,0 @@
<page-header title="'Event list'" breadcrumbs="['Events']" reload="true"> </page-header>
<docker-events-datatable dataset="events"></docker-events-datatable>

View file

@ -1,23 +0,0 @@
import moment from 'moment';
angular.module('portainer.docker').controller('EventsController', [
'$scope',
'Notifications',
'SystemService',
function ($scope, Notifications, SystemService) {
function initView() {
const since = moment().subtract(24, 'hour').unix();
const until = moment().unix();
SystemService.events({ since, until })
.then(function success(data) {
$scope.events = data;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to load events');
});
}
initView();
},
]);