From e0799828d074d601d0cf6b768ebed5eb8f290ab7 Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Tue, 9 Dec 2025 02:24:40 -0300 Subject: [PATCH 1/3] Implement trackhasgenre relationship management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add complete implementation for track-genre relationship management, following the established albumhasgenre pattern. This enables tracking which genres are associated with each track in the media library. Changes: - Fix trackhasgenre.proto: Correct CreateTrackHasGenreRequest to use fk_track_id instead of fk_album_id - Enhance TrackHasGenre model with nullable constraints, constructor, and toString method - Implement TrackHasGenreRepository with full CRUD operations - Implement TrackHasGenreService with business logic and validation - Add TrackHasGenreMapper for entity/protobuf conversion - Create action handlers: * CreateTrackHasGenreHandler (trackhasgenre.create) * DeleteTrackHasGenreHandler (trackhasgenre.delete) * GetTrackHasGenreByIdHandler (trackhasgenre.getById) * GetTrackHasGenreHandler (trackhasgenre.getAll) - Register TrackHasGenreService in DelegateActionManager for automatic handler discovery and dependency injection The implementation validates track and genre existence before creating relationships and provides proper error handling with appropriate HTTP status codes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../mapper/TrackHasGenreMapper.java | 47 ++++++++++ .../com/mediamanager/model/TrackHasGenre.java | 55 ++++++++++++ .../repository/TrackHasGenreRepository.java | 85 +++++++++++++++++++ .../delegate/DelegateActionManager.java | 5 ++ .../CreateTrackHasGenreHandler.java | 54 ++++++++++++ .../DeleteTrackHasGenreHandler.java | 62 ++++++++++++++ .../GetTrackHasGenreByIdHandler.java | 56 ++++++++++++ .../GetTrackHasGenreHandler.java | 48 +++++++++++ .../trackhasgenre/TrackHasGenreService.java | 77 +++++++++++++++++ src/main/proto/trackhasgenre.proto | 46 ++++++++++ 10 files changed, 535 insertions(+) create mode 100644 src/main/java/com/mediamanager/mapper/TrackHasGenreMapper.java create mode 100644 src/main/java/com/mediamanager/model/TrackHasGenre.java create mode 100644 src/main/java/com/mediamanager/repository/TrackHasGenreRepository.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/CreateTrackHasGenreHandler.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/DeleteTrackHasGenreHandler.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/GetTrackHasGenreByIdHandler.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/GetTrackHasGenreHandler.java create mode 100644 src/main/java/com/mediamanager/service/trackhasgenre/TrackHasGenreService.java create mode 100644 src/main/proto/trackhasgenre.proto diff --git a/src/main/java/com/mediamanager/mapper/TrackHasGenreMapper.java b/src/main/java/com/mediamanager/mapper/TrackHasGenreMapper.java new file mode 100644 index 0000000..e3e6968 --- /dev/null +++ b/src/main/java/com/mediamanager/mapper/TrackHasGenreMapper.java @@ -0,0 +1,47 @@ +package com.mediamanager.mapper; + +import com.mediamanager.model.TrackHasGenre; +import com.mediamanager.protocol.messages.TrackHasGenreMessages; + +public class TrackHasGenreMapper { + public static TrackHasGenreMessages.TrackHasGenre toProtobuf(TrackHasGenre entity) { + if (entity == null) { + return null; + } + + TrackHasGenreMessages.TrackHasGenre.Builder builder = TrackHasGenreMessages.TrackHasGenre.newBuilder(); + + Integer id = entity.getId(); + if (id != null) { + builder.setId(id); + } + + // Map Track foreign key + if (entity.getTrack() != null && entity.getTrack().getId() != null) { + builder.setFkTrackId(entity.getTrack().getId()); + } + + // Map Genre foreign key + if (entity.getGenre() != null && entity.getGenre().getId() != null) { + builder.setFkGenreId(entity.getGenre().getId()); + } + + return builder.build(); + } + + public static TrackHasGenre toEntity(TrackHasGenreMessages.TrackHasGenre protobuf) { + if (protobuf == null) { + return null; + } + + TrackHasGenre entity = new TrackHasGenre(); + + if (protobuf.getId() > 0) { + entity.setId(protobuf.getId()); + } + + // Note: Foreign key relationships (Track, Genre) are handled in the service layer + + return entity; + } +} diff --git a/src/main/java/com/mediamanager/model/TrackHasGenre.java b/src/main/java/com/mediamanager/model/TrackHasGenre.java new file mode 100644 index 0000000..e2e6717 --- /dev/null +++ b/src/main/java/com/mediamanager/model/TrackHasGenre.java @@ -0,0 +1,55 @@ +package com.mediamanager.model; + +import jakarta.persistence.*; + +@Entity +@Table(name = "trackhasgenre") +public class TrackHasGenre { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "fk_track_id", nullable = false) + private Track track; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "fk_genre_id", nullable = false) + private Genre genre; + + public TrackHasGenre() {} + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Track getTrack() { + return track; + } + + public void setTrack(Track track) { + this.track = track; + } + + public Genre getGenre() { + return genre; + } + + public void setGenre(Genre genre) { + this.genre = genre; + } + + @Override + public String toString() { + return "TrackHasGenre{" + + "id=" + id + + ", trackId=" + (track != null ? track.getId() : null) + + ", genreId=" + (genre != null ? genre.getId() : null) + + '}'; + } +} diff --git a/src/main/java/com/mediamanager/repository/TrackHasGenreRepository.java b/src/main/java/com/mediamanager/repository/TrackHasGenreRepository.java new file mode 100644 index 0000000..6437e04 --- /dev/null +++ b/src/main/java/com/mediamanager/repository/TrackHasGenreRepository.java @@ -0,0 +1,85 @@ +package com.mediamanager.repository; + + +import com.mediamanager.model.TrackHasGenre; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + + +import java.util.List; +import java.util.Optional; + +public class TrackHasGenreRepository { + private static final Logger logger = LogManager.getLogger(TrackHasGenreRepository.class); + + private final EntityManagerFactory entityManagerFactory; + + public TrackHasGenreRepository(EntityManagerFactory entityManagerFactory) { + this.entityManagerFactory = entityManagerFactory; + } + + public TrackHasGenre save(TrackHasGenre trackHasGenre) { + logger.debug("Saving TrackHasGenre: {}", trackHasGenre); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + try { + em.persist(trackHasGenre); + em.getTransaction().commit(); + logger.debug("TrackHasGenre has been saved successfully"); + return trackHasGenre; + } catch (Exception e) { + em.getTransaction().rollback(); + logger.error("Error while saving TrackHasGenre: {}", e.getMessage()); + throw e; + } finally { + if (em.isOpen()) em.close(); + } + } + + public List findAll() { + logger.debug("Finding All TrackHasGenre"); + EntityManager em = entityManagerFactory.createEntityManager(); + try{ + return em.createQuery("select t from TrackHasGenre t", TrackHasGenre.class).getResultList(); + }finally { + if (em.isOpen()) em.close(); + } + } + + public Optional findById(Integer id) { + logger.debug("Finding TrackHasGenre with id: {}", id); + EntityManager em = entityManagerFactory.createEntityManager(); + try{ + TrackHasGenre trackHasGenre = em.find(TrackHasGenre.class, id); + return Optional.ofNullable(trackHasGenre); + }finally { + if (em.isOpen()) em.close(); + } + } + + public boolean deleteById(Integer id){ + logger.debug("Deleting TrackHasGenre with id: {}", id); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + try{ + TrackHasGenre trackHasGenre = em.find(TrackHasGenre.class, id); + if (trackHasGenre == null) { + em.getTransaction().rollback(); + return false; + } + em.remove(trackHasGenre); + em.getTransaction().commit(); + logger.debug("TrackHasGenre has been deleted successfully"); + return true; + } catch (Exception e) { + em.getTransaction().rollback(); + logger.error("Error while deleting TrackHasGenre: {}", e.getMessage()); + throw e; + } finally { + if (em.isOpen()) em.close(); + } + } + +} diff --git a/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java b/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java index 11c3354..da043f5 100644 --- a/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java +++ b/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java @@ -12,6 +12,7 @@ import com.mediamanager.service.albumtype.AlbumTypeService; import com.mediamanager.service.bitdepth.BitDepthService; import com.mediamanager.service.bitrate.BitRateService; import com.mediamanager.service.composer.ComposerService; +import com.mediamanager.service.trackhasgenre.TrackHasGenreService; import com.mediamanager.repository.GenreRepository; import com.mediamanager.service.artist.ArtistService; @@ -112,6 +113,10 @@ public class DelegateActionManager { TrackService trackService = new TrackService(trackRepository, discRepository, composerRepository, bitDepthRepository, bitRateRepository, samplingRateRepository); serviceLocator.register(TrackService.class, trackService); + TrackHasGenreRepository trackHasGenreRepository = new TrackHasGenreRepository(entityManagerFactory); + TrackHasGenreService trackHasGenreService = new TrackHasGenreService(trackHasGenreRepository, trackRepository, genreRepository); + serviceLocator.register(TrackHasGenreService.class, trackHasGenreService); + serviceLocator.logRegisteredServices(); logger.info("Services initialized successfully"); diff --git a/src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/CreateTrackHasGenreHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/CreateTrackHasGenreHandler.java new file mode 100644 index 0000000..e8b9d09 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/CreateTrackHasGenreHandler.java @@ -0,0 +1,54 @@ +package com.mediamanager.service.delegate.handler.trackhasgenre; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.TrackHasGenreMapper; +import com.mediamanager.model.TrackHasGenre; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.TrackHasGenreMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.delegate.annotation.Action; +import com.mediamanager.service.trackhasgenre.TrackHasGenreService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Action("trackhasgenre.create") +public class CreateTrackHasGenreHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(CreateTrackHasGenreHandler.class); + private final TrackHasGenreService trackHasGenreService; + + public CreateTrackHasGenreHandler(TrackHasGenreService trackHasGenreService) { + this.trackHasGenreService = trackHasGenreService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) throws InvalidProtocolBufferException { + try{ + TrackHasGenreMessages.CreateTrackHasGenreRequest createRequest = + TrackHasGenreMessages.CreateTrackHasGenreRequest.parseFrom(requestPayload); + + TrackHasGenre trackHasGenre = trackHasGenreService.createTrackHasGenre( + createRequest.getFkTrackId() > 0 ? createRequest.getFkTrackId() : null, + createRequest.getFkGenreId() > 0 ? createRequest.getFkGenreId() : null + ); + + TrackHasGenreMessages.TrackHasGenre trackHasGenreProto = TrackHasGenreMapper.toProtobuf(trackHasGenre); + TrackHasGenreMessages.CreateTrackHasGenreResponse createTrackHasGenreResponse = TrackHasGenreMessages.CreateTrackHasGenreResponse.newBuilder() + .setTrackhasgenre(trackHasGenreProto) + .build(); + return TransportProtocol.Response.newBuilder() + .setPayload(createTrackHasGenreResponse.toByteString()); + } catch (IllegalArgumentException e) { + logger.error("Validation error", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(400) + .setPayload(ByteString.copyFromUtf8("Validation error: " + e.getMessage())); + + } catch (Exception e) { + logger.error("Error creating track has genre", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/DeleteTrackHasGenreHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/DeleteTrackHasGenreHandler.java new file mode 100644 index 0000000..17c01de --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/DeleteTrackHasGenreHandler.java @@ -0,0 +1,62 @@ +package com.mediamanager.service.delegate.handler.trackhasgenre; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.TrackHasGenreMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.delegate.annotation.Action; +import com.mediamanager.service.trackhasgenre.TrackHasGenreService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Action("trackhasgenre.delete") +public class DeleteTrackHasGenreHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(DeleteTrackHasGenreHandler.class); + + private final TrackHasGenreService trackHasGenreService; + + public DeleteTrackHasGenreHandler(TrackHasGenreService trackHasGenreService) { + this.trackHasGenreService = trackHasGenreService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + + try { + TrackHasGenreMessages.DeleteTrackHasGenreRequest deleteRequest = + TrackHasGenreMessages.DeleteTrackHasGenreRequest.parseFrom(requestPayload); + int id = deleteRequest.getId(); + boolean success = trackHasGenreService.deleteTrackHasGenre(id); + TrackHasGenreMessages.DeleteTrackHasGenreResponse deleteResponse; + if (success) { + deleteResponse = TrackHasGenreMessages.DeleteTrackHasGenreResponse.newBuilder() + .setSuccess(true) + .setMessage("Track has genre deleted successfully") + .build(); + return TransportProtocol.Response.newBuilder() + .setPayload(deleteResponse.toByteString()); + } else { + deleteResponse = TrackHasGenreMessages.DeleteTrackHasGenreResponse.newBuilder() + .setSuccess(false) + .setMessage("Track has genre not found") + .build(); + + return TransportProtocol.Response.newBuilder() + .setStatusCode(404) + .setPayload(deleteResponse.toByteString()); + } + } catch (Exception e) { + logger.error("Error deleting track has genre", e); + TrackHasGenreMessages.DeleteTrackHasGenreResponse deleteResponse = + TrackHasGenreMessages.DeleteTrackHasGenreResponse.newBuilder() + .setSuccess(false) + .setMessage("Error: " + e.getMessage()) + .build(); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(deleteResponse.toByteString()); + } + } + } diff --git a/src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/GetTrackHasGenreByIdHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/GetTrackHasGenreByIdHandler.java new file mode 100644 index 0000000..6a0c5d6 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/GetTrackHasGenreByIdHandler.java @@ -0,0 +1,56 @@ +package com.mediamanager.service.delegate.handler.trackhasgenre; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.TrackHasGenreMapper; +import com.mediamanager.model.TrackHasGenre; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.TrackHasGenreMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.delegate.annotation.Action; +import com.mediamanager.service.trackhasgenre.TrackHasGenreService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Optional; + +@Action(value = "trackhasgenre.getById") +public class GetTrackHasGenreByIdHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(GetTrackHasGenreByIdHandler.class); + private final TrackHasGenreService trackHasGenreService; + + public GetTrackHasGenreByIdHandler(TrackHasGenreService trackHasGenreService) { + this.trackHasGenreService = trackHasGenreService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException{ + + try{ + TrackHasGenreMessages.GetTrackHasGenreByIdRequest getByIdRequest = + TrackHasGenreMessages.GetTrackHasGenreByIdRequest.parseFrom(requestPayload); + int id = getByIdRequest.getId(); + + Optional trackHasGenreOpt = trackHasGenreService.getTrackHasGenreById(id); + + if (trackHasGenreOpt.isEmpty()){ + logger.warn("TrackHasGenre not found with ID: {}", id); + return TransportProtocol.Response.newBuilder() + .setStatusCode(404) + .setPayload(ByteString.copyFromUtf8("TrackHasGenre not found")); + } + TrackHasGenreMessages.TrackHasGenre trackHasGenreProto = TrackHasGenreMapper.toProtobuf(trackHasGenreOpt.get()); + TrackHasGenreMessages.GetTrackHasGenreByIdResponse getByIdResponse = TrackHasGenreMessages.GetTrackHasGenreByIdResponse.newBuilder() + .setTrackhasgenre(trackHasGenreProto) + .build(); + return TransportProtocol.Response.newBuilder() + .setPayload(getByIdResponse.toByteString()); + } catch (Exception e) { + logger.error("Error getting track has genre by ID", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: "+ e.getMessage())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/GetTrackHasGenreHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/GetTrackHasGenreHandler.java new file mode 100644 index 0000000..35b43f5 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/trackhasgenre/GetTrackHasGenreHandler.java @@ -0,0 +1,48 @@ +package com.mediamanager.service.delegate.handler.trackhasgenre; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.TrackHasGenreMapper; +import com.mediamanager.model.TrackHasGenre; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.TrackHasGenreMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.delegate.annotation.Action; +import com.mediamanager.service.trackhasgenre.TrackHasGenreService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; + + +@Action("trackhasgenre.getAll") +public class GetTrackHasGenreHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(GetTrackHasGenreHandler.class); + + private final TrackHasGenreService trackHasGenreService; + + public GetTrackHasGenreHandler(TrackHasGenreService trackHasGenreService){this.trackHasGenreService = trackHasGenreService;} + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) throws InvalidProtocolBufferException { + try{ + List trackHasGenres = trackHasGenreService.getAllTrackHasGenres(); + TrackHasGenreMessages.GetTrackHasGenresResponse.Builder responseBuilder = TrackHasGenreMessages.GetTrackHasGenresResponse.newBuilder(); + + for (TrackHasGenre trackHasGenre : trackHasGenres) { + TrackHasGenreMessages.TrackHasGenre trackHasGenreProto = TrackHasGenreMapper.toProtobuf(trackHasGenre); + responseBuilder.addTrackhasgenre(trackHasGenreProto); + } + TrackHasGenreMessages.GetTrackHasGenresResponse getTrackHasGenresResponse = responseBuilder.build(); + + return TransportProtocol.Response.newBuilder() + .setPayload(getTrackHasGenresResponse.toByteString()); + + }catch (Exception e){ + logger.error("Error getting track has genres", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/trackhasgenre/TrackHasGenreService.java b/src/main/java/com/mediamanager/service/trackhasgenre/TrackHasGenreService.java new file mode 100644 index 0000000..82b97f3 --- /dev/null +++ b/src/main/java/com/mediamanager/service/trackhasgenre/TrackHasGenreService.java @@ -0,0 +1,77 @@ +package com.mediamanager.service.trackhasgenre; + +import com.mediamanager.model.Track; +import com.mediamanager.model.TrackHasGenre; +import com.mediamanager.model.Genre; +import com.mediamanager.repository.TrackHasGenreRepository; +import com.mediamanager.repository.TrackRepository; +import com.mediamanager.repository.GenreRepository; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; +import java.util.Optional; + +public class TrackHasGenreService { + private static final Logger logger = LogManager.getLogger(TrackHasGenreService.class); + private final TrackHasGenreRepository repository; + private final TrackRepository trackRepository; + private final GenreRepository genreRepository; + + public TrackHasGenreService(TrackHasGenreRepository repository, TrackRepository trackRepository, GenreRepository genreRepository) { + this.repository = repository; + this.trackRepository = trackRepository; + this.genreRepository = genreRepository; + } + + public TrackHasGenre createTrackHasGenre(Integer trackId, Integer genreId) { + logger.debug("Creating track has genre relationship - trackId:{}, genreId:{}", trackId, genreId); + + if (trackId == null || trackId <= 0) { + throw new IllegalArgumentException("Track ID cannot be null or invalid"); + } + if (genreId == null || genreId <= 0) { + throw new IllegalArgumentException("Genre ID cannot be null or invalid"); + } + + // Verify Track exists + Optional track = trackRepository.findById(trackId); + if (track.isEmpty()) { + throw new IllegalArgumentException("Track not found with id: " + trackId); + } + + // Verify Genre exists + Optional genre = genreRepository.findById(genreId); + if (genre.isEmpty()) { + throw new IllegalArgumentException("Genre not found with id: " + genreId); + } + + TrackHasGenre trackHasGenre = new TrackHasGenre(); + trackHasGenre.setTrack(track.get()); + trackHasGenre.setGenre(genre.get()); + + return repository.save(trackHasGenre); + } + + public List getAllTrackHasGenres() { + logger.info("Getting all track has genre relationships"); + return repository.findAll(); + } + + public Optional getTrackHasGenreById(Integer id) { + if (id == null) { + throw new IllegalArgumentException("ID cannot be null"); + } + logger.info("Getting track has genre by id:{}", id); + return repository.findById(id); + } + + public boolean deleteTrackHasGenre(Integer id) { + if (id == null) { + throw new IllegalArgumentException("Track has genre id cannot be null"); + } + logger.info("Deleting track has genre:{}", id); + return repository.deleteById(id); + } + +} diff --git a/src/main/proto/trackhasgenre.proto b/src/main/proto/trackhasgenre.proto new file mode 100644 index 0000000..e483697 --- /dev/null +++ b/src/main/proto/trackhasgenre.proto @@ -0,0 +1,46 @@ +syntax = "proto3"; + +option java_package = "com.mediamanager.protocol.messages"; +option java_outer_classname = "TrackHasGenreMessages"; + +package mediamanager.messages; + +message TrackHasGenre{ + int32 id = 1; + int32 fk_track_id = 2; + int32 fk_genre_id =3; +} + +message CreateTrackHasGenreRequest { + int32 fk_track_id = 1; + int32 fk_genre_id = 2; +} + +message CreateTrackHasGenreResponse { + TrackHasGenre trackhasgenre = 1; +} + +message GetTrackHasGenresRequest { + +} + +message GetTrackHasGenresResponse { + repeated TrackHasGenre trackhasgenre = 1; +} + +message GetTrackHasGenreByIdRequest { + int32 id = 1; +} + +message GetTrackHasGenreByIdResponse { + TrackHasGenre trackhasgenre = 1; +} + +message DeleteTrackHasGenreRequest { + int32 id = 1; +} + +message DeleteTrackHasGenreResponse { + bool success = 1; + string message = 2; +} \ No newline at end of file From 7fd3c3e9f78da8b9ccc8a4e225478eb31be674c0 Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Tue, 9 Dec 2025 02:44:57 -0300 Subject: [PATCH 2/3] Implement trackhasartist relationship management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add complete implementation for track-artist relationship management, following the established albumhasgenre pattern. This enables tracking which artists are associated with each track in the media library. Changes: - Fix TrackHasArtist model: Rename from TrackHasArtistt.java (typo) and correct table name from "trackhasgenre" to "trackhasartist" - Implement TrackHasArtistRepository with full CRUD operations - Implement TrackHasArtistService with business logic and validation - Add TrackHasArtistMapper for entity/protobuf conversion - Create action handlers: * CreateTrackHasArtistHandler (trackhasartist.create) * DeleteTrackHasArtistHandler (trackhasartist.delete) * GetTrackHasArtistByIdHandler (trackhasartist.getById) * GetTrackHasArtistHandler (trackhasartist.getAll) - Register TrackHasArtistService in DelegateActionManager for automatic handler discovery and dependency injection - Proto file (trackhasartist.proto) was already correct The implementation validates track and artist existence before creating relationships and provides proper error handling with appropriate HTTP status codes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../mapper/TrackHasArtistMapper.java | 47 ++++++++++ .../mediamanager/model/TrackHasArtist.java | 55 ++++++++++++ .../repository/TrackHasArtistRepository.java | 85 +++++++++++++++++++ .../delegate/DelegateActionManager.java | 5 ++ .../CreateTrackHasArtistHandler.java | 54 ++++++++++++ .../DeleteTrackHasArtistHandler.java | 62 ++++++++++++++ .../GetTrackHasArtistByIdHandler.java | 56 ++++++++++++ .../GetTrackHasArtistHandler.java | 48 +++++++++++ .../trackhasartist/TrackHasArtistService.java | 77 +++++++++++++++++ src/main/proto/trackhasartist.proto | 46 ++++++++++ 10 files changed, 535 insertions(+) create mode 100644 src/main/java/com/mediamanager/mapper/TrackHasArtistMapper.java create mode 100644 src/main/java/com/mediamanager/model/TrackHasArtist.java create mode 100644 src/main/java/com/mediamanager/repository/TrackHasArtistRepository.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/CreateTrackHasArtistHandler.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/DeleteTrackHasArtistHandler.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/GetTrackHasArtistByIdHandler.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/GetTrackHasArtistHandler.java create mode 100644 src/main/java/com/mediamanager/service/trackhasartist/TrackHasArtistService.java create mode 100644 src/main/proto/trackhasartist.proto diff --git a/src/main/java/com/mediamanager/mapper/TrackHasArtistMapper.java b/src/main/java/com/mediamanager/mapper/TrackHasArtistMapper.java new file mode 100644 index 0000000..f8a0d20 --- /dev/null +++ b/src/main/java/com/mediamanager/mapper/TrackHasArtistMapper.java @@ -0,0 +1,47 @@ +package com.mediamanager.mapper; + +import com.mediamanager.model.TrackHasArtist; +import com.mediamanager.protocol.messages.TrackHasArtistMessages; + +public class TrackHasArtistMapper { + public static TrackHasArtistMessages.TrackHasArtist toProtobuf(TrackHasArtist entity) { + if (entity == null) { + return null; + } + + TrackHasArtistMessages.TrackHasArtist.Builder builder = TrackHasArtistMessages.TrackHasArtist.newBuilder(); + + Integer id = entity.getId(); + if (id != null) { + builder.setId(id); + } + + // Map Track foreign key + if (entity.getTrack() != null && entity.getTrack().getId() != null) { + builder.setFkTrackId(entity.getTrack().getId()); + } + + // Map Artist foreign key + if (entity.getArtist() != null && entity.getArtist().getId() != null) { + builder.setFkArtistId(entity.getArtist().getId()); + } + + return builder.build(); + } + + public static TrackHasArtist toEntity(TrackHasArtistMessages.TrackHasArtist protobuf) { + if (protobuf == null) { + return null; + } + + TrackHasArtist entity = new TrackHasArtist(); + + if (protobuf.getId() > 0) { + entity.setId(protobuf.getId()); + } + + // Note: Foreign key relationships (Track, Artist) are handled in the service layer + + return entity; + } +} diff --git a/src/main/java/com/mediamanager/model/TrackHasArtist.java b/src/main/java/com/mediamanager/model/TrackHasArtist.java new file mode 100644 index 0000000..abba89e --- /dev/null +++ b/src/main/java/com/mediamanager/model/TrackHasArtist.java @@ -0,0 +1,55 @@ +package com.mediamanager.model; + +import jakarta.persistence.*; + +@Entity +@Table(name = "trackhasartist") +public class TrackHasArtist { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "fk_track_id", nullable = false) + private Track track; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "fk_artist_id", nullable = false) + private Artist artist; + + public TrackHasArtist() {} + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Track getTrack() { + return track; + } + + public void setTrack(Track track) { + this.track = track; + } + + public Artist getArtist() { + return artist; + } + + public void setArtist(Artist artist) { + this.artist = artist; + } + + @Override + public String toString() { + return "TrackHasArtist{" + + "id=" + id + + ", trackId=" + (track != null ? track.getId() : null) + + ", artistId=" + (artist != null ? artist.getId() : null) + + '}'; + } +} diff --git a/src/main/java/com/mediamanager/repository/TrackHasArtistRepository.java b/src/main/java/com/mediamanager/repository/TrackHasArtistRepository.java new file mode 100644 index 0000000..1d81f37 --- /dev/null +++ b/src/main/java/com/mediamanager/repository/TrackHasArtistRepository.java @@ -0,0 +1,85 @@ +package com.mediamanager.repository; + + +import com.mediamanager.model.TrackHasArtist; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + + +import java.util.List; +import java.util.Optional; + +public class TrackHasArtistRepository { + private static final Logger logger = LogManager.getLogger(TrackHasArtistRepository.class); + + private final EntityManagerFactory entityManagerFactory; + + public TrackHasArtistRepository(EntityManagerFactory entityManagerFactory) { + this.entityManagerFactory = entityManagerFactory; + } + + public TrackHasArtist save(TrackHasArtist trackHasArtist) { + logger.debug("Saving TrackHasArtist: {}", trackHasArtist); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + try { + em.persist(trackHasArtist); + em.getTransaction().commit(); + logger.debug("TrackHasArtist has been saved successfully"); + return trackHasArtist; + } catch (Exception e) { + em.getTransaction().rollback(); + logger.error("Error while saving TrackHasArtist: {}", e.getMessage()); + throw e; + } finally { + if (em.isOpen()) em.close(); + } + } + + public List findAll() { + logger.debug("Finding All TrackHasArtist"); + EntityManager em = entityManagerFactory.createEntityManager(); + try{ + return em.createQuery("select t from TrackHasArtist t", TrackHasArtist.class).getResultList(); + }finally { + if (em.isOpen()) em.close(); + } + } + + public Optional findById(Integer id) { + logger.debug("Finding TrackHasArtist with id: {}", id); + EntityManager em = entityManagerFactory.createEntityManager(); + try{ + TrackHasArtist trackHasArtist = em.find(TrackHasArtist.class, id); + return Optional.ofNullable(trackHasArtist); + }finally { + if (em.isOpen()) em.close(); + } + } + + public boolean deleteById(Integer id){ + logger.debug("Deleting TrackHasArtist with id: {}", id); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + try{ + TrackHasArtist trackHasArtist = em.find(TrackHasArtist.class, id); + if (trackHasArtist == null) { + em.getTransaction().rollback(); + return false; + } + em.remove(trackHasArtist); + em.getTransaction().commit(); + logger.debug("TrackHasArtist has been deleted successfully"); + return true; + } catch (Exception e) { + em.getTransaction().rollback(); + logger.error("Error while deleting TrackHasArtist: {}", e.getMessage()); + throw e; + } finally { + if (em.isOpen()) em.close(); + } + } + +} diff --git a/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java b/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java index da043f5..724c87d 100644 --- a/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java +++ b/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java @@ -13,6 +13,7 @@ import com.mediamanager.service.bitdepth.BitDepthService; import com.mediamanager.service.bitrate.BitRateService; import com.mediamanager.service.composer.ComposerService; import com.mediamanager.service.trackhasgenre.TrackHasGenreService; +import com.mediamanager.service.trackhasartist.TrackHasArtistService; import com.mediamanager.repository.GenreRepository; import com.mediamanager.service.artist.ArtistService; @@ -117,6 +118,10 @@ public class DelegateActionManager { TrackHasGenreService trackHasGenreService = new TrackHasGenreService(trackHasGenreRepository, trackRepository, genreRepository); serviceLocator.register(TrackHasGenreService.class, trackHasGenreService); + TrackHasArtistRepository trackHasArtistRepository = new TrackHasArtistRepository(entityManagerFactory); + TrackHasArtistService trackHasArtistService = new TrackHasArtistService(trackHasArtistRepository, trackRepository, artistRepository); + serviceLocator.register(TrackHasArtistService.class, trackHasArtistService); + serviceLocator.logRegisteredServices(); logger.info("Services initialized successfully"); diff --git a/src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/CreateTrackHasArtistHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/CreateTrackHasArtistHandler.java new file mode 100644 index 0000000..37af6c3 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/CreateTrackHasArtistHandler.java @@ -0,0 +1,54 @@ +package com.mediamanager.service.delegate.handler.trackhasartist; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.TrackHasArtistMapper; +import com.mediamanager.model.TrackHasArtist; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.TrackHasArtistMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.delegate.annotation.Action; +import com.mediamanager.service.trackhasartist.TrackHasArtistService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Action("trackhasartist.create") +public class CreateTrackHasArtistHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(CreateTrackHasArtistHandler.class); + private final TrackHasArtistService trackHasArtistService; + + public CreateTrackHasArtistHandler(TrackHasArtistService trackHasArtistService) { + this.trackHasArtistService = trackHasArtistService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) throws InvalidProtocolBufferException { + try{ + TrackHasArtistMessages.CreateTrackHasArtistRequest createRequest = + TrackHasArtistMessages.CreateTrackHasArtistRequest.parseFrom(requestPayload); + + TrackHasArtist trackHasArtist = trackHasArtistService.createTrackHasArtist( + createRequest.getFkTrackId() > 0 ? createRequest.getFkTrackId() : null, + createRequest.getFkArtistId() > 0 ? createRequest.getFkArtistId() : null + ); + + TrackHasArtistMessages.TrackHasArtist trackHasArtistProto = TrackHasArtistMapper.toProtobuf(trackHasArtist); + TrackHasArtistMessages.CreateTrackHasArtistResponse createTrackHasArtistResponse = TrackHasArtistMessages.CreateTrackHasArtistResponse.newBuilder() + .setTrackhasartist(trackHasArtistProto) + .build(); + return TransportProtocol.Response.newBuilder() + .setPayload(createTrackHasArtistResponse.toByteString()); + } catch (IllegalArgumentException e) { + logger.error("Validation error", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(400) + .setPayload(ByteString.copyFromUtf8("Validation error: " + e.getMessage())); + + } catch (Exception e) { + logger.error("Error creating track has artist", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/DeleteTrackHasArtistHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/DeleteTrackHasArtistHandler.java new file mode 100644 index 0000000..1b7c643 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/DeleteTrackHasArtistHandler.java @@ -0,0 +1,62 @@ +package com.mediamanager.service.delegate.handler.trackhasartist; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.TrackHasArtistMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.delegate.annotation.Action; +import com.mediamanager.service.trackhasartist.TrackHasArtistService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Action("trackhasartist.delete") +public class DeleteTrackHasArtistHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(DeleteTrackHasArtistHandler.class); + + private final TrackHasArtistService trackHasArtistService; + + public DeleteTrackHasArtistHandler(TrackHasArtistService trackHasArtistService) { + this.trackHasArtistService = trackHasArtistService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + + try { + TrackHasArtistMessages.DeleteTrackHasArtistRequest deleteRequest = + TrackHasArtistMessages.DeleteTrackHasArtistRequest.parseFrom(requestPayload); + int id = deleteRequest.getId(); + boolean success = trackHasArtistService.deleteTrackHasArtist(id); + TrackHasArtistMessages.DeleteTrackHasArtistResponse deleteResponse; + if (success) { + deleteResponse = TrackHasArtistMessages.DeleteTrackHasArtistResponse.newBuilder() + .setSuccess(true) + .setMessage("Track has artist deleted successfully") + .build(); + return TransportProtocol.Response.newBuilder() + .setPayload(deleteResponse.toByteString()); + } else { + deleteResponse = TrackHasArtistMessages.DeleteTrackHasArtistResponse.newBuilder() + .setSuccess(false) + .setMessage("Track has artist not found") + .build(); + + return TransportProtocol.Response.newBuilder() + .setStatusCode(404) + .setPayload(deleteResponse.toByteString()); + } + } catch (Exception e) { + logger.error("Error deleting track has artist", e); + TrackHasArtistMessages.DeleteTrackHasArtistResponse deleteResponse = + TrackHasArtistMessages.DeleteTrackHasArtistResponse.newBuilder() + .setSuccess(false) + .setMessage("Error: " + e.getMessage()) + .build(); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(deleteResponse.toByteString()); + } + } + } diff --git a/src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/GetTrackHasArtistByIdHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/GetTrackHasArtistByIdHandler.java new file mode 100644 index 0000000..db3996f --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/GetTrackHasArtistByIdHandler.java @@ -0,0 +1,56 @@ +package com.mediamanager.service.delegate.handler.trackhasartist; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.TrackHasArtistMapper; +import com.mediamanager.model.TrackHasArtist; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.TrackHasArtistMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.delegate.annotation.Action; +import com.mediamanager.service.trackhasartist.TrackHasArtistService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Optional; + +@Action(value = "trackhasartist.getById") +public class GetTrackHasArtistByIdHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(GetTrackHasArtistByIdHandler.class); + private final TrackHasArtistService trackHasArtistService; + + public GetTrackHasArtistByIdHandler(TrackHasArtistService trackHasArtistService) { + this.trackHasArtistService = trackHasArtistService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException{ + + try{ + TrackHasArtistMessages.GetTrackHasArtistByIdRequest getByIdRequest = + TrackHasArtistMessages.GetTrackHasArtistByIdRequest.parseFrom(requestPayload); + int id = getByIdRequest.getId(); + + Optional trackHasArtistOpt = trackHasArtistService.getTrackHasArtistById(id); + + if (trackHasArtistOpt.isEmpty()){ + logger.warn("TrackHasArtist not found with ID: {}", id); + return TransportProtocol.Response.newBuilder() + .setStatusCode(404) + .setPayload(ByteString.copyFromUtf8("TrackHasArtist not found")); + } + TrackHasArtistMessages.TrackHasArtist trackHasArtistProto = TrackHasArtistMapper.toProtobuf(trackHasArtistOpt.get()); + TrackHasArtistMessages.GetTrackHasArtistByIdResponse getByIdResponse = TrackHasArtistMessages.GetTrackHasArtistByIdResponse.newBuilder() + .setTrackhasartist(trackHasArtistProto) + .build(); + return TransportProtocol.Response.newBuilder() + .setPayload(getByIdResponse.toByteString()); + } catch (Exception e) { + logger.error("Error getting track has artist by ID", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: "+ e.getMessage())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/GetTrackHasArtistHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/GetTrackHasArtistHandler.java new file mode 100644 index 0000000..1734264 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/trackhasartist/GetTrackHasArtistHandler.java @@ -0,0 +1,48 @@ +package com.mediamanager.service.delegate.handler.trackhasartist; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.TrackHasArtistMapper; +import com.mediamanager.model.TrackHasArtist; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.TrackHasArtistMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.delegate.annotation.Action; +import com.mediamanager.service.trackhasartist.TrackHasArtistService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; + + +@Action("trackhasartist.getAll") +public class GetTrackHasArtistHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(GetTrackHasArtistHandler.class); + + private final TrackHasArtistService trackHasArtistService; + + public GetTrackHasArtistHandler(TrackHasArtistService trackHasArtistService){this.trackHasArtistService = trackHasArtistService;} + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) throws InvalidProtocolBufferException { + try{ + List trackHasArtists = trackHasArtistService.getAllTrackHasArtists(); + TrackHasArtistMessages.GetTrackHasArtistsResponse.Builder responseBuilder = TrackHasArtistMessages.GetTrackHasArtistsResponse.newBuilder(); + + for (TrackHasArtist trackHasArtist : trackHasArtists) { + TrackHasArtistMessages.TrackHasArtist trackHasArtistProto = TrackHasArtistMapper.toProtobuf(trackHasArtist); + responseBuilder.addTrackhasartist(trackHasArtistProto); + } + TrackHasArtistMessages.GetTrackHasArtistsResponse getTrackHasArtistsResponse = responseBuilder.build(); + + return TransportProtocol.Response.newBuilder() + .setPayload(getTrackHasArtistsResponse.toByteString()); + + }catch (Exception e){ + logger.error("Error getting track has artists", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/trackhasartist/TrackHasArtistService.java b/src/main/java/com/mediamanager/service/trackhasartist/TrackHasArtistService.java new file mode 100644 index 0000000..bad7a27 --- /dev/null +++ b/src/main/java/com/mediamanager/service/trackhasartist/TrackHasArtistService.java @@ -0,0 +1,77 @@ +package com.mediamanager.service.trackhasartist; + +import com.mediamanager.model.Track; +import com.mediamanager.model.TrackHasArtist; +import com.mediamanager.model.Artist; +import com.mediamanager.repository.TrackHasArtistRepository; +import com.mediamanager.repository.TrackRepository; +import com.mediamanager.repository.ArtistRepository; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; +import java.util.Optional; + +public class TrackHasArtistService { + private static final Logger logger = LogManager.getLogger(TrackHasArtistService.class); + private final TrackHasArtistRepository repository; + private final TrackRepository trackRepository; + private final ArtistRepository artistRepository; + + public TrackHasArtistService(TrackHasArtistRepository repository, TrackRepository trackRepository, ArtistRepository artistRepository) { + this.repository = repository; + this.trackRepository = trackRepository; + this.artistRepository = artistRepository; + } + + public TrackHasArtist createTrackHasArtist(Integer trackId, Integer artistId) { + logger.debug("Creating track has artist relationship - trackId:{}, artistId:{}", trackId, artistId); + + if (trackId == null || trackId <= 0) { + throw new IllegalArgumentException("Track ID cannot be null or invalid"); + } + if (artistId == null || artistId <= 0) { + throw new IllegalArgumentException("Artist ID cannot be null or invalid"); + } + + // Verify Track exists + Optional track = trackRepository.findById(trackId); + if (track.isEmpty()) { + throw new IllegalArgumentException("Track not found with id: " + trackId); + } + + // Verify Artist exists + Optional artist = artistRepository.findById(artistId); + if (artist.isEmpty()) { + throw new IllegalArgumentException("Artist not found with id: " + artistId); + } + + TrackHasArtist trackHasArtist = new TrackHasArtist(); + trackHasArtist.setTrack(track.get()); + trackHasArtist.setArtist(artist.get()); + + return repository.save(trackHasArtist); + } + + public List getAllTrackHasArtists() { + logger.info("Getting all track has artist relationships"); + return repository.findAll(); + } + + public Optional getTrackHasArtistById(Integer id) { + if (id == null) { + throw new IllegalArgumentException("ID cannot be null"); + } + logger.info("Getting track has artist by id:{}", id); + return repository.findById(id); + } + + public boolean deleteTrackHasArtist(Integer id) { + if (id == null) { + throw new IllegalArgumentException("Track has artist id cannot be null"); + } + logger.info("Deleting track has artist:{}", id); + return repository.deleteById(id); + } + +} diff --git a/src/main/proto/trackhasartist.proto b/src/main/proto/trackhasartist.proto new file mode 100644 index 0000000..42b8621 --- /dev/null +++ b/src/main/proto/trackhasartist.proto @@ -0,0 +1,46 @@ +syntax = "proto3"; + +option java_package = "com.mediamanager.protocol.messages"; +option java_outer_classname = "TrackHasArtistMessages"; + +package mediamanager.messages; + +message TrackHasArtist{ + int32 id = 1; + int32 fk_track_id = 2; + int32 fk_artist_id =3; +} + +message CreateTrackHasArtistRequest { + int32 fk_track_id = 1; + int32 fk_artist_id = 2; +} + +message CreateTrackHasArtistResponse { + TrackHasArtist trackhasartist = 1; +} + +message GetTrackHasArtistsRequest { + +} + +message GetTrackHasArtistsResponse { + repeated TrackHasArtist trackhasartist = 1; +} + +message GetTrackHasArtistByIdRequest { + int32 id = 1; +} + +message GetTrackHasArtistByIdResponse { + TrackHasArtist trackhasartist = 1; +} + +message DeleteTrackHasArtistRequest { + int32 id = 1; +} + +message DeleteTrackHasArtistResponse { + bool success = 1; + string message = 2; +} \ No newline at end of file From 80ee003fc9663ebe7d571c6c1e6f0a99c1bb4d7d Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Tue, 9 Dec 2025 16:43:47 -0300 Subject: [PATCH 3/3] Implement trackhascomposer relationship management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add complete implementation for track-composer relationship management, following the established trackhasgenre pattern. This enables tracking which composers are associated with each track in the media library. Changes: - Enhance TrackHasComposer model: Add @Entity and @Table annotations, nullable constraints, constructor, and toString method - Implement TrackHasComposerRepository with full CRUD operations - Implement TrackHasComposerService with business logic and validation - Add TrackHasComposerMapper for entity/protobuf conversion - Create action handlers: * CreateTrackHasComposerHandler (trackhascomposer.create) * DeleteTrackHasComposerHandler (trackhascomposer.delete) * GetTrackHasComposerByIdHandler (trackhascomposer.getById) * GetTrackHasComposerHandler (trackhascomposer.getAll) - Register TrackHasComposerService in DelegateActionManager for automatic handler discovery and dependency injection - Proto file (trackhascomposer.proto) was already correct The implementation validates track and composer existence before creating relationships and provides proper error handling with appropriate HTTP status codes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../mapper/TrackHasComposerMapper.java | 47 ++++++++++ .../mediamanager/model/TrackHasComposer.java | 55 ++++++++++++ .../TrackHasComposerRepository.java | 85 +++++++++++++++++++ .../delegate/DelegateActionManager.java | 5 ++ .../CreateTrackHasComposerHandler.java | 54 ++++++++++++ .../DeleteTrackHasComposerHandler.java | 62 ++++++++++++++ .../GetTrackHasComposerByIdHandler.java | 56 ++++++++++++ .../GetTrackHasComposerHandler.java | 48 +++++++++++ .../TrackHasComposerService.java | 77 +++++++++++++++++ src/main/proto/trackhascomposer.proto | 46 ++++++++++ 10 files changed, 535 insertions(+) create mode 100644 src/main/java/com/mediamanager/mapper/TrackHasComposerMapper.java create mode 100644 src/main/java/com/mediamanager/model/TrackHasComposer.java create mode 100644 src/main/java/com/mediamanager/repository/TrackHasComposerRepository.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/CreateTrackHasComposerHandler.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/DeleteTrackHasComposerHandler.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/GetTrackHasComposerByIdHandler.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/GetTrackHasComposerHandler.java create mode 100644 src/main/java/com/mediamanager/service/trackhascomposer/TrackHasComposerService.java create mode 100644 src/main/proto/trackhascomposer.proto diff --git a/src/main/java/com/mediamanager/mapper/TrackHasComposerMapper.java b/src/main/java/com/mediamanager/mapper/TrackHasComposerMapper.java new file mode 100644 index 0000000..c722faa --- /dev/null +++ b/src/main/java/com/mediamanager/mapper/TrackHasComposerMapper.java @@ -0,0 +1,47 @@ +package com.mediamanager.mapper; + +import com.mediamanager.model.TrackHasComposer; +import com.mediamanager.protocol.messages.TrackHasComposerMessages; + +public class TrackHasComposerMapper { + public static TrackHasComposerMessages.TrackHasComposer toProtobuf(TrackHasComposer entity) { + if (entity == null) { + return null; + } + + TrackHasComposerMessages.TrackHasComposer.Builder builder = TrackHasComposerMessages.TrackHasComposer.newBuilder(); + + Integer id = entity.getId(); + if (id != null) { + builder.setId(id); + } + + // Map Track foreign key + if (entity.getTrack() != null && entity.getTrack().getId() != null) { + builder.setFkTrackId(entity.getTrack().getId()); + } + + // Map Composer foreign key + if (entity.getComposer() != null && entity.getComposer().getId() != null) { + builder.setFkComposerId(entity.getComposer().getId()); + } + + return builder.build(); + } + + public static TrackHasComposer toEntity(TrackHasComposerMessages.TrackHasComposer protobuf) { + if (protobuf == null) { + return null; + } + + TrackHasComposer entity = new TrackHasComposer(); + + if (protobuf.getId() > 0) { + entity.setId(protobuf.getId()); + } + + // Note: Foreign key relationships (Track, Composer) are handled in the service layer + + return entity; + } +} diff --git a/src/main/java/com/mediamanager/model/TrackHasComposer.java b/src/main/java/com/mediamanager/model/TrackHasComposer.java new file mode 100644 index 0000000..88dc3ff --- /dev/null +++ b/src/main/java/com/mediamanager/model/TrackHasComposer.java @@ -0,0 +1,55 @@ +package com.mediamanager.model; + +import jakarta.persistence.*; + +@Entity +@Table(name = "trackhascomposer") +public class TrackHasComposer { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "fk_track_id", nullable = false) + private Track track; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "fk_composer_id", nullable = false) + private Composer composer; + + public TrackHasComposer() {} + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Track getTrack() { + return track; + } + + public void setTrack(Track track) { + this.track = track; + } + + public Composer getComposer() { + return composer; + } + + public void setComposer(Composer composer) { + this.composer = composer; + } + + @Override + public String toString() { + return "TrackHasComposer{" + + "id=" + id + + ", trackId=" + (track != null ? track.getId() : null) + + ", composerId=" + (composer != null ? composer.getId() : null) + + '}'; + } +} diff --git a/src/main/java/com/mediamanager/repository/TrackHasComposerRepository.java b/src/main/java/com/mediamanager/repository/TrackHasComposerRepository.java new file mode 100644 index 0000000..5ddbe61 --- /dev/null +++ b/src/main/java/com/mediamanager/repository/TrackHasComposerRepository.java @@ -0,0 +1,85 @@ +package com.mediamanager.repository; + + +import com.mediamanager.model.TrackHasComposer; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + + +import java.util.List; +import java.util.Optional; + +public class TrackHasComposerRepository { + private static final Logger logger = LogManager.getLogger(TrackHasComposerRepository.class); + + private final EntityManagerFactory entityManagerFactory; + + public TrackHasComposerRepository(EntityManagerFactory entityManagerFactory) { + this.entityManagerFactory = entityManagerFactory; + } + + public TrackHasComposer save(TrackHasComposer trackHasComposer) { + logger.debug("Saving TrackHasComposer: {}", trackHasComposer); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + try { + em.persist(trackHasComposer); + em.getTransaction().commit(); + logger.debug("TrackHasComposer has been saved successfully"); + return trackHasComposer; + } catch (Exception e) { + em.getTransaction().rollback(); + logger.error("Error while saving TrackHasComposer: {}", e.getMessage()); + throw e; + } finally { + if (em.isOpen()) em.close(); + } + } + + public List findAll() { + logger.debug("Finding All TrackHasComposer"); + EntityManager em = entityManagerFactory.createEntityManager(); + try{ + return em.createQuery("select t from TrackHasComposer t", TrackHasComposer.class).getResultList(); + }finally { + if (em.isOpen()) em.close(); + } + } + + public Optional findById(Integer id) { + logger.debug("Finding TrackHasComposer with id: {}", id); + EntityManager em = entityManagerFactory.createEntityManager(); + try{ + TrackHasComposer trackHasComposer = em.find(TrackHasComposer.class, id); + return Optional.ofNullable(trackHasComposer); + }finally { + if (em.isOpen()) em.close(); + } + } + + public boolean deleteById(Integer id){ + logger.debug("Deleting TrackHasComposer with id: {}", id); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + try{ + TrackHasComposer trackHasComposer = em.find(TrackHasComposer.class, id); + if (trackHasComposer == null) { + em.getTransaction().rollback(); + return false; + } + em.remove(trackHasComposer); + em.getTransaction().commit(); + logger.debug("TrackHasComposer has been deleted successfully"); + return true; + } catch (Exception e) { + em.getTransaction().rollback(); + logger.error("Error while deleting TrackHasComposer: {}", e.getMessage()); + throw e; + } finally { + if (em.isOpen()) em.close(); + } + } + +} diff --git a/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java b/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java index 724c87d..783b617 100644 --- a/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java +++ b/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java @@ -14,6 +14,7 @@ import com.mediamanager.service.bitrate.BitRateService; import com.mediamanager.service.composer.ComposerService; import com.mediamanager.service.trackhasgenre.TrackHasGenreService; import com.mediamanager.service.trackhasartist.TrackHasArtistService; +import com.mediamanager.service.trackhascomposer.TrackHasComposerService; import com.mediamanager.repository.GenreRepository; import com.mediamanager.service.artist.ArtistService; @@ -122,6 +123,10 @@ public class DelegateActionManager { TrackHasArtistService trackHasArtistService = new TrackHasArtistService(trackHasArtistRepository, trackRepository, artistRepository); serviceLocator.register(TrackHasArtistService.class, trackHasArtistService); + TrackHasComposerRepository trackHasComposerRepository = new TrackHasComposerRepository(entityManagerFactory); + TrackHasComposerService trackHasComposerService = new TrackHasComposerService(trackHasComposerRepository, trackRepository, composerRepository); + serviceLocator.register(TrackHasComposerService.class, trackHasComposerService); + serviceLocator.logRegisteredServices(); logger.info("Services initialized successfully"); diff --git a/src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/CreateTrackHasComposerHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/CreateTrackHasComposerHandler.java new file mode 100644 index 0000000..7d9b2fb --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/CreateTrackHasComposerHandler.java @@ -0,0 +1,54 @@ +package com.mediamanager.service.delegate.handler.trackhascomposer; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.TrackHasComposerMapper; +import com.mediamanager.model.TrackHasComposer; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.TrackHasComposerMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.delegate.annotation.Action; +import com.mediamanager.service.trackhascomposer.TrackHasComposerService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Action("trackhascomposer.create") +public class CreateTrackHasComposerHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(CreateTrackHasComposerHandler.class); + private final TrackHasComposerService trackHasComposerService; + + public CreateTrackHasComposerHandler(TrackHasComposerService trackHasComposerService) { + this.trackHasComposerService = trackHasComposerService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) throws InvalidProtocolBufferException { + try{ + TrackHasComposerMessages.CreateTrackHasComposerRequest createRequest = + TrackHasComposerMessages.CreateTrackHasComposerRequest.parseFrom(requestPayload); + + TrackHasComposer trackHasComposer = trackHasComposerService.createTrackHasComposer( + createRequest.getFkTrackId() > 0 ? createRequest.getFkTrackId() : null, + createRequest.getFkComposerId() > 0 ? createRequest.getFkComposerId() : null + ); + + TrackHasComposerMessages.TrackHasComposer trackHasComposerProto = TrackHasComposerMapper.toProtobuf(trackHasComposer); + TrackHasComposerMessages.CreateTrackHasComposerResponse createTrackHasComposerResponse = TrackHasComposerMessages.CreateTrackHasComposerResponse.newBuilder() + .setTrackhascomposer(trackHasComposerProto) + .build(); + return TransportProtocol.Response.newBuilder() + .setPayload(createTrackHasComposerResponse.toByteString()); + } catch (IllegalArgumentException e) { + logger.error("Validation error", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(400) + .setPayload(ByteString.copyFromUtf8("Validation error: " + e.getMessage())); + + } catch (Exception e) { + logger.error("Error creating track has composer", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/DeleteTrackHasComposerHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/DeleteTrackHasComposerHandler.java new file mode 100644 index 0000000..a9d17fd --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/DeleteTrackHasComposerHandler.java @@ -0,0 +1,62 @@ +package com.mediamanager.service.delegate.handler.trackhascomposer; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.TrackHasComposerMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.delegate.annotation.Action; +import com.mediamanager.service.trackhascomposer.TrackHasComposerService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Action("trackhascomposer.delete") +public class DeleteTrackHasComposerHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(DeleteTrackHasComposerHandler.class); + + private final TrackHasComposerService trackHasComposerService; + + public DeleteTrackHasComposerHandler(TrackHasComposerService trackHasComposerService) { + this.trackHasComposerService = trackHasComposerService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + + try { + TrackHasComposerMessages.DeleteTrackHasComposerRequest deleteRequest = + TrackHasComposerMessages.DeleteTrackHasComposerRequest.parseFrom(requestPayload); + int id = deleteRequest.getId(); + boolean success = trackHasComposerService.deleteTrackHasComposer(id); + TrackHasComposerMessages.DeleteTrackHasComposerResponse deleteResponse; + if (success) { + deleteResponse = TrackHasComposerMessages.DeleteTrackHasComposerResponse.newBuilder() + .setSuccess(true) + .setMessage("Track has composer deleted successfully") + .build(); + return TransportProtocol.Response.newBuilder() + .setPayload(deleteResponse.toByteString()); + } else { + deleteResponse = TrackHasComposerMessages.DeleteTrackHasComposerResponse.newBuilder() + .setSuccess(false) + .setMessage("Track has composer not found") + .build(); + + return TransportProtocol.Response.newBuilder() + .setStatusCode(404) + .setPayload(deleteResponse.toByteString()); + } + } catch (Exception e) { + logger.error("Error deleting track has composer", e); + TrackHasComposerMessages.DeleteTrackHasComposerResponse deleteResponse = + TrackHasComposerMessages.DeleteTrackHasComposerResponse.newBuilder() + .setSuccess(false) + .setMessage("Error: " + e.getMessage()) + .build(); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(deleteResponse.toByteString()); + } + } + } diff --git a/src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/GetTrackHasComposerByIdHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/GetTrackHasComposerByIdHandler.java new file mode 100644 index 0000000..d717746 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/GetTrackHasComposerByIdHandler.java @@ -0,0 +1,56 @@ +package com.mediamanager.service.delegate.handler.trackhascomposer; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.TrackHasComposerMapper; +import com.mediamanager.model.TrackHasComposer; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.TrackHasComposerMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.delegate.annotation.Action; +import com.mediamanager.service.trackhascomposer.TrackHasComposerService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Optional; + +@Action(value = "trackhascomposer.getById") +public class GetTrackHasComposerByIdHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(GetTrackHasComposerByIdHandler.class); + private final TrackHasComposerService trackHasComposerService; + + public GetTrackHasComposerByIdHandler(TrackHasComposerService trackHasComposerService) { + this.trackHasComposerService = trackHasComposerService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException{ + + try{ + TrackHasComposerMessages.GetTrackHasComposerByIdRequest getByIdRequest = + TrackHasComposerMessages.GetTrackHasComposerByIdRequest.parseFrom(requestPayload); + int id = getByIdRequest.getId(); + + Optional trackHasComposerOpt = trackHasComposerService.getTrackHasComposerById(id); + + if (trackHasComposerOpt.isEmpty()){ + logger.warn("TrackHasComposer not found with ID: {}", id); + return TransportProtocol.Response.newBuilder() + .setStatusCode(404) + .setPayload(ByteString.copyFromUtf8("TrackHasComposer not found")); + } + TrackHasComposerMessages.TrackHasComposer trackHasComposerProto = TrackHasComposerMapper.toProtobuf(trackHasComposerOpt.get()); + TrackHasComposerMessages.GetTrackHasComposerByIdResponse getByIdResponse = TrackHasComposerMessages.GetTrackHasComposerByIdResponse.newBuilder() + .setTrackhascomposer(trackHasComposerProto) + .build(); + return TransportProtocol.Response.newBuilder() + .setPayload(getByIdResponse.toByteString()); + } catch (Exception e) { + logger.error("Error getting track has composer by ID", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: "+ e.getMessage())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/GetTrackHasComposerHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/GetTrackHasComposerHandler.java new file mode 100644 index 0000000..20c211e --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/trackhascomposer/GetTrackHasComposerHandler.java @@ -0,0 +1,48 @@ +package com.mediamanager.service.delegate.handler.trackhascomposer; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.TrackHasComposerMapper; +import com.mediamanager.model.TrackHasComposer; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.TrackHasComposerMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.delegate.annotation.Action; +import com.mediamanager.service.trackhascomposer.TrackHasComposerService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; + + +@Action("trackhascomposer.getAll") +public class GetTrackHasComposerHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(GetTrackHasComposerHandler.class); + + private final TrackHasComposerService trackHasComposerService; + + public GetTrackHasComposerHandler(TrackHasComposerService trackHasComposerService){this.trackHasComposerService = trackHasComposerService;} + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) throws InvalidProtocolBufferException { + try{ + List trackHasComposers = trackHasComposerService.getAllTrackHasComposers(); + TrackHasComposerMessages.GetTrackHasComposersResponse.Builder responseBuilder = TrackHasComposerMessages.GetTrackHasComposersResponse.newBuilder(); + + for (TrackHasComposer trackHasComposer : trackHasComposers) { + TrackHasComposerMessages.TrackHasComposer trackHasComposerProto = TrackHasComposerMapper.toProtobuf(trackHasComposer); + responseBuilder.addTrackhascomposer(trackHasComposerProto); + } + TrackHasComposerMessages.GetTrackHasComposersResponse getTrackHasComposersResponse = responseBuilder.build(); + + return TransportProtocol.Response.newBuilder() + .setPayload(getTrackHasComposersResponse.toByteString()); + + }catch (Exception e){ + logger.error("Error getting track has composers", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/trackhascomposer/TrackHasComposerService.java b/src/main/java/com/mediamanager/service/trackhascomposer/TrackHasComposerService.java new file mode 100644 index 0000000..71224f2 --- /dev/null +++ b/src/main/java/com/mediamanager/service/trackhascomposer/TrackHasComposerService.java @@ -0,0 +1,77 @@ +package com.mediamanager.service.trackhascomposer; + +import com.mediamanager.model.Track; +import com.mediamanager.model.TrackHasComposer; +import com.mediamanager.model.Composer; +import com.mediamanager.repository.TrackHasComposerRepository; +import com.mediamanager.repository.TrackRepository; +import com.mediamanager.repository.ComposerRepository; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; +import java.util.Optional; + +public class TrackHasComposerService { + private static final Logger logger = LogManager.getLogger(TrackHasComposerService.class); + private final TrackHasComposerRepository repository; + private final TrackRepository trackRepository; + private final ComposerRepository composerRepository; + + public TrackHasComposerService(TrackHasComposerRepository repository, TrackRepository trackRepository, ComposerRepository composerRepository) { + this.repository = repository; + this.trackRepository = trackRepository; + this.composerRepository = composerRepository; + } + + public TrackHasComposer createTrackHasComposer(Integer trackId, Integer composerId) { + logger.debug("Creating track has composer relationship - trackId:{}, composerId:{}", trackId, composerId); + + if (trackId == null || trackId <= 0) { + throw new IllegalArgumentException("Track ID cannot be null or invalid"); + } + if (composerId == null || composerId <= 0) { + throw new IllegalArgumentException("Composer ID cannot be null or invalid"); + } + + // Verify Track exists + Optional track = trackRepository.findById(trackId); + if (track.isEmpty()) { + throw new IllegalArgumentException("Track not found with id: " + trackId); + } + + // Verify Composer exists + Optional composer = composerRepository.findById(composerId); + if (composer.isEmpty()) { + throw new IllegalArgumentException("Composer not found with id: " + composerId); + } + + TrackHasComposer trackHasComposer = new TrackHasComposer(); + trackHasComposer.setTrack(track.get()); + trackHasComposer.setComposer(composer.get()); + + return repository.save(trackHasComposer); + } + + public List getAllTrackHasComposers() { + logger.info("Getting all track has composer relationships"); + return repository.findAll(); + } + + public Optional getTrackHasComposerById(Integer id) { + if (id == null) { + throw new IllegalArgumentException("ID cannot be null"); + } + logger.info("Getting track has composer by id:{}", id); + return repository.findById(id); + } + + public boolean deleteTrackHasComposer(Integer id) { + if (id == null) { + throw new IllegalArgumentException("Track has composer id cannot be null"); + } + logger.info("Deleting track has composer:{}", id); + return repository.deleteById(id); + } + +} diff --git a/src/main/proto/trackhascomposer.proto b/src/main/proto/trackhascomposer.proto new file mode 100644 index 0000000..ad66bd2 --- /dev/null +++ b/src/main/proto/trackhascomposer.proto @@ -0,0 +1,46 @@ +syntax = "proto3"; + +option java_package = "com.mediamanager.protocol.messages"; +option java_outer_classname = "TrackHasComposerMessages"; + +package mediamanager.messages; + +message TrackHasComposer{ + int32 id = 1; + int32 fk_track_id = 2; + int32 fk_composer_id =3; +} + +message CreateTrackHasComposerRequest { + int32 fk_track_id = 1; + int32 fk_composer_id = 2; +} + +message CreateTrackHasComposerResponse { + TrackHasComposer trackhascomposer = 1; +} + +message GetTrackHasComposersRequest { + +} + +message GetTrackHasComposersResponse { + repeated TrackHasComposer trackhascomposer = 1; +} + +message GetTrackHasComposerByIdRequest { + int32 id = 1; +} + +message GetTrackHasComposerByIdResponse { + TrackHasComposer trackhascomposer = 1; +} + +message DeleteTrackHasComposerRequest { + int32 id = 1; +} + +message DeleteTrackHasComposerResponse { + bool success = 1; + string message = 2; +}