Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d22b2c6
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
6d242af
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
c66e0dc
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
1674ce3
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
0a3b5e2
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
3a28410
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
ff9df43
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
9ee4dbd
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
388fb51
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
5d86b36
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
22c8082
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
e141263
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
ebc04e7
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
3849b98
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
f407baa
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
1dc8b5b
working post movie and series method (still need some fixes)
NickGrahovskis Jan 7, 2026
da5e907
working post movie and series method based on account Roles (still n…
NickGrahovskis Jan 8, 2026
9936f59
working post movie and series querys
NickGrahovskis Jan 11, 2026
ed38562
add Movie and Series controllers, updated the security on admin endpo…
NickGrahovskis Jan 12, 2026
3128590
Merge branch 'NickBranch_v2' into Nick/branch3
NickGrahovskis Jan 12, 2026
ff6369f
Little fixes after merging
NickGrahovskis Jan 12, 2026
ad5d67d
Fixed creat full series logic
NickGrahovskis Jan 12, 2026
5a29618
Fixed security based on employee account id, added new method and cre…
NickGrahovskis Jan 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ backups/*.sql.gz
backups/*.sql.zip
backups/*.dump
!backups/.gitkeep

target/
.idea/
src/main/resources
**/application.properties
application.properties
10 changes: 9 additions & 1 deletion init-db/03_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ CREATE TABLE employee (
id SERIAL PRIMARY KEY,
role_id INT NOT NULL REFERENCES role(id),
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL
email VARCHAR(255) UNIQUE NOT NULL,
account_id int references accounts(id)
);

-- Insert initial lookup data
Expand Down Expand Up @@ -289,3 +290,10 @@ INSERT INTO media_content_warning (media_id, content_warning_id) VALUES
(4, 1), (4, 5), (4, 6), -- Pulp Fiction: Violence, Drug Abuse, Coarse Language
(6, 1), (6, 2), -- Matrix: Violence, Fear
(7, 1), (7, 5), (7, 6); -- Breaking Bad: Violence, Drug Abuse, Coarse Language


INSERT INTO employee (role_id, name, email)
VALUES
(1, 'Alice Junior', 'alice.junior@streamflix.com'),
(2, 'Bob Mid', 'bob.mid@streamflix.com'),
(3, 'Carol Senior', 'carol.senior@streamflix.com');
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example.streamflix.config;

import io.netty.handler.codec.http.HttpMethod;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
Expand Down Expand Up @@ -30,6 +31,9 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/register", "/api/v1/auth/login", "/api/v1/auth/verify", "/api/v1/auth/forgot-password", "/api/v1/auth/reset-password").permitAll()
.requestMatchers("/api/v1/media/admin/**","/api/v1/media/movie/admin/**")
.hasAnyRole("Junior", "Mid-level", "Senior")

.requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
.requestMatchers("/api/v1/analytics/admin/**").authenticated()
.requestMatchers("/api/v1/analytics/**").permitAll()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.example.streamflix.controller;

import com.example.streamflix.model.MediaDetailsDto;
import com.example.streamflix.model.StreamValidationResult;
import com.example.streamflix.entity.Media;
import com.example.streamflix.entity.Series;
import com.example.streamflix.model.*;
import com.example.streamflix.enums.MediaQuality;
import com.example.streamflix.service.MediaService;
import com.example.streamflix.service.MovieService;
import com.example.streamflix.service.SeriesService;
import com.example.streamflix.service.StreamingService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand All @@ -12,6 +15,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
Expand All @@ -25,10 +29,14 @@ public class MediaController {

private final MediaService mediaService;
private final StreamingService streamingService;
private final MovieService movieService;
private final SeriesService seriesService;

public MediaController(MediaService mediaService, StreamingService streamingService) {
public MediaController(MediaService mediaService, StreamingService streamingService, MovieService movieService, SeriesService seriesService) {
this.mediaService = mediaService;
this.streamingService = streamingService;
this.movieService = movieService;
this.seriesService = seriesService;
}

@Operation(summary = "Get available media", description = "Retrieve a list of media available for a specific profile")
Expand Down
110 changes: 110 additions & 0 deletions src/main/java/com/example/streamflix/controller/MovieController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package com.example.streamflix.controller;

import com.example.streamflix.entity.Movie;
import com.example.streamflix.model.MovieRequest;
import com.example.streamflix.service.MediaService;
import com.example.streamflix.service.MovieService;
import com.example.streamflix.service.SeriesService;
import com.example.streamflix.service.StreamingService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.persistence.Column;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.*;

@RestController
@RequestMapping("/api/v1/media/movie")
@Tag(name = "Movie", description = "Operations related to media content and streaming")
public class MovieController {

private final MediaService mediaService;
private final StreamingService streamingService;
private final MovieService movieService;
private final SeriesService seriesService;

public MovieController(MediaService mediaService,
StreamingService streamingService,
MovieService movieService,
SeriesService seriesService) {
this.mediaService = mediaService;
this.streamingService = streamingService;
this.movieService = movieService;
this.seriesService = seriesService;
}


@Operation(summary = "Get all movies",
description = "Retrieves a complete list of all movies available in the database."
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved list"),
@ApiResponse(responseCode = "204", description = "No movies found in the database")
})
@GetMapping("")
public List<Movie> getAllMovies(){
return movieService.getAllMovies();
}

@Operation(
summary = "Get movie by ID",
description = "Returns a single movie object based on its unique identifier."
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Movie found"),
@ApiResponse(responseCode = "404", description = "Movie not found with the provided ID")
})
@GetMapping("{movieId}")
public ResponseEntity<Movie> getMovieById(@PathVariable Long movieId){
return movieService.getMovieById(movieId);
}

@Operation(
summary = "Delete movie",
description = "Admin only: Removes a movie record from the system."
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Movie successfully deleted"),
@ApiResponse(responseCode = "404", description = "Movie not found")
})
@DeleteMapping("/admin/{movieId}")
public Movie deleteMovieById(@PathVariable Long movieId){
return movieService.deleteMovieById(movieId);
}

@Operation(
summary = "Update movie details",
description = "Admin only: Updates an existing movie's information."
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Movie updated successfully"),
@ApiResponse(responseCode = "400", description = "Invalid input data"),
@ApiResponse(responseCode = "404", description = "Movie not found")
})
@PutMapping("/admin/{movieId}")
public ResponseEntity<?> updateMovieById(@PathVariable Long movieId, @RequestBody MovieRequest movieRequest){
Object updated = movieService.updateMovie(movieId, movieRequest);
return ResponseEntity.ok(updated);
}

