Implement Channel endpoints and services for managing channels in the server

This commit is contained in:
2026-04-20 15:43:43 -04:00
parent 24671fefef
commit 0ea6005e7b
10 changed files with 243 additions and 34 deletions

View File

@@ -0,0 +1,82 @@
package net.kpuig.shoebill.backend.controllers;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import net.kpuig.shoebill.backend.datamodels.channel.GetAllChannelsFromServerResponse;
import net.kpuig.shoebill.backend.datamodels.channel.GetChannelResponse;
import net.kpuig.shoebill.backend.datamodels.channel.PostChannelRequest;
import net.kpuig.shoebill.backend.datamodels.channel.PostChannelResponse;
import net.kpuig.shoebill.backend.services.ChannelService;
import net.kpuig.shoebill.backend.services.exceptions.NotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
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.RequestBody;
@RestController
@RequestMapping("/api")
public class ChannelController {
@Autowired private ChannelService channelService;
@Operation(summary = "Get all channels from a server", description = "Retrieves a list of all channels from a server by the server's ID")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Channels retrieved"),
@ApiResponse(responseCode = "404", description = "Server not found")
})
@GetMapping("/server/{serverId}/channel/")
public ResponseEntity<GetAllChannelsFromServerResponse> getAllChannelsFromServer(@PathVariable(name = "serverId") Long serverId) {
try {
var response = channelService.getAllChannelsFromServer(serverId);
return ResponseEntity.ok(response);
} catch (NotFoundException notFoundException) {
return ResponseEntity.notFound().build();
}
}
@Operation(summary = "Get a channel by ID", description = "Retrieves a channel by its ID")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Channel retrieved"),
@ApiResponse(responseCode = "404", description = "Channel not found")
})
@GetMapping("/channel/{channelId}")
public ResponseEntity<GetChannelResponse> getChannelById(@PathVariable(name = "channelId") Long channelId) {
try {
var response = channelService.getChannelById(channelId);
return ResponseEntity.ok(response);
} catch (NotFoundException notFoundException) {
return ResponseEntity.notFound().build();
}
}
@Operation(summary = "Create a new channel", description = "Creates a new channel with the provided name, position, type, and server ID")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "Channel created"),
@ApiResponse(responseCode = "400", description = "Invalid request")
})
@PostMapping("/server/{serverId}/channel")
public ResponseEntity<PostChannelResponse> createChannel(
@PathVariable(name = "serverId") Long serverId,
@RequestBody PostChannelRequest request) {
try {
var response = channelService.createChannel(serverId, request);
return ResponseEntity
.created(UriComponentsBuilder.fromPath("/api/channel/{id}")
.buildAndExpand(response.getId())
.toUri())
.body(response);
} catch (RuntimeException e) {
return ResponseEntity.badRequest().build();
}
}
}

View File

