Add webex message posting

This commit is contained in:
2026-01-02 00:03:46 +01:00
parent a0e974d511
commit 08afaa19ee
20 changed files with 34360 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
package be.naaturel.boardmateapi.common.models;
public class Message {
private String id;
private String content;
private String clientId;
private int timeStamp;
public Message(String id, String content, String clientId, int timeStamp){
this.id = id;
this.content = content;
this.clientId = clientId;
this.timeStamp = timeStamp;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public int getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(int timeStamp) {
this.timeStamp = timeStamp;
}
}

View File

@@ -0,0 +1,38 @@
package be.naaturel.boardmateapi.common.models;
public class Room {
private String id;
private String title;
private String clientId;
public Room(String id, String title, String clientId) {
this.id = id;
this.title = title;
this.clientId = clientId;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
}

View File

@@ -0,0 +1,25 @@
package be.naaturel.boardmateapi.configurations.configurations;
import be.naaturel.boardmateapi.configurations.properties.WebexProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WebexConfig {
private final WebexProperties properties;
public WebexConfig(WebexProperties properties){
this.properties = properties;
}
@Bean(name = "clientToken")
public String clientToken(){
return properties.clientToken;
}
@Bean(name = "botToken")
public String botToken(){
return properties.botToken;
}
}

View File

@@ -0,0 +1,15 @@
package be.naaturel.boardmateapi.configurations.properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class WebexProperties {
@Value("${webex.client.token}")
public String clientToken;
@Value("${webex.bot.token}")
public String botToken;
}

View File

@@ -0,0 +1,60 @@
package be.naaturel.boardmateapi.controllers;
import be.naaturel.boardmateapi.common.models.Message;
import be.naaturel.boardmateapi.controllers.dtos.MessagePostRequestDto;
import be.naaturel.boardmateapi.controllers.dtos.ResponseBody;
import be.naaturel.boardmateapi.services.MessageService;
import be.naaturel.boardmateapi.services.WebexService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ChatController {
private final MessageService messageService;
private final WebexService webexService;
@Autowired
public ChatController(MessageService messageService, WebexService webexService){
this.messageService = messageService;
this.webexService = webexService;
}
@PostMapping("/message/send")
public ResponseEntity<ResponseBody<String>> postMessage(@RequestBody MessagePostRequestDto messagePostRequest) {
ResponseBody<String> result = ResponseBody.createEmpty();
try {
Message model = new Message(
null,
messagePostRequest.getContent(),
messagePostRequest.getClientId(),
messagePostRequest.getTimeStamp());
this.webexService.post(model);
String id = this.messageService.save(model);
result.setSuccess(true);
result.setData(id);
return ResponseEntity.
status(HttpStatus.OK)
.body(result);
} catch (Exception e){
result.setMessage(e.getMessage());
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(result);
}
}
@GetMapping("/message/history")
public void history(){
}
}

View File

@@ -26,6 +26,7 @@ public class ClientController {
ResponseBody<String> result = ResponseBody.createEmpty();
try{
String clientId = service.create(dto.getCompanyName(), dto.getUsername(), dto.getKey());
result.setSuccess(true);
result.setData(clientId);
return ResponseEntity.
status(HttpStatus.OK)
@@ -37,4 +38,7 @@ public class ClientController {
.body(result);
}
}
}

View File

@@ -0,0 +1,33 @@
package be.naaturel.boardmateapi.controllers.dtos;
public class MessagePostRequestDto {
private String content;
private String clientId;
private int timeStamp;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public int getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(int timeStamp) {
this.timeStamp = timeStamp;
}
}

View File

@@ -0,0 +1,7 @@
package be.naaturel.boardmateapi.repository;
import be.naaturel.boardmateapi.repository.dtos.MessageDto;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface MessageRepo extends MongoRepository<MessageDto, String> {
}

View File

@@ -0,0 +1,12 @@
package be.naaturel.boardmateapi.repository;
import be.naaturel.boardmateapi.common.models.Room;
import be.naaturel.boardmateapi.repository.dtos.RoomDto;
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
import java.util.Optional;
public interface RoomRepo extends MongoRepository<RoomDto, String> {
Optional<RoomDto> findByClientId(String clientId);
}

View File

@@ -0,0 +1,53 @@
package be.naaturel.boardmateapi.repository.dtos;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@Document("messages")
public class MessageDto {
@Id
private String id;
@Field("clientId")
private String clientId;
@Field("content")
private String content;
@Field("timestamp")
private int timestamp;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getTimestamp() {
return timestamp;
}
public void setTimestamp(int timestamp) {
this.timestamp = timestamp;
}
}

View File

@@ -0,0 +1,55 @@
package be.naaturel.boardmateapi.repository.dtos;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@Document("rooms")
public class RoomDto {
@Id
private String id;
@Field("title")
private String title;
@Field("roomId")
private String roomId;
@Field("clientId")
@Indexed(unique = true)
private String clientId;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getRoomId() {
return roomId;
}
public void setRoomId(String roomId) {
this.roomId = roomId;
}
}

View File

@@ -0,0 +1,21 @@
package be.naaturel.boardmateapi.repository.mappings;
import be.naaturel.boardmateapi.common.models.Message;
import be.naaturel.boardmateapi.repository.dtos.MessageDto;
public class MessageMapper {
public static Message toModel(MessageDto dto){
return new Message(dto.getId(), dto.getContent(), dto.getClientId(), dto.getTimestamp());
}
public static MessageDto toDto(Message model){
MessageDto dto = new MessageDto();
dto.setId(model.getId());
dto.setContent(model.getContent());
dto.setClientId(model.getClientId());
dto.setTimestamp(model.getTimeStamp());
return dto;
}
}

View File

@@ -0,0 +1,19 @@
package be.naaturel.boardmateapi.repository.mappings;
import be.naaturel.boardmateapi.common.models.Room;
import be.naaturel.boardmateapi.repository.dtos.RoomDto;
public class RoomMapper {
public static Room toModel(RoomDto dto){
return new Room(dto.getRoomId(),dto.getTitle(), dto.getClientId());
}
public static RoomDto toDto(Room model){
RoomDto dto = new RoomDto();
dto.setTitle(model.getTitle());
dto.setClientId(model.getClientId());
dto.setRoomId(model.getId());
return dto;
}
}

View File

@@ -0,0 +1,31 @@
package be.naaturel.boardmateapi.services;
import be.naaturel.boardmateapi.common.exceptions.ServiceException;
import be.naaturel.boardmateapi.common.models.Message;
import be.naaturel.boardmateapi.repository.MessageRepo;
import be.naaturel.boardmateapi.repository.dtos.MessageDto;
import be.naaturel.boardmateapi.repository.mappings.MessageMapper;
import org.springframework.stereotype.Service;
@Service
public class MessageService {
private final MessageRepo repo;
public MessageService(MessageRepo repo){
this.repo = repo;
}
public String save(Message message) throws ServiceException {
try {
MessageDto dto = MessageMapper.toDto(message);
MessageDto result = repo.save(dto);
return result.getId();
} catch (Exception e){
throw new ServiceException("Failed to save message : " + e.getMessage(), e);
}
}
}

View File

@@ -0,0 +1,114 @@
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.Message;
import be.naaturel.boardmateapi.common.models.Room;
import be.naaturel.boardmateapi.repository.RoomRepo;
import be.naaturel.boardmateapi.repository.dtos.RoomDto;
import be.naaturel.boardmateapi.repository.mappings.RoomMapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
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.Map;
import java.util.Optional;
@Service
public class WebexService {
private final String botToken;
private final RoomRepo repo;
@Autowired
public WebexService(
RoomRepo repo,
@Qualifier("botToken") String botToken){
this.repo = repo;
this.botToken = botToken;
}
public void post(Message m) throws ServiceException {
try(HttpClient client = HttpClient.newHttpClient()) {
Room room = getClientRoom(m.getClientId());
if(room == null){
room = createRoom(m.getClientId());
}
ObjectMapper mapper = new ObjectMapper();
String jsonBody = mapper.writeValueAsString(
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());
}
} catch (Exception e) {
Logger.displayError(Arrays.toString(e.getStackTrace()));
throw new ServiceException("Failed to post message");
}
}
public Room createRoom(String clientId) throws ServiceException {
try (HttpClient client = HttpClient.newHttpClient()) {
ObjectMapper mapper = new ObjectMapper();
String jsonBody = mapper.writeValueAsString(Map.of("title", "Support"));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://webexapis.com/v1/rooms"))
.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());
}
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);
}
}
private Room getClientRoom(String clientId){
Optional<RoomDto> dto = repo.findByClientId(clientId);
return dto.map(RoomMapper::toModel).orElse(null);
}
}

View File

@@ -26,6 +26,10 @@ mqtt.client-id=board-mate-api
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
#=============METRICS=============
management.endpoint.health.show-details=always
management.endpoints.web.exposure.include=*