Skip to content

Commit

Permalink
Implemented updating books
Browse files Browse the repository at this point in the history
  • Loading branch information
apfohl committed Dec 19, 2023
1 parent adf0878 commit 44947bc
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 47 deletions.
80 changes: 33 additions & 47 deletions HotwiredBooks/Controllers/BooksController.cs
Original file line number Diff line number Diff line change
@@ -1,55 +1,49 @@
using HotwiredBooks.Attributes;
using HotwiredBooks.Components;
using HotwiredBooks.Extensions;
using HotwiredBooks.Models;
using HotwiredBooks.ViewModels;
using Microsoft.AspNetCore.Mvc;
using MonadicBits;

namespace HotwiredBooks.Controllers;

using static Functional;

public sealed class BooksController(IBooksRepository booksRepository) : Controller
{
private record FormData(string Title, string Author);

[HttpGet]
public async Task<ActionResult> Index() => View(new BooksIndexViewModel(
public async Task<IActionResult> Index() => View(new BooksIndexViewModel(
(await booksRepository.All()).OrderBy(book => book.Title)));

[HttpGet]
public ActionResult New() => View();
public IActionResult New() => View();

[HttpPost]
[ValidateAntiForgeryToken]
[TurboStreamResponse]
public async Task<ActionResult> Create(IFormCollection collection)
{
var book = await (await ParseFormData(collection))
.Match(
formData => booksRepository.Create(formData.Title, formData.Author),
() => Task.FromResult<Maybe<Book>>(Nothing)
);

return View(new BooksCreateViewModel(
book.Match(b => b, () => throw new ArgumentException()),
(await booksRepository.All()).Count())
);
}
public Task<IActionResult> Create(IFormCollection collection) =>
(
from formData in ParseFormData(collection)
from book in booksRepository.Create(formData.Title, formData.Author)
select book
)
.MapAsync<Book, IActionResult>(async book =>
View(new BooksCreateViewModel(book, (await booksRepository.All()).Count())))
.OrElse(StatusCode(500, "An unexpected error occurred on the server."));

[HttpGet]
public async Task<ActionResult> Edit(Guid id) =>
(await booksRepository.Lookup(id))
.Match(
book => View(new BooksEditViewModel(book)),
() => throw new ArgumentException()
);
public Task<IActionResult> Edit(Guid id) =>
booksRepository
.Lookup(id)
.MapAsync<Book, IActionResult>(book => View(new BooksEditViewModel(book)))
.OrElse(StatusCode(500, "An unexpected error occurred on the server."));

[HttpPatch, HttpPut]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Update(Guid id, IFormCollection collection)
{
var saveBook =
[TurboStreamResponse]
public Task<IActionResult> Update(Guid id, IFormCollection collection) =>
(
from formData in ParseFormData(collection)
from book in booksRepository.Lookup(id)
from updatedBook in booksRepository.Update(
Expand All @@ -59,34 +53,26 @@ book with
Author = formData.Author
}
)
select updatedBook;

var savedBook = await saveBook;

return Created();
}
select updatedBook
)
.MapAsync<Book, IActionResult>(book => View(new BooksEditViewModel(book)))
.OrElse(StatusCode(500, "An unexpected error occurred on the server."));

[HttpPost]
[ValidateAntiForgeryToken]
[TurboStreamResponse]
public async Task<ActionResult> Delete(Guid id) =>
await (await booksRepository
.Lookup(id)
.BindAsync(booksRepository.Delete))
.Match(
async book => View(
new BooksDeleteViewModel(
book,
(await booksRepository.All()).Count()
)
),
() => throw new ArgumentException()
);
public Task<IActionResult> Delete(Guid id) =>
booksRepository
.Lookup(id)
.BindAsync(booksRepository.Delete)
.MapAsync<Book, IActionResult>(async book =>
View(new BooksDeleteViewModel(book, (await booksRepository.All()).Count())))
.OrElse(StatusCode(500, "An unexpected error occurred on the server."));

private static Task<Maybe<FormData>> ParseFormData(IFormCollection collection) =>
Task.FromResult(
from title in collection.TryGetValue("title", out var t) ? t.Just() : Nothing
from author in collection.TryGetValue("author", out var a) ? a.Just() : Nothing
from title in collection.JustGetValue("title")
from author in collection.JustGetValue("author")
select new FormData(title, author)
);
}
16 changes: 16 additions & 0 deletions HotwiredBooks/Extensions/FunctionalExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Microsoft.Extensions.Primitives;
using MonadicBits;

namespace HotwiredBooks.Extensions;
Expand All @@ -7,4 +8,19 @@ namespace HotwiredBooks.Extensions;
public static class FunctionalExtensions
{
public static Maybe<T> ToMaybe<T>(this T value) => value?.Just() ?? Nothing;

public static Maybe<StringValues> JustGetValue(this IFormCollection collection, string key) =>
collection.TryGetValue(key, out var value) ? value.Just() : Nothing;

public static T OrElse<T>(this Maybe<T> maybe, T orElse) =>
maybe.Match(value => value, () => orElse);

public static T OrElse<T>(this Maybe<T> maybe, Func<T> orElse) =>
maybe.Match(value => value, orElse);

public static async Task<T> OrElse<T>(this Task<Maybe<T>> maybe, Func<T> orElse) =>
(await maybe).Match(value => value, orElse);

public static async Task<T> OrElse<T>(this Task<Maybe<T>> maybe, T orElse) =>
(await maybe).Match(value => value, () => orElse);
}
9 changes: 9 additions & 0 deletions HotwiredBooks/Views/Books/Update.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@using HotwiredBooks.Extensions
@model HotwiredBooks.ViewModels.BooksEditViewModel
@{
Layout = null;
}

<turbo-stream action="Update" target="@Html.DomId(Model.Book)">
<vc:book book="@Model.Book"/>
</turbo-stream>

0 comments on commit 44947bc

Please sign in to comment.