Integrate webex webhooks

This commit is contained in:
2026-01-02 15:24:02 +01:00
parent a4c72098b3
commit 071fe5badd
16 changed files with 502 additions and 93 deletions

View File

@@ -8,13 +8,17 @@ services:
- "8000:8080"
- "5005:5005"
environment:
SSL_KEYSTORE_PATH: "/certs/keystore.p12"
BROKER_URL: "tcp://api-broker:1883"
BROKER_SECURE_URL: "tcp://api-broker:8883"
NGROK_URL: "https://lipographic-angla-cistophoric.ngrok-free.dev"
MONGODB_URI: "mongodb://board-mate-user:apx820kcng@mongodb:27017/board-mate-db"
BROKER_USERNAME: "board-mate-api"
BROKER_PASSWORD: "hepl"
JWT_SECRET: "enY3OWU4djFyMTByNTZhcG9uY3Z0djQ5cnY0eDhhNWM0bjg5OTRjNDhidA=="
SSL_KEYSTORE_PATH: "/certs/keystore.p12"
MONGODB_URI: "mongodb://board-mate-user:apx820kcng@mongodb:27017/board-mate-db"
WEBEX_BOT_TOKEN: "YmU0NTdkNDMtYTg1ZC00M2YyLTk3YzUtODA1MWFmOTk1NjA1ZmU3MTYxNGUtYWZm_P0A1_14a2639d-5e4d-48b4-9757-f4b8a23372de"
WEBEX_CLIENT_TOKEN: "N2U0ZGE1OWItZTBhOC00MmU5LWIwMzYtNjM0NDk3NGUwMmIyNjEyMmNiMTYtOTI3_P0A1_14a2639d-5e4d-48b4-9757-f4b8a23372de"
WEBEX_SHARED_SECRET: "cUxSc0ZjNVBZVG5oRmhqaVN0YUtMTEVZb0pIZW5EY2Rwa0hUaWdiVm9nWlJiY2t5aFdmTjhvWmQ5U3R3TDIxVE1CRTl4VGJldVI3TFdVa3lMbFVPZUVSMkZPSnBHTjk="
DATABASE_NAME: "board-mate-db"
JAVA_TOOL_OPTIONS: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"
depends_on:
@@ -79,15 +83,21 @@ services:
- ./mosquitto/log:/mosquitto/log
- ./mosquitto/certs:/mosquitto/certs
#mongo-express:
# image: mongo-express:latest
# depends_on:
# - mongodb
# ports:
# - "8401:8081"
# environment:
# - ME_CONFIG_MONGODB_SERVER=mongodb
# - ME_CONFIG_MONGODB_PORT=27017
# - ME_CONFIG_MONGODB_ADMINUSERNAME=root
# - ME_CONFIG_MONGODB_ADMINPASSWORD=secret
# - ME_CONFIG_MONGODB_AUTH_DATABASE=admin
ngrok:
image: ngrok/ngrok:latest
command: http --log=stdout nginx:8600
ports:
- "8500:4040"
environment:
NGROK_AUTHTOKEN: 37gBJ6KZQIK9nwcq0mWlfRHqtvX_49hdE8PVLmRTzUVaNRJqx
NGROK_PORT: nginx:8600
nginx:
image: nginx:alpine
container_name: nginx
ports:
- "8600:8080"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- boardmate-api

16
api/nginx/nginx.conf Normal file
View File

