From e6e54a09a4add18881bc40e8f6e52c09916795ff Mon Sep 17 00:00:00 2001
From: Lawrence Angrave <angrave@illinois.edu>
Date: Sat, 6 Jan 2024 13:33:14 -0600
Subject: [PATCH] Fix NewPlayList and polish

---
 ClassTranscribeDatabase/Models/Models.cs      | 10 +--
 .../Controllers/AccountController.cs          |  1 +
 .../Controllers/MediaController.cs            |  8 +--
 .../Controllers/PlaylistsController.cs        | 67 +++++++++++++------
 .../Controllers/TaskController.cs             |  5 ++
 ClassTranscribeServer/Program.cs              |  2 +-
 TaskEngine/Tasks/DownloadMediaTask.cs         |  4 +-
 .../PlaylistsControllerTest.cs                | 30 +++++----
 8 files changed, 82 insertions(+), 45 deletions(-)

diff --git a/ClassTranscribeDatabase/Models/Models.cs b/ClassTranscribeDatabase/Models/Models.cs
index d75ab06..b71ea5b 100644
--- a/ClassTranscribeDatabase/Models/Models.cs
+++ b/ClassTranscribeDatabase/Models/Models.cs
@@ -245,12 +245,12 @@ public class Playlist : Entity
         public JObject JsonMetadata { get; set; } = new JObject(); // for serverside use
         public string Options { get; set; } = "";
         
