Skip to content

Commit

Permalink
Added Webex Notifier (#3186)
Browse files Browse the repository at this point in the history
* Added Webex Notifier

* Added Webex Notifier doc

* updated webex notifier doc

---------

Co-authored-by: Stephan Köninger <stephan.koeninger@codecentric.de>
  • Loading branch information
Shubzz-02 and SteKoe committed Mar 8, 2024
1 parent 2d801c2 commit 5bce1bf
Show file tree
Hide file tree
Showing 5 changed files with 380 additions and 2 deletions.
28 changes: 28 additions & 0 deletions spring-boot-admin-docs/src/site/asciidoc/server-notifications.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,34 @@ To enable https://telegram.org/[Telegram] notifications you need to create and a
| `+++"&lt;strong&gt;#{instance.registration.name}&lt;/strong&gt;/#{instance.id} is &lt;strong&gt;#{event.statusInfo.status}&lt;/strong&gt;"+++`
|===

[[webex-notifications]]
=== Webex Notifications ===
To enable https://www.webex.com/[Webex] notifications, you need to set the appropriate configuration properties for `auth-token` and `room-id`.

.Webex notifications configuration options
|===
| Property name |Description |Default value

| spring.boot.admin.notify.webex.enabled
| Enable Webex notifications
| `true`

| spring.boot.admin.notify.webex.url
| The Webex server url to send the notifications to.
| "https://webexapis.com/v1/messages"

| spring.boot.admin.notify.webex.auth-token
| The authentication token for your Webex account (e.g. `123456-ascbhuwbtzzk-abtabhixta-788654`).
|

| spring.boot.admin.notify.webex.room-id
| Unique identifier for the target room in Webex.
|

| spring.boot.admin.notify.webex.message
| Text to send. SpEL-expressions are supported. By default, messages will be sent as Markdown, so you can include Markdown formatting.
| `+++"*#{instance.registration.name}* (#{instance.id}) is *#{event.statusInfo.status}*"+++`
|===

[[discord-notifications]]
=== Discord Notifications ===
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 Down Expand Up @@ -69,6 +69,7 @@
import de.codecentric.boot.admin.server.notify.RocketChatNotifier;
import de.codecentric.boot.admin.server.notify.SlackNotifier;
import de.codecentric.boot.admin.server.notify.TelegramNotifier;
import de.codecentric.boot.admin.server.notify.WebexNotifier;
import de.codecentric.boot.admin.server.notify.filter.FilteringNotifier;
import de.codecentric.boot.admin.server.notify.filter.web.NotificationFilterController;

Expand Down Expand Up @@ -362,4 +363,19 @@ public FeiShuNotifier feiShuNotifier(InstanceRepository repository, NotifierProx

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.boot.admin.notify.webex", name = "auth-token")
@AutoConfigureBefore({ NotifierTriggerConfiguration.class, CompositeNotifierConfiguration.class })
@Lazy(false)
public static class WebexNotifierConfiguration {

@Bean
@ConditionalOnMissingBean
@ConfigurationProperties("spring.boot.admin.notify.webex")
public WebexNotifier webexNotifier(InstanceRepository repository, NotifierProxyProperties proxyProperties) {
return new WebexNotifier(repository, createNotifierRestTemplate(proxyProperties));
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*
* 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.notify;

import java.net.URI;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.web.client.RestTemplate;
import reactor.core.publisher.Mono;

import de.codecentric.boot.admin.server.domain.entities.Instance;
import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
import de.codecentric.boot.admin.server.domain.events.InstanceEvent;

// The following class, `WebexNotifier`, is responsible for sending notifications through the Webex API
// whenever events related to the state of instances within the Spring Boot Admin server occur.

/**
* `WebexNotifier` sends notifications via Webex API when instance events occur. It is
* part of the spring-boot-admin-server which is used for monitoring and managing Spring
* Boot applications.
*/
public class WebexNotifier extends AbstractStatusChangeNotifier {

private static final Logger LOGGER = LoggerFactory.getLogger(WebexNotifier.class);

private static final URI DEFAULT_URL = URI.create("https://webexapis.com/v1/messages");

private static final String DEFAULT_MESSAGE = "<strong>#{instance.registration.name}</strong>/#{instance.id} is <strong>#{event.statusInfo.status}</strong>";

private RestTemplate restTemplate;

/**
* base url for Webex API (i.e. https://webexapis.com/v1/messages)
*/
private URI url = DEFAULT_URL;

/**
* Bearer authentication token for Webex API
*/
@Nullable
private String authToken;

/**
* Room identifier in Webex where the message will be sent
*/
@Nullable
private String roomId;

private final SpelExpressionParser parser = new SpelExpressionParser();

/**
* Template for the message to be sent
*/
private Expression message;

/**
* Creates a new WebexNotifier with the given repository and restTemplate.
* @param repository the instance repository responsible for storing instances
* @param restTemplate the restTemplate used to make HTTP requests
*/
public WebexNotifier(InstanceRepository repository, RestTemplate restTemplate) {
super(repository);
this.restTemplate = restTemplate;
this.message = parser.parseExpression(DEFAULT_MESSAGE, ParserContext.TEMPLATE_EXPRESSION);
}

/**
* Sends a notification with the given event and instance.
* @param event the instance event to notify
* @param instance the instance associated with the event
* @return a Mono representing the completion of the notification
* @throws IllegalStateException if 'authToken' is null
*/
@Override
protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {

if (authToken == null) {
return Mono.error(new IllegalStateException("'authToken' must not be null."));
}

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(authToken);

LOGGER.debug("Event: {}", event.getInstance());

return Mono.fromRunnable(() -> restTemplate.postForEntity(url,
new HttpEntity<>(createMessage(event, instance), headers), Void.class));
}

/**
* Creates a message object containing the parameters required for sending a
* notification.
* @param event the instance event for which the message is being created
* @param instance the instance associated with the event
* @return a Map object containing the parameters for sending a notification
*/
protected Object createMessage(InstanceEvent event, Instance instance) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("roomId", this.roomId);
parameters.put("markdown", getText(event, instance));
return parameters;
}

/**
* Retrieves the text for the given event and instance.
* @param event the instance event for which the text is being retrieved
* @param instance the instance associated with the event
* @return the text for the event and instance, or null if not available
*/
@Nullable
protected String getText(InstanceEvent event, Instance instance) {
Map<String, Object> root = new HashMap<>();
root.put("event", event);
root.put("instance", instance);
root.put("lastStatus", getLastStatus(event.getInstance()));
SimpleEvaluationContext context = SimpleEvaluationContext
.forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess(), new MapAccessor())
.withRootObject(root)
.build();

return message.getValue(context, String.class);
}

public void setRestTemplate(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}

public URI getUrl() {
return url;
}

public void setUrl(URI url) {
this.url = url;
}

@Nullable
public String getAuthToken() {
return authToken;
}

public void setAuthToken(@Nullable String authToken) {
this.authToken = authToken;
}

@Nullable
public String getRoomId() {
return roomId;
}

public void setRoomId(@Nullable String roomId) {
this.roomId = roomId;
}

public Expression getMessage() {
return message;
}

public void setMessage(String message) {
this.message = parser.parseExpression(message, ParserContext.TEMPLATE_EXPRESSION);
}

}
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 @@ -44,6 +44,7 @@
import de.codecentric.boot.admin.server.notify.SlackNotifier;
import de.codecentric.boot.admin.server.notify.TelegramNotifier;
import de.codecentric.boot.admin.server.notify.TestNotifier;
import de.codecentric.boot.admin.server.notify.WebexNotifier;

import static org.assertj.core.api.Assertions.assertThat;

Expand Down Expand Up @@ -131,6 +132,13 @@ public void test_rocketchat() {
.run((context) -> assertThat(context).hasSingleBean(RocketChatNotifier.class));
}

@Test
public void test_webex() {
this.contextRunner
.withPropertyValues("spring.boot.admin.notify.webex.auth-token:123456:abtshubzztk-abtabhixta-788654")
.run((context) -> assertThat(context).hasSingleBean(WebexNotifier.class));
}

@Test
public void test_multipleNotifiers() {
this.contextRunner.withUserConfiguration(TestMultipleNotifierConfig.class).run((context) -> {
Expand Down
Loading

0 comments on commit 5bce1bf

Please sign in to comment.