@Operation(
summary = "Create a new movie",
description = "Admin only: Adds a new movie record to the catalog."
)
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "Movie created successfully"),
@ApiResponse(responseCode = "400", description = "Invalid request body")
})
@PostMapping("/admin")
public ResponseEntity<?> createNewMovie(@RequestBody MovieRequest movieRequest){

Object created = movieService.createMovie(movieRequest);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.example.streamflix.controller;

import com.example.streamflix.entity.Movie;
import com.example.streamflix.model.EpisodeRequest;
import com.example.streamflix.model.MovieRequest;
import com.example.streamflix.model.SeriesCreationRequest;
import com.example.streamflix.service.MediaService;
import com.example.streamflix.service.MovieService;
import com.example.streamflix.service.SeriesService;
import com.example.streamflix.service.StreamingService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.persistence.Column;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.*;

@RestController
@RequestMapping("/api/v1/media/series")
@Tag(name = "Series", description = "Operations related to series")
public class SeriesController {

private final MediaService mediaService;
private final StreamingService streamingService;
private final MovieService movieService;
private final SeriesService seriesService;

public SeriesController(MediaService mediaService,
StreamingService streamingService,
MovieService movieService,
SeriesService seriesService) {
this.mediaService = mediaService;
this.streamingService = streamingService;
this.movieService = movieService;
this.seriesService = seriesService;
}

@Operation(summary = "Get all series", description = "Returns a list of all series available in the system")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved list"),
@ApiResponse(responseCode = "204", description = "No series found")
})
@GetMapping
public ResponseEntity<List<?>> getAllSeries() {
List<?> seriesList = seriesService.getSeriesList();
if (seriesList.isEmpty()) {
return ResponseEntity.noContent().build();
}
return ResponseEntity.ok(seriesList);
}

@Operation(summary = "Get series by ID", description = "Retrieves a single series along with its seasons and episodes")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Found the series"),
@ApiResponse(responseCode = "404", description = "Series not found")
})
@GetMapping("/{id}")
public ResponseEntity<?> getSeriesById(
@Parameter(description = "ID of the series to be obtained", required = true)
@PathVariable Long id) {

return seriesService.getSeriesById(id);
}

@Operation(summary = "Create a new series", description = "Admin endpoint to initialize a new series")
@PostMapping("/admin/series")
public ResponseEntity<?> createNewSeries(@RequestBody SeriesCreationRequest seriesCreationRequest){

Object created = seriesService.createFullSeries(seriesCreationRequest);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}

@Operation(summary = "Create a new season", description = "Add a season to an existing series")
@PostMapping("/admin/series/season")
public ResponseEntity<?> createNewSeason(@RequestBody SeriesCreationRequest seriesCreationRequest){

Object created = seriesService.createSeason(seriesCreationRequest);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}

@Operation(summary = "Create a new episode", description = "Add an episode to an existing season")
@PostMapping("/admin/series/episode")
public ResponseEntity<?> createNewEpisode(@RequestBody EpisodeRequest episodeRequest) {
Object created = seriesService.createEpisode(episodeRequest);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}


}
14 changes: 14 additions & 0 deletions src/main/java/com/example/streamflix/entity/Employee.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public class Employee {
@Column(unique = true, nullable = false)
private String email;

@ManyToOne
@JoinColumn(name = "account_id")
private Account account;

public Employee() {
}

Expand Down Expand Up @@ -60,4 +64,14 @@ public String getEmail() {
public void setEmail(String email) {
this.email = email;
}

public Account getAccount()
{
return this.account;
}

public void setAccount(Account account)
{
this.account = account;
}
}
1 change: 1 addition & 0 deletions src/main/java/com/example/streamflix/entity/Episode.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.streamflix.entity;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import jakarta.persistence.*;
import java.util.List;
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/example/streamflix/entity/Media.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.example.streamflix.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import io.swagger.v3.oas.annotations.media.Schema;
Expand Down
25 changes: 21 additions & 4 deletions src/main/java/com/example/streamflix/entity/Series.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,26 @@ public Series(AgeRating ageRating, String title, java.time.LocalDate releaseDate
this.description = description;
}

public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}

public List<Season> getSeasons() {
return seasons;
}

public void setSeasons(List<Season> seasons) {
this.seasons = seasons;
}

public List<Season> getSeasons() { return seasons; }
public void setSeasons(List<Season> seasons) { this.seasons = seasons; }
public void addSeason(Season season) {
if (this.seasons == null) {
this.seasons = new ArrayList<>();
}
this.seasons.add(season);
season.setSeries(this);
}
}
Loading