-        public virtual JObject getOptionsAsJson() {
+        public virtual JObject GetOptionsAsJson() {
             return String.IsNullOrEmpty(this.Options) ? new JObject() : JObject.Parse(this.Options);
         }
         
-        public virtual void setOptionsAsJson(JObject json) {
-            this.Options = json.ToString();
+        public virtual void SetOptionsAsJson(JObject json) {
+            this.Options = json.ToString(Newtonsoft.Json.Formatting.None);
         }
         public int Index { get; set; }
         public Visibility Visibility { get; set; }
@@ -283,11 +283,11 @@ public class Media : Entity
         
         public string Options { get; set; } = "";
         
-        public virtual JObject getOptionsAsJson() {
+        public virtual JObject GetOptionsAsJson() {
             return String.IsNullOrEmpty(this.Options) ? new JObject() :  JObject.Parse(this.Options);
         }
         
-        public virtual void setOptionsAsJson(JObject json) {
+        public virtual void SetOptionsAsJson(JObject json) {
             this.Options = json.ToString(Newtonsoft.Json.Formatting.None);
         }
     }
diff --git a/ClassTranscribeServer/Controllers/AccountController.cs b/ClassTranscribeServer/Controllers/AccountController.cs
index 273aeea..ce4112f 100644
--- a/ClassTranscribeServer/Controllers/AccountController.cs
+++ b/ClassTranscribeServer/Controllers/AccountController.cs
@@ -152,6 +152,7 @@ public class MediaWorkerLoginDTO
         [HttpPost]
         public async Task<ActionResult<LoggedInDTO>> MediaWorkerSignIn(MediaWorkerLoginDTO accessDTO)
         {
+            if (accessDTO == null) return BadRequest("Missing parameter");
             try
             {
                 string access = accessDTO.Access;
diff --git a/ClassTranscribeServer/Controllers/MediaController.cs b/ClassTranscribeServer/Controllers/MediaController.cs
index 6421560..e95ca06 100644
--- a/ClassTranscribeServer/Controllers/MediaController.cs
+++ b/ClassTranscribeServer/Controllers/MediaController.cs
@@ -60,7 +60,7 @@ public async Task<ActionResult<MediaDTO>> GetMedia(string id)
             var playlist = await _context.Playlists.FindAsync(media.PlaylistId);
             //unused var v = await _context.Videos.FindAsync(media.VideoId);
             var user = await _userUtils.GetUser(User);
-            var restrict = (bool?) playlist.getOptionsAsJson()[ "restrictRoomStream"] ?? false;
+            var restrict = (bool?) playlist.GetOptionsAsJson()[ "restrictRoomStream"] ?? false;
             var mediaDTO = new MediaDTO
             {
                 Id = media.Id,
@@ -71,7 +71,7 @@ public async Task<ActionResult<MediaDTO>> GetMedia(string id)
                 SourceType = media.SourceType,
                 Duration = media.Video.Duration,
                 PublishStatus = media.PublishStatus,
-                Options = media.getOptionsAsJson(),
+                Options = media.GetOptionsAsJson(),
                 Transcriptions = media.Video.Transcriptions
                 .Select(t => new TranscriptionDTO
                 {
@@ -171,7 +171,7 @@ public async Task<IActionResult> PutMediaOption(string id, string option, string
             if(value == null) return BadRequest("value not specified");
 
             Media media = await _context.Medias.FindAsync(id);
-            JObject theOptions = media.getOptionsAsJson();
+            JObject theOptions = media.GetOptionsAsJson();
             if (media == null)
             {
                 return NotFound();
@@ -193,7 +193,7 @@ public async Task<IActionResult> PutMediaOption(string id, string option, string
             } else {
                 return BadRequest("Invalid type");// should never happen
             }
-            media.setOptionsAsJson(theOptions);
+            media.SetOptionsAsJson(theOptions);
             await _context.SaveChangesAsync();
             return NoContent();
         }
diff --git a/ClassTranscribeServer/Controllers/PlaylistsController.cs b/ClassTranscribeServer/Controllers/PlaylistsController.cs
index 083ee78..d68e736 100644
--- a/ClassTranscribeServer/Controllers/PlaylistsController.cs
+++ b/ClassTranscribeServer/Controllers/PlaylistsController.cs
@@ -71,7 +71,7 @@ public async Task<ActionResult<PlaylistDTO>> GetPlaylistsByVideoId(string videoI
                 PublishStatus = p.PublishStatus,
                 ListCheckedAt = p.ListCheckedAt,
                 ListUpdatedAt = p.ListUpdatedAt,
-                Options = p.getOptionsAsJson()
+                Options = p.GetOptionsAsJson()
             }).ToList().FirstOrDefault();
             return playlist;
         }
@@ -109,7 +109,7 @@ public async Task<ActionResult<IEnumerable<PlaylistDTO>>> GetPlaylists(string of
                 PublishStatus = p.PublishStatus,
                 ListCheckedAt = p.ListCheckedAt,
                 ListUpdatedAt = p.ListUpdatedAt,
-                Options = p.getOptionsAsJson()
+                Options = p.GetOptionsAsJson()
             }).ToList();
             return playlists;
         }
@@ -139,7 +139,7 @@ public async Task<ActionResult<IEnumerable<PlaylistDTO>>> GetPlaylists2(string o
 
             var hideRoomVideos = new Dictionary<string,bool>(); 
             foreach (var p in playLists) {
-                var restrict = (bool?) p.getOptionsAsJson()[ "restrictRoomStream"] ?? false;
+                var restrict = (bool?) p.GetOptionsAsJson()[ "restrictRoomStream"] ?? false;
                 hideRoomVideos.Add(p.Id, restrict);
             };
             var playlistDTOs = playLists.Select(p => new PlaylistDTO
@@ -154,7 +154,7 @@ public async Task<ActionResult<IEnumerable<PlaylistDTO>>> GetPlaylists2(string o
                 PublishStatus = p.PublishStatus,
                 ListCheckedAt = p.ListCheckedAt,
                 ListUpdatedAt = p.ListUpdatedAt,
-                Options = p.getOptionsAsJson(),
+                Options = p.GetOptionsAsJson(),
                 Medias = p.Medias.Where(m => m.Video != null).Select(m => new MediaDTO
                 {
                     Id = m.Id,
@@ -167,7 +167,7 @@ public async Task<ActionResult<IEnumerable<PlaylistDTO>>> GetPlaylists2(string o
                     SourceType = m.SourceType,
                     Duration = m.Video?.Duration,
                     PublishStatus = m.PublishStatus,
-                    Options = m.getOptionsAsJson(),
+                    Options = m.GetOptionsAsJson(),
                     Video = new VideoDTO
                     {
                         Id = m.Video.Id,
@@ -241,7 +241,7 @@ public async Task<ActionResult<PlaylistDTO>> GetPlaylist(string id)
             // In memory transformation into DTO resut
 
             var hideRoomVideos = new Dictionary<string,bool>(); 
-            var restrict = (bool?) p.getOptionsAsJson()[ "restrictRoomStream"] ?? false;
+            var restrict = (bool?) p.GetOptionsAsJson()[ "restrictRoomStream"] ?? false;
             
             List<MediaDTO> mediasDTO = mediaList.Select(m => new MediaDTO
                 {
@@ -254,7 +254,7 @@ public async Task<ActionResult<PlaylistDTO>> GetPlaylist(string id)
                     SourceType = m.SourceType,
                     Duration = m.Video?.Duration,
                     PublishStatus = m.PublishStatus,
-                    Options = m.getOptionsAsJson(),
+                    Options = m.GetOptionsAsJson(),
                     SceneDetectReady = m.Video != null && m.Video.HasSceneObjectData(),
                     Ready = m.Video != null && "NoError" == m.Video.TranscriptionStatus ,
                     Video = m.Video == null ? null : new VideoDTO
@@ -288,7 +288,7 @@ public async Task<ActionResult<PlaylistDTO>> GetPlaylist(string id)
                 PublishStatus = p.PublishStatus,
                 ListUpdatedAt = p.ListUpdatedAt,
                 ListCheckedAt = p.ListCheckedAt,
-                Options = p.getOptionsAsJson()
+                Options = p.GetOptionsAsJson()
             };
         }
        
@@ -315,7 +315,7 @@ public async Task<IActionResult> PutPlaylistOptions(string id, JObject options)
                 return new ChallengeResult();
             }
             
-            p.setOptionsAsJson(options);
+            p.SetOptionsAsJson(options);
            
             try
             {
@@ -364,7 +364,7 @@ public async Task<IActionResult> PutPlaylist(string id, PlaylistUpdateDTO playli
             var p = await _context.Playlists.FindAsync(playlist.Id);
 
             p.Name = playlist.Name;
-            p.setOptionsAsJson(playlist.Options);
+            p.SetOptionsAsJson(playlist.Options);
             p.PublishStatus = playlist.PublishStatus;
 
             try
@@ -390,18 +390,19 @@ public async Task<IActionResult> PutPlaylist(string id, PlaylistUpdateDTO playli
         // POST: api/Playlists
         [HttpPost]
         [Authorize]
-        public async Task<ActionResult<Playlist>> PostPlaylist(Playlist playlist)
+        public async Task<ActionResult<PlaylistDTO>> PostPlaylist(NewPlaylistDTO playlist)
         {
+            
             if (playlist == null || playlist.OfferingId == null)
             {
-                return BadRequest();
+                return BadRequest("Playlist missing or OfferingId missing");
             }
 
             var offering = await _context.Offerings.FindAsync(playlist.OfferingId);
 
             if (offering == null)
             {
-                return BadRequest();
+                return BadRequest("No such offering");
             }
 
             var authorizationResult = await _authorizationService.AuthorizeAsync(this.User, offering, Globals.POLICY_UPDATE_OFFERING);
@@ -410,28 +411,28 @@ public async Task<ActionResult<Playlist>> PostPlaylist(Playlist playlist)
             {
                 if (User.Identity.IsAuthenticated)
                 {
-                    return new ForbidResult();
+                    return new ForbidResult("Not authenticated");
                 }
 
-                return new ChallengeResult();
+                return new ChallengeResult("Not authorized for this offering");
             }
-
+           
             if (playlist.PlaylistIdentifier != null && playlist.PlaylistIdentifier.Length > 0)
             {
                 playlist.PlaylistIdentifier = playlist.PlaylistIdentifier.Trim();
             }
-
+            var p = playlist.ToPlaylist();
             // If playlists are deleted the Count != Max Index, so use the max index (still not perfect, what if 2 playlists are created at the same time)
             if (offering.Playlists != null && offering.Playlists.Count > 0)
             {
-                playlist.Index = 1 + offering.Playlists.Max(p => p.Index);
+                p.Index = 1 + offering.Playlists.Max(pl => pl.Index);
             }
 
-            _context.Playlists.Add(playlist);
+            _context.Playlists.Add(p);
             await _context.SaveChangesAsync();
-            _wakeDownloader.UpdatePlaylist(playlist.Id);
+            _wakeDownloader.UpdatePlaylist(p.Id);
 
-            return CreatedAtAction("GetPlaylist", new { id = playlist.Id }, playlist);
+            return await GetPlaylist(p.Id);
         }
 
         // DELETE: api/Playlists/5
@@ -538,6 +539,30 @@ public class PlaylistUpdateDTO {
         public JObject Options { get; set; }
         public PublishStatus PublishStatus { get; set; }
     }
+
+    public class NewPlaylistDTO
+    {
+        public string OfferingId { get; set; }
+        public string Name { get; set; }
+        public SourceType SourceType { get; set; }
+        public string PlaylistIdentifier { get; set; }
+        public JObject JsonMetadata { get; set; }
+        public JObject Options { get; set; }
+        public PublishStatus PublishStatus { get; set; }
+
+        public Playlist ToPlaylist()
+        {
+            return new Playlist
+            {
+                OfferingId = OfferingId,
+                Name = Name,
+                PublishStatus = PublishStatus,
+                SourceType = SourceType,
+                JsonMetadata = JsonMetadata,
+                Options = Options.ToString(Newtonsoft.Json.Formatting.None)
+            };
+        }
+    }
     public class PlaylistDTO
     {
         public string Id { get; set; }
diff --git a/ClassTranscribeServer/Controllers/TaskController.cs b/ClassTranscribeServer/Controllers/TaskController.cs
index 0ab36ac..0e9b811 100644
--- a/ClassTranscribeServer/Controllers/TaskController.cs
+++ b/ClassTranscribeServer/Controllers/TaskController.cs
@@ -157,6 +157,8 @@ public class PhraseHintsDTO
         //Future: [Authorize(Roles = Globals.ROLE_MEDIA_WORKER + "," + Globals.ROLE_ADMIN)]
         public async Task<ActionResult> UpdatePhraseHints(string videoId, PhraseHintsDTO phraseHintsDTO)
         {
+            if (videoId == null || phraseHintsDTO == null) return BadRequest("Missing parameters");
+
             Video video = await _context.Videos.FindAsync(videoId);
             string hints = phraseHintsDTO.PhraseHints ?? "";
                        
@@ -181,6 +183,8 @@ public async Task<ActionResult> UpdatePhraseHints(string videoId, PhraseHintsDTO
         //Future: [Authorize(Roles = Globals.ROLE_MEDIA_WORKER + "," + Globals.ROLE_ADMIN)]
         public async Task<ActionResult> UpdateGlossary(string videoId, JObject glossary)
         {
+            if (videoId == null || glossary == null) return BadRequest("Missing parameters");
+
             string glossaryAsString = glossary.ToString(0);
             Video video = await _context.Videos.FindAsync(videoId);
             if(video.HasGlossaryData())
@@ -216,6 +220,7 @@ public async Task<ActionResult<Object>> GetGlossary(string videoId) {
         //Future: [Authorize(Roles = Globals.ROLE_MEDIA_WORKER + "," + Globals.ROLE_ADMIN)]
         public async Task<ActionResult> UpdateGlossaryTimestamp(string videoId, JObject glossaryTimestamp)
         {
+            if (videoId == null || glossaryTimestamp == null) return BadRequest("Missing parameters");
             string glossaryTimestampAsString = glossaryTimestamp.ToString(0);
             Video video = await _context.Videos.FindAsync(videoId);
             if(video.HasGlossaryTimestamp())
diff --git a/ClassTranscribeServer/Program.cs b/ClassTranscribeServer/Program.cs
index ea087d0..e653519 100644
--- a/ClassTranscribeServer/Program.cs
+++ b/ClassTranscribeServer/Program.cs
@@ -23,7 +23,7 @@ public static IWebHostBuilder CreateWebHostBuilder(string[] args)
 
             string viewSQL = Environment.GetEnvironmentVariable("LogEntityFrameworkSQL") ?? "false";
 
-            if( viewSQL.Trim().ToLower() != "true") {
+            if( viewSQL.Trim().ToUpperInvariant() != "TRUE") {
                 
                 v.ConfigureLogging((context, logging) => {
                     logging.AddFilter("Microsoft.EntityFrameworkCore.Database.Command", LogLevel.Warning);
diff --git a/TaskEngine/Tasks/DownloadMediaTask.cs b/TaskEngine/Tasks/DownloadMediaTask.cs
index a5b698e..aef6f7c 100644
--- a/TaskEngine/Tasks/DownloadMediaTask.cs
+++ b/TaskEngine/Tasks/DownloadMediaTask.cs
@@ -146,9 +146,9 @@ protected override async Task OnConsume(string mediaId, TaskParameters taskParam
 
         public async Task<Video> DownloadKalturaVideo(string subdir, Media media)
         {
-            string? swapInfo = media.getOptionsAsJson()?.GetValue("swapStreams")?.ToString();
+            string? swapInfo = media.GetOptionsAsJson()?.GetValue("swapStreams")?.ToString();
             GetLogger().LogInformation($"DownloadKalturaVideo ({media.Id}): swap streams: {swapInfo}");
-            bool swapStreams = Boolean.Parse( media.getOptionsAsJson().GetValue("swapStreams").ToString());
+            bool swapStreams = Boolean.Parse( media.GetOptionsAsJson().GetValue("swapStreams").ToString());
             string? video2Url = null;
             string video1Url = media.JsonMetadata["downloadUrl"].ToString();
             try {
diff --git a/UnitTests/ClassTranscribeServer/ControllerTests/PlaylistsControllerTest.cs b/UnitTests/ClassTranscribeServer/ControllerTests/PlaylistsControllerTest.cs
index 3d0b3b5..4f5d02b 100644
--- a/UnitTests/ClassTranscribeServer/ControllerTests/PlaylistsControllerTest.cs
+++ b/UnitTests/ClassTranscribeServer/ControllerTests/PlaylistsControllerTest.cs
@@ -292,7 +292,7 @@ public async Task Put_Playlist_Success()
             var updatedPlaylist = _context.Playlists.Find(playlist.Id);
             Assert.Equal(update.Name, updatedPlaylist.Name);
             Assert.Equal(update.PublishStatus, updatedPlaylist.PublishStatus);
-            Assert.Equal(update.Options, updatedPlaylist.getOptionsAsJson());
+            Assert.Equal(update.Options, updatedPlaylist.GetOptionsAsJson());
             Assert.Equal(playlist.SourceType, updatedPlaylist.SourceType);
         }
 
@@ -322,36 +322,42 @@ public async Task Put_Playlist_Fail()
         [Fact]
         public async Task Post_Playlist_Success()
         {
-            var playlist = new Playlist
+            var playlist = new NewPlaylistDTO
             {
                 OfferingId = "offering",
                 SourceType = SourceType.Local,
                 Name = "foo",
-                Index = 0
+                JsonMetadata = JObject.Parse("{'JM':'XY'}"),
+                Options =JObject.Parse("{'OptionA':'A'}")
             };
 
             _context.Offerings.Add(new Offering { Id = playlist.OfferingId });
 
             var result = await _controller.PostPlaylist(playlist);
-            Assert.IsType<CreatedAtActionResult>(result.Result);
+            // Assert.IsType<ActionResult>(result);
 
-            var newPlaylist = _context.Playlists.Find(playlist.Id);
-            Assert.Equal(playlist, newPlaylist);
+            var newPlaylist = _context.Playlists.Find(result.Value.Id);
+            Assert.Equal(playlist.Name, newPlaylist.Name);
             Assert.Equal(PublishStatus.Published, playlist.PublishStatus);
-            Assert.Equal(Visibility.Visible, playlist.Visibility);
+            Assert.Equal(Visibility.Visible, newPlaylist.Visibility);
+            Assert.Equal(SourceType.Local, newPlaylist.SourceType);
+            Assert.Equal(playlist.OfferingId, newPlaylist.OfferingId);
+            Assert.Contains("OptionA", newPlaylist.Options);
+            Assert.Contains("XY", newPlaylist.JsonMetadata.ToString());
+
         }
 
         [Fact]
         public async Task Post_Playlist_Fail()
         {
             var result = await _controller.PostPlaylist(null);
-            Assert.IsType<BadRequestResult>(result.Result);
+            Assert.IsType<BadRequestObjectResult>(result.Result);
 
-            result = await _controller.PostPlaylist(new Playlist());
-            Assert.IsType<BadRequestResult>(result.Result);
+            result = await _controller.PostPlaylist(new NewPlaylistDTO());
+            Assert.IsType<BadRequestObjectResult>(result.Result);
 
-            result = await _controller.PostPlaylist(new Playlist() { OfferingId = "none" });
-            Assert.IsType<BadRequestResult>(result.Result);
+            result = await _controller.PostPlaylist(new NewPlaylistDTO() { OfferingId = "none" });
+            Assert.IsType<BadRequestObjectResult>(result.Result);
         }
 
         [Fact]