Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#1939): implement UI button to retrigger service detection #3938

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2023 the original author or authors.
* Copyright 2014-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -42,6 +42,7 @@
import de.codecentric.boot.admin.server.domain.values.InstanceId;
import de.codecentric.boot.admin.server.domain.values.Registration;
import de.codecentric.boot.admin.server.services.InstanceRegistry;
import de.codecentric.boot.admin.server.web.client.RefreshInstancesEvent;

/**
* Listener for Heartbeats events to publish all services to the instance registry.
Expand Down Expand Up @@ -105,6 +106,11 @@ public void onInstanceRegistered(InstanceRegisteredEvent<?> event) {
discover();
}

@EventListener
public void onRefreshInstances(RefreshInstancesEvent event) {
discover();
}

@EventListener
public void onParentHeartbeat(ParentHeartbeatEvent event) {
discoverIfNeeded(event.getValue());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"status": "Status",
"label": "Anwendungen",
"shutdown": "Möchten Sie die Anwendung <code>{name}</code> herunterfahren?",
"refreshed": "Anwendungen wurden aktualisiert.",
"restart": "Möchten Sie Anwendung <code>{name}</code> neu starten?",
"restarted": "Die Anwendung wurde neu gestartet.",
"unregister": "Möchten Sie die Anwendung <code>{name}</code> deregistrieren?",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"offline": "offline",
"shutdown": "Shutdown application <code>{name}</code>?",
"shutdown_successful": "Successfully shutdown application {name}.",
"refreshed": "Applications refreshed.",
"restart": "Restart application <code>{name}</code>?",
"restarted": "Successfully restarted application <code>{name}</code>.",
"unregister": "Deregister application <code>{name}</code>?",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!--
- Copyright 2014-2019 the original author or authors.
- Copyright 2014-2024 the original author or authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
Expand All @@ -21,6 +21,9 @@
<sba-sticky-subnav>
<div class="container mx-auto flex">
<ApplicationStats />
<sba-confirm-button class="mr-1" @click="refreshContext">
<font-awesome-icon :icon="'rotate-left'" />
</sba-confirm-button>
<ApplicationNotificationCenter
v-if="hasNotificationFiltersSupport"
:notification-filters="notificationFilters"
Expand Down Expand Up @@ -196,6 +199,9 @@ import ApplicationStats from '@/views/applications/ApplicationStats.vue';
import ApplicationStatusHero from '@/views/applications/ApplicationStatusHero.vue';
import InstancesList from '@/views/applications/InstancesList.vue';
import NotificationFilterSettings from '@/views/applications/NotificationFilterSettings.vue';
import SbaButton from '@/components/sba-button.vue';
import SbaConfirmButton from '@/components/sba-confirm-button.vue';
import axios from '@/utils/axios';

const props = defineProps({
error: {
Expand Down Expand Up @@ -320,6 +326,12 @@ const grouped = computed(() => {
return sortBy(list, [(item) => getApplicationStatus(item)]);
});

const refreshContext = () => {
axios.post('/applications').then(() => {
notificationCenter.success(t('applications.refreshed'));
});
};

function getApplicationStatus(item: InstancesListType): string {
return applicationStore.findApplicationByInstanceId(item.instances[0].id)
?.status;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2023 the original author or authors.
* Copyright 2014-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -21,6 +21,7 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.accept.ContentNegotiationManager;
Expand Down Expand Up @@ -56,8 +57,9 @@ public InstancesController instancesController(InstanceRegistry instanceRegistry

@Bean
@ConditionalOnMissingBean
public ApplicationsController applicationsController(ApplicationRegistry applicationRegistry) {
return new ApplicationsController(applicationRegistry);
public ApplicationsController applicationsController(ApplicationRegistry applicationRegistry,
ApplicationEventPublisher applicationEventPublisher) {
return new ApplicationsController(applicationRegistry, applicationEventPublisher);
}

@Configuration(proxyBeanMethods = false)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2023 the original author or authors.
* Copyright 2014-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -20,18 +20,21 @@

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import de.codecentric.boot.admin.server.domain.entities.Application;
import de.codecentric.boot.admin.server.services.ApplicationRegistry;
import de.codecentric.boot.admin.server.web.client.RefreshInstancesEvent;

/**
* REST controller for controlling registration of managed instances.
Expand All @@ -49,15 +52,23 @@ public class ApplicationsController {

private final ApplicationRegistry registry;

public ApplicationsController(ApplicationRegistry registry) {
private final ApplicationEventPublisher publisher;

public ApplicationsController(ApplicationRegistry registry, ApplicationEventPublisher publisher) {
this.registry = registry;
this.publisher = publisher;
}

@GetMapping(path = "/applications", produces = MediaType.APPLICATION_JSON_VALUE)
public Flux<Application> applications() {
return registry.getApplications();
}

@PostMapping(path = "/applications", produces = MediaType.APPLICATION_JSON_VALUE)
public void refreshApplications() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this has already been merged, but I`m no sure if POST to /applications is intuitive for triggering discovery.

I would expect to send some data and add a new application on that method+path (like registering). That would also match the DELETE below where we unregister an application.

publisher.publishEvent(new RefreshInstancesEvent(this));
}

@GetMapping(path = "/applications/{name}", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<ResponseEntity<Application>> application(@PathVariable("name") String name) {
return registry.getApplication(name).map(ResponseEntity::ok).defaultIfEmpty(ResponseEntity.notFound().build());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2014-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package de.codecentric.boot.admin.server.web.client;

import org.springframework.context.ApplicationEvent;

public class RefreshInstancesEvent extends ApplicationEvent {

public RefreshInstancesEvent(Object source) {
super(source);
}

}
Loading