@@ -14,23 +14,25 @@ import net.kpuig.shoebill.backend.datamodels.server.PostServerRequest;
import net.kpuig.shoebill.backend.datamodels.server.PostServerResponse;
import net.kpuig.shoebill.backend.services.ServerService;
import net.kpuig.shoebill.backend.services.exceptions.BadRequestException;
import net.kpuig.shoebill.backend.services.exceptions.NotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
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.RequestBody;
@RestController
@RequestMapping("/api/server")
@RequestMapping("/api")
public class ServerController {
@Autowired private ServerService serverService;
@Operation(summary = "Get all servers", description = "Retrieves a list of all servers")
@ApiResponse(responseCode = "200", description = "Servers retrieved")
@GetMapping("/")
@GetMapping("/server")
public ResponseEntity<GetAllServersResponse> getAllServers() {
return ResponseEntity.ok(serverService.getAllServers());
}
@@ -40,11 +42,11 @@ public class ServerController {
@ApiResponse(responseCode = "200", description = "Server retrieved"),
@ApiResponse(responseCode = "404", description = "Server not found")
})
@GetMapping("")
public ResponseEntity<GetServerResponse> getServerById(@RequestParam(name = "id", required = false) Long id) {
@GetMapping("/server/{id}")
public ResponseEntity<GetServerResponse> getServerById(@PathVariable(name = "id") Long id) {
try {
return ResponseEntity.ok(serverService.getServerById(id));
} catch (Exception e) {
} catch (NotFoundException e) {
return ResponseEntity.notFound().build();
}
}
@@ -54,7 +56,7 @@ public class ServerController {
@ApiResponse(responseCode = "201", description = "Server created"),
@ApiResponse(responseCode = "400", description = "Invalid request")
})
@PostMapping("/")
@PostMapping("/server")
public ResponseEntity<PostServerResponse> createNewServer(@Valid @RequestBody PostServerRequest request) {
try {
var response = serverService.createServer(request);

View File

@@ -5,6 +5,7 @@ import java.net.URI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -23,9 +24,10 @@ import net.kpuig.shoebill.backend.datamodels.userprofile.PostUserProfileRequest;
import net.kpuig.shoebill.backend.datamodels.userprofile.PostUserProfileResponse;
import net.kpuig.shoebill.backend.services.UserProfileService;
import net.kpuig.shoebill.backend.services.exceptions.BadRequestException;
import net.kpuig.shoebill.backend.services.exceptions.NotFoundException;
@RestController
@RequestMapping("/api/user-profile")
@RequestMapping("/api")
public class UserProfileController {
@Autowired
private UserProfileService userProfileService;
@@ -33,46 +35,45 @@ public class UserProfileController {
@Operation(summary = "Get all user profiles", description = "Retrieves a list of all user profiles")
@ApiResponse(responseCode = "200", description = "User profiles retrieved",
content = { @Content(mediaType = "application/json") })
@GetMapping("/")
@GetMapping("/user-profile")
public ResponseEntity<GetAllUserProfilesResponse> getAllUserProfiles() {
return ResponseEntity.ok(userProfileService.getAllUserProfiles());
}
@Operation(summary = "Get user profile by ID or username", description = "Retrieves a user profile by its ID or username")
@Operation(summary = "Get user profile by ID", description = "Retrieves a user profile by its ID")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "User profile retrieved",
content = { @Content(mediaType = "application/json") }),
@ApiResponse(responseCode = "404", description = "User profile not found")
})
@GetMapping("")
public ResponseEntity<GetUserProfileResponse> getUserProfileById(
@RequestParam(name = "id", required = false) Long id,
@RequestParam(name = "username", required = false) String username) {
if (id == null && username == null) { // Must provide at least one of them
return ResponseEntity.badRequest().build();
} else if (id != null && username != null) { // Both have been provided
return ResponseEntity.badRequest().build();
} else if (id != null) { // ID has been provided
@GetMapping("/user-profile/{id}")
public ResponseEntity<GetUserProfileResponse> getUserProfileById(@PathVariable(name = "id") Long id) {
try {
var response = userProfileService.getUserProfileById(id);
return ResponseEntity.ok(response);
} catch (NumberFormatException e) {
return ResponseEntity.ok(userProfileService.getUserProfileById(id));
} catch (NotFoundException e) {
return ResponseEntity.notFound().build();
}
} else { // Username has been provided
try {
var response = userProfileService.getUserProfileByUsername(username);
return ResponseEntity.ok(response);
} catch (NumberFormatException e) {
return ResponseEntity.notFound().build();
}
@Operation(summary = "Get user profile by username", description = "Retrieves a user profile by its username")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "User profile retrieved",
content = { @Content(mediaType = "application/json") }),
@ApiResponse(responseCode = "404", description = "User profile not found")
})
@GetMapping("/user-profile/")
public ResponseEntity<GetUserProfileResponse> getUserProfileByUsername(@RequestParam(name = "username") String username) {
try {
return ResponseEntity.ok(userProfileService.getUserProfileByUsername(username));
} catch (NotFoundException e) {
return ResponseEntity.notFound().build();
}
}
@Operation(summary = "Create user profile", description = "Creates a new user profile")
@ApiResponse(responseCode = "201", description = "User profile created",
content = { @Content(mediaType = "application/json") })
@PostMapping("/")
@PostMapping("/user-profile")
public ResponseEntity<PostUserProfileResponse> createUserProfile(@RequestBody @Valid PostUserProfileRequest request) {
try {
var response = userProfileService.createUserProfile(request.getUsername());

View File

@@ -0,0 +1,10 @@
package net.kpuig.shoebill.backend.datamodels.channel;
import java.util.List;
import lombok.Data;
@Data
public class GetAllChannelsFromServerResponse {
private List<GetChannelResponse> channels;
}

View File

@@ -0,0 +1,11 @@
package net.kpuig.shoebill.backend.datamodels.channel;
import lombok.Data;
@Data
public class GetChannelResponse {
private Long id;
private Long position;
private ChannelType type;
private String name;
}

View File

@@ -0,0 +1,17 @@
package net.kpuig.shoebill.backend.datamodels.channel;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
public class PostChannelRequest {
@NotNull
private Long position;
@NotNull
private ChannelType type;
@NotBlank
private String name;
}

View File

@@ -0,0 +1,11 @@
package net.kpuig.shoebill.backend.datamodels.channel;
import lombok.Data;
@Data
public class PostChannelResponse {
private Long id;
private Long position;
private ChannelType type;
private String name;
}

View File

@@ -1,5 +1,7 @@
package net.kpuig.shoebill.backend.repositories;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@@ -8,4 +10,6 @@ import net.kpuig.shoebill.backend.datamodels.channel.Channel;
@Repository
public interface ChannelRepository extends JpaRepository<Channel, Long> {
List<Channel> findAllByServerId(Long serverId);
}

View File

@@ -0,0 +1,70 @@
package net.kpuig.shoebill.backend.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import net.kpuig.shoebill.backend.datamodels.channel.Channel;
import net.kpuig.shoebill.backend.datamodels.channel.GetAllChannelsFromServerResponse;
import net.kpuig.shoebill.backend.datamodels.channel.GetChannelResponse;
import net.kpuig.shoebill.backend.datamodels.channel.PostChannelRequest;
import net.kpuig.shoebill.backend.datamodels.channel.PostChannelResponse;
import net.kpuig.shoebill.backend.datamodels.server.Server;
import net.kpuig.shoebill.backend.repositories.ChannelRepository;
import net.kpuig.shoebill.backend.repositories.ServerRepository;
import net.kpuig.shoebill.backend.services.exceptions.NotFoundException;
@Service
public class ChannelService {
@Autowired private ChannelRepository channelRepository;
@Autowired private ServerRepository serverRepository;
public GetAllChannelsFromServerResponse getAllChannelsFromServer(Long serverId) {
if (!serverRepository.existsById(serverId)) {
throw new NotFoundException("Server not found");
}
var channels = channelRepository.findAllByServerId(serverId);
var response = new GetAllChannelsFromServerResponse();
response.setChannels(channels.stream().map(channel -> {
var channelResponse = new GetChannelResponse();
channelResponse.setId(channel.getId());
channelResponse.setName(channel.getName());
channelResponse.setPosition(channel.getPosition());
channelResponse.setType(channel.getType());
return channelResponse;
}).toList());
return response;
}
public GetChannelResponse getChannelById(Long channelId) {
var channel = channelRepository.findById(channelId).orElseThrow();
var response = new GetChannelResponse();
response.setId(channel.getId());
response.setName(channel.getName());
response.setPosition(channel.getPosition());
response.setType(channel.getType());
return response;
}
public PostChannelResponse createChannel(Long serverId, PostChannelRequest request) {
Server server;
try {
server = serverRepository.findById(serverId).orElseThrow();
} catch (Exception e) {
throw new RuntimeException("Server not found");
}
var channel = new Channel();
channel.setName(request.getName());
channel.setPosition(request.getPosition());
channel.setType(request.getType());
channel.setServer(server);
channel = channelRepository.save(channel);
var response = new PostChannelResponse();
response.setId(channel.getId());
response.setName(channel.getName());
response.setPosition(channel.getPosition());
response.setType(channel.getType());
return response;
}
}

View File

@@ -9,6 +9,7 @@ import net.kpuig.shoebill.backend.datamodels.userprofile.PostUserProfileResponse
import net.kpuig.shoebill.backend.datamodels.userprofile.UserProfile;
import net.kpuig.shoebill.backend.repositories.UserProfileRepository;
import net.kpuig.shoebill.backend.services.exceptions.BadRequestException;
import net.kpuig.shoebill.backend.services.exceptions.NotFoundException;
@Service
public class UserProfileService {
@@ -32,7 +33,7 @@ public class UserProfileService {
response.setId(userProfile.getId());
response.setUsername(userProfile.getUsername());
}, () -> {
throw new net.kpuig.shoebill.backend.services.exceptions.NotFoundException("User profile not found");
throw new NotFoundException("User profile not found");
});
return response;
}
@@ -43,7 +44,7 @@ public class UserProfileService {
response.setId(userProfile.getId());
response.setUsername(userProfile.getUsername());
}, () -> {
throw new net.kpuig.shoebill.backend.services.exceptions.NotFoundException("User profile not found");
throw new NotFoundException("User profile not found");
});
return response;
}