@@ -0,0 +1,16 @@
events {}
http {
server {
listen 8600;
location / {
proxy_pass https://boardmate-api:8080;
proxy_ssl_verify off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}

View File

@@ -5,9 +5,9 @@ public class Message {
private String id;
private String content;
private String clientId;
private int timeStamp;
private long timeStamp;
public Message(String id, String content, String clientId, int timeStamp){
public Message(String id, String content, String clientId, long timeStamp){
this.id = id;
this.content = content;
this.clientId = clientId;
@@ -38,7 +38,7 @@ public class Message {
this.clientId = clientId;
}
public int getTimeStamp() {
public long getTimeStamp() {
return timeStamp;
}

View File

@@ -59,6 +59,7 @@ public class AppSecurityConfig {
"/docs/**",
"/v1/docs/**",
"/swagger-ui.html",
"/message/webhook",
"/authenticate",
"/client/create").permitAll()
.anyRequest().authenticated()

View File

@@ -1,25 +1,45 @@
package be.naaturel.boardmateapi.configurations.configurations;
import be.naaturel.boardmateapi.common.helpers.Logger;
import be.naaturel.boardmateapi.configurations.properties.NgrokProperties;
import be.naaturel.boardmateapi.configurations.properties.WebexProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@Configuration
public class WebexConfig {
private final WebexProperties properties;
private final WebexProperties webexProperties;
private final NgrokProperties ngrokProperties;
public WebexConfig(WebexProperties properties){
this.properties = properties;
public WebexConfig(WebexProperties webexProperties, NgrokProperties ngrokProperties){
this.webexProperties = webexProperties;
this.ngrokProperties = ngrokProperties;
}
@Bean(name = "clientToken")
public String clientToken() {
return properties.clientToken;
return webexProperties.clientToken;
}
@Bean(name = "botToken")
public String botToken() {
return properties.botToken;
return this.webexProperties.botToken;
}
@Bean(name = "sharedSecret")
public String sharedSecret() {
return webexProperties.sharedSecret;
}
}

View File

@@ -0,0 +1,12 @@
package be.naaturel.boardmateapi.configurations.properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class NgrokProperties {
@Value("${ngrok.url}")
public String url;
}

View File

@@ -12,4 +12,7 @@ public class WebexProperties {
@Value("${webex.bot.token}")
public String botToken;
@Value("${webex.shared-secret}")
public String sharedSecret;
}

View File

@@ -1,12 +1,18 @@
package be.naaturel.boardmateapi.controllers;
import be.naaturel.boardmateapi.common.exceptions.ServiceException;
import be.naaturel.boardmateapi.common.helpers.Logger;
import be.naaturel.boardmateapi.common.models.Message;
import be.naaturel.boardmateapi.common.models.Room;
import be.naaturel.boardmateapi.controllers.dtos.MessageDto;
import be.naaturel.boardmateapi.controllers.dtos.MessagePostRequestDto;
import be.naaturel.boardmateapi.controllers.dtos.ResponseBody;
import be.naaturel.boardmateapi.controllers.dtos.WebexWebhook;
import be.naaturel.boardmateapi.services.MessageService;
import be.naaturel.boardmateapi.services.MqttService;
import be.naaturel.boardmateapi.services.WebexService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -19,11 +25,16 @@ public class ChatController {
private final MessageService messageService;
private final WebexService webexService;
private final MqttService mqttService;
@Autowired
public ChatController(MessageService messageService, WebexService webexService){
public ChatController(
MessageService messageService,
WebexService webexService,
MqttService mqttService){
this.messageService = messageService;
this.webexService = webexService;
this.mqttService = mqttService;
}
@PostMapping("/message/send")
@@ -81,4 +92,38 @@ public class ChatController {
}
}
@PostMapping("/message/webhook")
public ResponseEntity<ResponseBody<Void>> handleWebhook(
@RequestHeader("X-Spark-Signature") String signature,
@RequestBody String rawPayload) {
Logger.displayInfo("========================");
ResponseBody<Void> result = ResponseBody.createEmpty();
if (!webexService.verifySignature(rawPayload, signature)) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(result);
}
try {
ObjectMapper mapper = new ObjectMapper();
WebexWebhook payload = mapper.readValue(rawPayload, WebexWebhook.class);
Room room = webexService.getByRoom(payload.getData().getRoomId());
Message msg = webexService.fetchMessage(payload.getData().getId());
this.messageService.save(msg);
this.mqttService.publish("/chat/" + room.getClientId() + "/message", msg);
Logger.displayInfo(msg.getContent());
Logger.displayInfo(msg.getId());
Logger.displayInfo(msg.getClientId());
result.setSuccess(true);
return ResponseEntity.ok(result);
} catch (Exception e) {
result.setMessage("Unable to handle request");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}
}

View File

@@ -5,7 +5,7 @@ public class MessageDto {
private String content;
private int timestamp;
private long timestamp;
public String getContent() {
return content;
@@ -15,11 +15,11 @@ public class MessageDto {
this.content = content;
}
public int getTimestamp() {
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(int timestamp) {
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}

View File

@@ -0,0 +1,85 @@
package be.naaturel.boardmateapi.controllers.dtos;
public class WebexWebhook {
private String id;
private String name;
private String targetUrl;
private String resource;
private String event;
private String orgId;
private String createdBy;
private String appId;
private String ownedBy;
private String status;
private String created;
private String actorId;
private WebexData data;
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getTargetUrl() { return targetUrl; }
public void setTargetUrl(String targetUrl) { this.targetUrl = targetUrl; }
public String getResource() { return resource; }
public void setResource(String resource) { this.resource = resource; }
public String getEvent() { return event; }
public void setEvent(String event) { this.event = event; }
public String getOrgId() { return orgId; }
public void setOrgId(String orgId) { this.orgId = orgId; }
public String getCreatedBy() { return createdBy; }
public void setCreatedBy(String createdBy) { this.createdBy = createdBy; }
public String getAppId() { return appId; }
public void setAppId(String appId) { this.appId = appId; }
public String getOwnedBy() { return ownedBy; }
public void setOwnedBy(String ownedBy) { this.ownedBy = ownedBy; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getCreated() { return created; }
public void setCreated(String created) { this.created = created; }
public String getActorId() { return actorId; }
public void setActorId(String actorId) { this.actorId = actorId; }
public WebexData getData() { return data; }
public void setData(WebexData data) { this.data = data; }
public static class WebexData {
private String id;
private String roomId;
private String roomType;
private String personId;
private String personEmail;
private String created;
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getRoomId() { return roomId; }
public void setRoomId(String roomId) { this.roomId = roomId; }
public String getRoomType() { return roomType; }
public void setRoomType(String roomType) { this.roomType = roomType; }
public String getPersonId() { return personId; }
public void setPersonId(String personId) { this.personId = personId; }
public String getPersonEmail() { return personEmail; }
public void setPersonEmail(String personEmail) { this.personEmail = personEmail; }
public String getCreated() { return created; }
public void setCreated(String created) { this.created = created; }
}
}

View File

@@ -9,4 +9,6 @@ import java.util.Optional;
public interface RoomRepo extends MongoRepository<RoomDto, String> {
Optional<RoomDto> findByClientId(String clientId);
Optional<RoomDto> findByRoomId(String roomdId);
}

View File

@@ -17,7 +17,7 @@ public class MessageDto {
private String content;
@Field("timestamp")
private int timestamp;
private long timestamp;
public String getId() {
return id;
@@ -43,11 +43,11 @@ public class MessageDto {
this.content = content;
}
public int getTimestamp() {
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(int timestamp) {
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
}

View File

@@ -0,0 +1,76 @@
package be.naaturel.boardmateapi.repository.dtos;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.Instant;
import java.util.List;
@JsonIgnoreProperties(ignoreUnknown = true)
public class WebexMessageDto {
private String id;
private String roomId;
private String roomType;
private String text;
private String personId;
private String personEmail;
private Instant created;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getRoomId() {
return roomId;
}
public void setRoomId(String roomId) {
this.roomId = roomId;
}
public String getRoomType() {
return roomType;
}
public void setRoomType(String roomType) {
this.roomType = roomType;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getPersonId() {
return personId;
}
public void setPersonId(String personId) {
this.personId = personId;
}
public String getPersonEmail() {
return personEmail;
}
public void setPersonEmail(String personEmail) {
this.personEmail = personEmail;
}
public Instant getCreated() {
return created;
}
@JsonProperty("created")
public void setCreated(String created) {
this.created = Instant.parse(created);
}
}

View File

@@ -3,6 +3,7 @@ package be.naaturel.boardmateapi.services;
import be.naaturel.boardmateapi.common.exceptions.ServiceException;
import be.naaturel.boardmateapi.common.helpers.Logger;
import be.naaturel.boardmateapi.common.models.MqttMessageWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.eclipse.paho.client.mqttv3.*;
import org.springframework.stereotype.Service;
@@ -32,14 +33,23 @@ public class MqttService {
this.onMessageReceived = consumer;
}
public void publish(String topic, String payload) {
public void publish(String topic, Object data) throws ServiceException {
try {
String payload = new ObjectMapper().writeValueAsString(data);
publish(topic, payload);
} catch (Exception e){
throw new ServiceException("Unable to serialize data", e);
}
}
public void publish(String topic, String payload) throws ServiceException {
try {
connect();
MqttMessage message = new MqttMessage(payload.getBytes(StandardCharsets.UTF_8));
message.setQos(1);
brokerClient.publish(topic, message);
} catch (MqttException e) {
throw new RuntimeException(e);
throw new ServiceException("Failed to publish on broker", e);
}
}

View File

@@ -4,66 +4,93 @@ import be.naaturel.boardmateapi.common.exceptions.ServiceException;
import be.naaturel.boardmateapi.common.helpers.Logger;
import be.naaturel.boardmateapi.common.models.Message;
import be.naaturel.boardmateapi.common.models.Room;
import be.naaturel.boardmateapi.configurations.properties.NgrokProperties;
import be.naaturel.boardmateapi.configurations.properties.WebexProperties;
import be.naaturel.boardmateapi.repository.RoomRepo;
import be.naaturel.boardmateapi.repository.dtos.RoomDto;
import be.naaturel.boardmateapi.repository.dtos.WebexMessageDto;
import be.naaturel.boardmateapi.repository.mappings.RoomMapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@Service
public class WebexService {
private final String botToken;
private final String clientToken;
private final String sharedSecret;
private final String ngrokUrl;
private final RoomRepo repo;
private final ObjectMapper mapper;
@Autowired
public WebexService(
RoomRepo repo,
@Qualifier("botToken") String botToken){
WebexProperties webexProperties,
NgrokProperties ngrokProperties) {
this.repo = repo;
this.botToken = botToken;
this.botToken = webexProperties.botToken;
this.clientToken = webexProperties.clientToken;
this.sharedSecret = webexProperties.sharedSecret;
this.ngrokUrl = ngrokProperties.url;
this.mapper = new ObjectMapper();
}
@PostConstruct
public void initializeWebhooks() {
try (HttpClient client = HttpClient.newHttpClient()) {
List<Map<String, Object>> rooms = fetchRooms(client);
List<Map<String, Object>> existingWebhooks = fetchWebhooks(client);
String targetUrl = ngrokUrl + "/message/webhook";
for (Map<String, Object> room : rooms) {
String roomId = (String) room.get("id");
String roomTitle = (String) room.get("title");
if (!webhookExists(existingWebhooks, roomId, targetUrl)) {
createWebhook(client, roomId, targetUrl);
Logger.displayInfo("Webhook created for room: " + roomTitle);
} else {
Logger.displayInfo("Webhook already exists for room: " + roomTitle + "(ID:" + roomId + ")" );
}
}
} catch (Exception e) {
e.printStackTrace();
Logger.displayError("Failed to init webhooks: " + e.getMessage());
}
}
public void post(Message m) throws ServiceException {
try (HttpClient client = HttpClient.newHttpClient()) {
Room room = getClientRoom(m.getClientId());
Room room = getByClient(m.getClientId());
if (room == null) {
room = createRoom(m.getClientId());
inviteMemberToRoom(room.getId(), "laurent.crema@student.hepl.be");
}
ObjectMapper mapper = new ObjectMapper();
String jsonBody = mapper.writeValueAsString(
Map.of(
Map<String, Object> body = Map.of(
"roomId", room.getId(),
"text", m.getContent()
)
);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://webexapis.com/v1/messages"))
.header("Authorization", "Bearer " + this.botToken)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 300) {
throw new RuntimeException("Webex error " + response.statusCode() + " : " + response.body());
}
sendPostRequest(client, "https://webexapis.com/v1/messages", botToken, body);
} catch (Exception e) {
Logger.displayError(Arrays.toString(e.getStackTrace()));
@@ -71,15 +98,134 @@ public class WebexService {
}
}
public Message fetchMessage(String messageId) throws ServiceException {
try (HttpClient client = HttpClient.newHttpClient()) {
String response = sendGetRequest(client,
"https://webexapis.com/v1/messages/" + messageId, clientToken);
WebexMessageDto dto = mapper.readValue(response, WebexMessageDto.class);
return new Message(dto.getId(), dto.getText(), dto.getPersonId(), dto.getCreated().getEpochSecond());
} catch (Exception e) {
throw new ServiceException("Failed to fetch Webex message", e);
}
}
public Room createRoom(String clientId) throws ServiceException {
try (HttpClient client = HttpClient.newHttpClient()) {
Map<String, Object> body = Map.of("title", "Support");
String response = sendPostRequest(client, "https://webexapis.com/v1/rooms", botToken, body);
ObjectMapper mapper = new ObjectMapper();
String jsonBody = mapper.writeValueAsString(Map.of("title", "Support"));
JsonNode jsonNode = mapper.readTree(response);
String id = jsonNode.get("id").asText();
String title = jsonNode.get("title").asText();
RoomDto dto = new RoomDto();
dto.setRoomId(id);
dto.setTitle(title);
dto.setClientId(clientId);
repo.save(dto);
return new Room(id, title, clientId);
} catch (Exception e) {
Logger.displayError(Arrays.toString(e.getStackTrace()));
throw new ServiceException("Failed to create room: " + e.getMessage(), e);
}
}
public void inviteMemberToRoom(String roomId, String email) throws ServiceException {
try (HttpClient client = HttpClient.newHttpClient()) {
Map<String, Object> body = Map.of(
"roomId", roomId,
"personEmail", email
);
sendPostRequest(client, "https://webexapis.com/v1/memberships", botToken, body);
} catch (Exception e) {
Logger.displayError(Arrays.toString(e.getStackTrace()));
throw new ServiceException("Failed to invite member: " + e.getMessage(), e);
}
}
public boolean verifySignature(String payload, String signature) {
try {
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(sharedSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
byte[] payloadBytes = payload.getBytes(StandardCharsets.UTF_8);
byte[] rawHmac = mac.doFinal(payloadBytes);
StringBuilder sb = new StringBuilder();
for (byte b : rawHmac) {
sb.append(String.format("%02x", b));
}
String expected = sb.toString();
return expected.equals(signature.trim());
} catch (Exception e) {
Logger.displayError(e.getMessage());
return false;
}
}
public Room getByClient(String clientId) {
return repo.findByClientId(clientId).map(RoomMapper::toModel).orElse(null);
}
public Room getByRoom(String roomId) {
return repo.findByRoomId(roomId).map(RoomMapper::toModel).orElse(null);
}
private List<Map<String, Object>> fetchRooms(HttpClient client) throws Exception {
String response = sendGetRequest(client, "https://webexapis.com/v1/rooms", clientToken);
JsonNode items = mapper.readTree(response).get("items");
return mapper.readValue(items.toString(), List.class);
}
private List<Map<String, Object>> fetchWebhooks(HttpClient client) throws Exception {
String response = sendGetRequest(client, "https://webexapis.com/v1/webhooks", clientToken);
JsonNode items = mapper.readTree(response).get("items");
return mapper.readValue(items.toString(), List.class);
}
private void createWebhook(HttpClient client, String roomId, String targetUrl) throws Exception {
Map<String, Object> body = Map.of(
"name", "BoardMate Webhook",
"targetUrl", targetUrl,
"resource", "messages",
"event", "created",
"roomId", roomId,
"secret", sharedSecret
);
sendPostRequest(client, "https://webexapis.com/v1/webhooks", clientToken, body);
}
private boolean webhookExists(List<Map<String, Object>> webhooks, String roomId, String targetUrl) {
return webhooks.stream().anyMatch(w ->
targetUrl.equals(w.get("targetUrl")) &&
"messages".equals(w.get("resource")) &&
"created".equals(w.get("event")) &&
roomId.equals(w.get("roomId"))
);
}
private String sendGetRequest(HttpClient client, String url, String token) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Authorization", "Bearer " + token)
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 300) {
throw new RuntimeException("Webex GET error " + response.statusCode() + " : " + response.body());
}
return response.body();
}
private String sendPostRequest(HttpClient client, String url, String token, Map<String, Object> body) throws Exception {
String jsonBody = mapper.writeValueAsString(body);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://webexapis.com/v1/rooms"))
.header("Authorization", "Bearer " + this.botToken)
.uri(URI.create(url))
.header("Authorization", "Bearer " + token)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
@@ -87,28 +233,8 @@ public class WebexService {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 300) {
throw new RuntimeException("Webex error " + response.statusCode() + " : " + response.body());
throw new RuntimeException("Webex POST error " + response.statusCode() + " : " + response.body());
}
JsonNode jsonNode = mapper.readTree(response.body());
String id = jsonNode.get("id").asText();
String title = jsonNode.get("title").asText();
RoomDto dto = new RoomDto();
dto.setTitle(title);
dto.setClientId(clientId);
dto.setRoomId(id);
repo.save(dto);
return new Room(id, title, clientId);
} catch (Exception e) {
Logger.displayError(Arrays.toString(e.getStackTrace()));
throw new ServiceException("Failed to create private room : " + e.getMessage(), e);
return response.body();
}
}
private Room getClientRoom(String clientId){
Optional<RoomDto> dto = repo.findByClientId(clientId);
return dto.map(RoomMapper::toModel).orElse(null);
}
}

View File

@@ -27,8 +27,11 @@ mqtt.username=${BROKER_USERNAME}
mqtt.password=${BROKER_PASSWORD}
#=============WEBEX=============
webex.client.token=N2E0ODMyZDUtY2JmZi00YjlhLWFjZmEtOTU0MmFlNjY3ZDE2M2ZhYWYzNzAtNzFm_P0A1_14a2639d-5e4d-48b4-9757-f4b8a23372de
webex.bot.token=MGM4ZDYzYzctZTZiMi00MjNlLWI3YzEtOTFhNDlmOGM1YzVjYWJhYTk0NzctNjBj_P0A1_14a2639d-5e4d-48b4-9757-f4b8a23372de
webex.client.token=${WEBEX_CLIENT_TOKEN}
webex.bot.token=${WEBEX_BOT_TOKEN}
webex.shared-secret=${WEBEX_SHARED_SECRET}
#=============NGROK=============
ngrok.url=${NGROK_URL}
#=============METRICS=============
management.endpoint.health.show-details=always