Use Refit for APIs, make data/API stack async

This commit is contained in:
Neil Brommer 2021-11-28 22:32:21 -08:00
parent 9c4f01ab13
commit b00158daa7
17 changed files with 196 additions and 124 deletions

View file

@ -1,6 +1,8 @@
@using Start.Shared @using Start.Shared
@using System.IO @using Start.Shared.Api
@inject HttpClient Http @using Refit
@inject IBookmarkContainersApi bookmarkContainersApi
<Dialog Title="Create Container" Active="this.IsOpen" OnClose="this.OnDialogClose"> <Dialog Title="Create Container" Active="this.IsOpen" OnClose="this.OnDialogClose">
<EditForm Model="this.model" OnValidSubmit="this.OnSubmit"> <EditForm Model="this.model" OnValidSubmit="this.OnSubmit">
@ -50,16 +52,10 @@
protected async void OnSubmit() protected async void OnSubmit()
{ {
HttpResponseMessage response = await Http ApiResponse<BookmarkContainerDto?> apiResponse = await bookmarkContainersApi
.PostAsJsonAsync("BookmarkContainers/Create", model.Title); .CreateBookmarkContainer(model.Title);
Stream stream = response.RequestMessage!.Content!.ReadAsStream(); BookmarkContainerDto? container = apiResponse.Content;
StreamReader reader = new StreamReader(stream);
Console.WriteLine(reader.ReadToEnd());
BookmarkContainerDto? container = await response
!.Content
!.ReadFromJsonAsync<BookmarkContainerDto>();
if (container == null) if (container == null)
{ {

View file

@ -1,5 +1,8 @@
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication @using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Start.Shared.Api
@inject HttpClient Http @inject HttpClient Http
@inject IBookmarkContainersApi bookmarkContainersApi
@{ string title = $"Delete Container \"{this.ContainerTitle}\""; } @{ string title = $"Delete Container \"{this.ContainerTitle}\""; }
@ -41,8 +44,8 @@
{ {
try try
{ {
HttpResponseMessage result = await Http HttpResponseMessage result = await bookmarkContainersApi
.DeleteAsync($"BookmarkContainers/Delete/{this.BookmarkContainerId}"); .DeleteBookmarkContainer(this.BookmarkContainerId);
if (result.StatusCode == System.Net.HttpStatusCode.OK) if (result.StatusCode == System.Net.HttpStatusCode.OK)
{ {

View file

@ -1,11 +1,16 @@
@page "/Start" @page "/Start"
@using Microsoft.AspNetCore.Authorization @using System.Collections.Generic
@using System.Linq
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication @using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Start.Client.Components @using Start.Client.Components
@using Start.Shared @using Start.Shared
@attribute [Authorize] @using Start.Shared.Api
@inject HttpClient Http @using Refit
@* Distiguish from Refit.Authorize *@
@attribute [Microsoft.AspNetCore.Authorization.Authorize]
@inject Blazored.LocalStorage.ILocalStorageService localStorage @inject Blazored.LocalStorage.ILocalStorageService localStorage
@inject IBookmarkContainersApi bookmarkContainersApi
@if (bookmarkContainers == null) @if (bookmarkContainers == null)
{ {
@ -101,9 +106,10 @@ else
{ {
try try
{ {
this.bookmarkContainers = await Http ApiResponse<IEnumerable<BookmarkContainerDto>> response = await bookmarkContainersApi
.GetFromJsonAsync<IList<BookmarkContainerDto>>( .GetAllBookmarkContainers();
"BookmarkContainers");
this.bookmarkContainers = response.Content?.ToList();
if (this.bookmarkContainers == null || !this.bookmarkContainers.Any()) if (this.bookmarkContainers == null || !this.bookmarkContainers.Any())
{ {
@ -120,13 +126,10 @@ else
protected async Task CreateDefaultContainer() protected async Task CreateDefaultContainer()
{ {
HttpResponseMessage response = await Http ApiResponse<BookmarkContainerDto?> response = await bookmarkContainersApi
.PostAsJsonAsync("BookmarkContainers/Create", "Default"); .CreateBookmarkContainer("Default");
BookmarkContainerDto? container = await response BookmarkContainerDto? container = response.Content;
.RequestMessage
!.Content
!.ReadFromJsonAsync<BookmarkContainerDto?>();
if (container != null) if (container != null)
await this.OnContainerSelected(container.BookmarkContainerId); await this.OnContainerSelected(container.BookmarkContainerId);
@ -139,12 +142,13 @@ else
if (!this.bookmarkContainers?.Any(bc => bc.BookmarkContainerId == bookmarkContainerId) ?? false) if (!this.bookmarkContainers?.Any(bc => bc.BookmarkContainerId == bookmarkContainerId) ?? false)
bookmarkContainerId = this.bookmarkContainers?.First().BookmarkContainerId ?? bookmarkContainerId; bookmarkContainerId = this.bookmarkContainers?.First().BookmarkContainerId ?? bookmarkContainerId;
BookmarkContainerDto? bookmarkContainer = await Http ApiResponse<BookmarkContainerDto?> response = await bookmarkContainersApi
.GetFromJsonAsync<BookmarkContainerDto?>( .GetBookmarkContainer(bookmarkContainerId);
$"BookmarkContainers/{bookmarkContainerId}");
BookmarkContainerDto? container = response.Content;
await this.SetSelectedContainer(bookmarkContainerId); await this.SetSelectedContainer(bookmarkContainerId);
this.selectedBookmarkContainer = bookmarkContainer; this.selectedBookmarkContainer = container;
} }
catch (AccessTokenNotAvailableException e) catch (AccessTokenNotAvailableException e)
{ {

View file

@ -5,6 +5,8 @@ using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Blazored.LocalStorage; using Blazored.LocalStorage;
using Refit;
using Start.Shared.Api;
namespace Start.Client { namespace Start.Client {
public class Program { public class Program {
@ -16,12 +18,26 @@ namespace Start.Client {
client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)) client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>(); .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
// Supply HttpClient instances that include access tokens when making requests to the server project // Supply HttpClient instances that include access tokens when making requests to the
// server project
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>() builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("Start.ServerAPI")); .CreateClient("Start.ServerAPI"));
// Blazor will throw an error if a relative URI is used, so we have to get the base
// address for building the API paths
Uri baseUri = new(builder.HostEnvironment.BaseAddress);
builder.Services.AddRefitClient<IBookmarkContainersApi>()
.ConfigureHttpClient(c => {
c.BaseAddress = new Uri(baseUri, "BookmarkContainers");
})
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
builder.Services.AddRefitClient<IBookmarksApi>()
.ConfigureHttpClient(c => { c.BaseAddress = new Uri(baseUri, "Bookmarks"); })
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
builder.Services.AddApiAuthorization(); builder.Services.AddApiAuthorization();
builder.Services.AddBlazoredLocalStorage(); builder.Services.AddBlazoredLocalStorage();
await builder.Build().RunAsync(); await builder.Build().RunAsync();

View file

@ -17,6 +17,8 @@
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
<PackageReference Include="System.Net.Http.Json" Version="5.0.0" /> <PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
<PackageReference Include="Blazored.LocalStorage" Version="4.1.5" /> <PackageReference Include="Blazored.LocalStorage" Version="4.1.5" />
<PackageReference Include="Refit" Version="6.1.15" />
<PackageReference Include="Refit.HttpClientFactory" Version="6.1.15" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -29,6 +31,8 @@
<ItemGroup> <ItemGroup>
<None Remove="Blazored.LocalStorage" /> <None Remove="Blazored.LocalStorage" />
<None Remove="Refit" />
<None Remove="Refit.HttpClientFactory" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Remove="wwwroot\css\Spectre\" /> <Content Remove="wwwroot\css\Spectre\" />

View file

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -22,9 +23,9 @@ namespace Start.Server.Controllers {
[ProducesResponseType(StatusCodes.Status200OK, [ProducesResponseType(StatusCodes.Status200OK,
Type = typeof(IEnumerable<BookmarkContainerDto>))] Type = typeof(IEnumerable<BookmarkContainerDto>))]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetAllBookmarkContainers() { public async Task<IActionResult> GetAllBookmarkContainers() {
List<BookmarkContainerDto>? containers = this.bookmarkContainerService List<BookmarkContainerDto>? containers = (await this.bookmarkContainerService
.GetUserBookmarkContainers(this.GetAuthorizedUserId()) .GetUserBookmarkContainers(this.GetAuthorizedUserId()))
.Select(bc => bc.MapToDto()) .Select(bc => bc.MapToDto())
.ToList(); .ToList();
@ -38,9 +39,9 @@ namespace Start.Server.Controllers {
[Route("{bookmarkContainerId}")] [Route("{bookmarkContainerId}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BookmarkContainerDto))] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BookmarkContainerDto))]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetBookmarkContainer(int bookmarkContainerId) { public async Task<IActionResult> GetBookmarkContainer(int bookmarkContainerId) {
BookmarkContainerDto? container = this.bookmarkContainerService BookmarkContainerDto? container = (await this.bookmarkContainerService
.GetBookmarkContainer(this.GetAuthorizedUserId(), bookmarkContainerId, true, true) .GetBookmarkContainer(this.GetAuthorizedUserId(), bookmarkContainerId, true, true))
?.MapToDto(); ?.MapToDto();
if (container == null) if (container == null)
@ -53,9 +54,9 @@ namespace Start.Server.Controllers {
[Route("Create")] [Route("Create")]
[ProducesResponseType(StatusCodes.Status201Created, Type = typeof(BookmarkContainerDto))] [ProducesResponseType(StatusCodes.Status201Created, Type = typeof(BookmarkContainerDto))]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
public IActionResult CreateBookmarkContainer([FromBody] string title) { public async Task<IActionResult> CreateBookmarkContainer([FromBody] string title) {
BookmarkContainerDto? container = this.bookmarkContainerService BookmarkContainerDto? container = (await this.bookmarkContainerService
.CreateBookmarkContainer(this.GetAuthorizedUserId(), title) .CreateBookmarkContainer(this.GetAuthorizedUserId(), title))
?.MapToDto(); ?.MapToDto();
if (container == null) if (container == null)
@ -71,8 +72,8 @@ namespace Start.Server.Controllers {
[Route("Delete/{bookmarkContainerId}")] [Route("Delete/{bookmarkContainerId}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult DeleteBookmarkContainer(int bookmarkContainerId) { public async Task<IActionResult> DeleteBookmarkContainer(int bookmarkContainerId) {
bool res = this.bookmarkContainerService bool res = await this.bookmarkContainerService
.DeleteBookmarkContainer(this.GetAuthorizedUserId(), bookmarkContainerId); .DeleteBookmarkContainer(this.GetAuthorizedUserId(), bookmarkContainerId);
if (!res) if (!res)

View file

@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Threading.Tasks;
using System.Linq;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -25,9 +24,9 @@ namespace Start.Server.Controllers {
[Route("{bookmarkId}")] [Route("{bookmarkId}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BookmarkDto))] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BookmarkDto))]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetBookmark(int bookmarkId) { public async Task<IActionResult> GetBookmark(int bookmarkId) {
BookmarkDto? bookmark = this.bookmarkService BookmarkDto? bookmark = (await this.bookmarkService
.GetBookmark(this.GetAuthorizedUserId(), bookmarkId) .GetBookmark(this.GetAuthorizedUserId(), bookmarkId))
?.MapToDto(); ?.MapToDto();
if (bookmark == null) if (bookmark == null)
@ -40,10 +39,10 @@ namespace Start.Server.Controllers {
[Route("Create")] [Route("Create")]
[ProducesResponseType(StatusCodes.Status201Created, Type = typeof(BookmarkDto))] [ProducesResponseType(StatusCodes.Status201Created, Type = typeof(BookmarkDto))]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
public IActionResult CreateBookmark(string title, string url, string? notes, public async Task<IActionResult> CreateBookmark(string title, string url, string? notes,
int bookmarkGroupId) { int bookmarkGroupId) {
BookmarkDto? bookmark = this.bookmarkService BookmarkDto? bookmark = (await this.bookmarkService
.CreateBookmark(this.GetAuthorizedUserId(), title, url, notes, bookmarkGroupId) .CreateBookmark(this.GetAuthorizedUserId(), title, url, notes, bookmarkGroupId))
?.MapToDto(); ?.MapToDto();
if (bookmark == null) if (bookmark == null)
@ -58,8 +57,9 @@ namespace Start.Server.Controllers {
[Route("Delete/{bookmarkId}")] [Route("Delete/{bookmarkId}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult DeleteBookmark(int bookmarkId) { public async Task<IActionResult> DeleteBookmark(int bookmarkId) {
var res = this.bookmarkService.DeleteBookmark(this.GetAuthorizedUserId(), bookmarkId); var res = await this.bookmarkService
.DeleteBookmark(this.GetAuthorizedUserId(), bookmarkId);
if (!res) if (!res)
return NotFound(); return NotFound();

View file

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Start.Server.Data.Services.Interfaces; using Start.Server.Data.Services.Interfaces;
using Start.Server.Extensions; using Start.Server.Extensions;
@ -13,15 +14,15 @@ namespace Start.Server.Data.Services {
this.db = dbContext; this.db = dbContext;
} }
public BookmarkContainer? GetBookmarkContainer(string userId, public async Task<BookmarkContainer?> GetBookmarkContainer(string userId,
int bookmarkContainerId, bool includeGroups = false, bool includeBookmarks = false) { int bookmarkContainerId, bool includeGroups = false, bool includeBookmarks = false) {
BookmarkContainer? bookmarkContainer = this.db.BookmarkContainers BookmarkContainer? bookmarkContainer = await this.db.BookmarkContainers
.Where(bc => bc.BookmarkContainerId == bookmarkContainerId) .Where(bc => bc.BookmarkContainerId == bookmarkContainerId)
.If(includeGroups, q => q.Include(bc => bc.BookmarkGroups)) .If(includeGroups, q => q.Include(bc => bc.BookmarkGroups))
.If(includeBookmarks, q => q .If(includeBookmarks, q => q
.Include(bc => bc.BookmarkGroups) .Include(bc => bc.BookmarkGroups)
.ThenInclude(bg => bg.Bookmarks)) .ThenInclude(bg => bg.Bookmarks))
.SingleOrDefault(); .SingleOrDefaultAsync();
if (bookmarkContainer == null) if (bookmarkContainer == null)
return null; return null;
@ -33,31 +34,31 @@ namespace Start.Server.Data.Services {
return bookmarkContainer; return bookmarkContainer;
} }
public IList<BookmarkContainer> GetUserBookmarkContainers(string userId, public async Task<IList<BookmarkContainer>> GetUserBookmarkContainers(string userId,
bool includeGroups = false, bool includeBookmarks = false) { bool includeGroups = false, bool includeBookmarks = false) {
return this.db.BookmarkContainers return await this.db.BookmarkContainers
.Where(bc => bc.ApplicationUserId == userId) .Where(bc => bc.ApplicationUserId == userId)
.If(includeGroups, q => q.Include(bc => bc.BookmarkGroups)) .If(includeGroups, q => q.Include(bc => bc.BookmarkGroups))
.If(includeBookmarks, q => q .If(includeBookmarks, q => q
.Include(bc => bc.BookmarkGroups) .Include(bc => bc.BookmarkGroups)
.ThenInclude(bg => bg.Bookmarks)) .ThenInclude(bg => bg.Bookmarks))
.ToList(); .ToListAsync();
} }
public BookmarkContainer? CreateBookmarkContainer(string userId, public async Task<BookmarkContainer?> CreateBookmarkContainer(string userId,
string title) { string title) {
// No need to worry about ownership here // No need to worry about ownership here
BookmarkContainer newContainer = new(userId, title); BookmarkContainer newContainer = new(userId, title);
this.db.BookmarkContainers.Add(newContainer); await this.db.BookmarkContainers.AddAsync(newContainer);
this.db.SaveChanges(); await this.db.SaveChangesAsync();
return newContainer; return newContainer;
} }
public BookmarkContainer? UpdateBookmarkContainer(string userId, public async Task<BookmarkContainer?> UpdateBookmarkContainer(string userId,
BookmarkContainer bookmarkContainer) { BookmarkContainer bookmarkContainer) {
BookmarkContainer? exitingBookmarkContainer = this.db.BookmarkContainers BookmarkContainer? exitingBookmarkContainer = await this.db.BookmarkContainers
.SingleOrDefault(bc => bc.BookmarkContainerId .SingleOrDefaultAsync(bc => bc.BookmarkContainerId
== bookmarkContainer.BookmarkContainerId); == bookmarkContainer.BookmarkContainerId);
if (exitingBookmarkContainer == null if (exitingBookmarkContainer == null
@ -66,15 +67,15 @@ namespace Start.Server.Data.Services {
return null; return null;
this.db.Entry(bookmarkContainer).State = EntityState.Modified; this.db.Entry(bookmarkContainer).State = EntityState.Modified;
this.db.SaveChanges(); await this.db.SaveChangesAsync();
return bookmarkContainer; return bookmarkContainer;
} }
public bool DeleteBookmarkContainer(string userId, int bookmarkContainerId) { public async Task<bool> DeleteBookmarkContainer(string userId, int bookmarkContainerId) {
BookmarkContainer? bookmarkContainer = this.db.BookmarkContainers BookmarkContainer? bookmarkContainer = await this.db.BookmarkContainers
.Where(bc => bc.BookmarkContainerId == bookmarkContainerId) .Where(bc => bc.BookmarkContainerId == bookmarkContainerId)
.SingleOrDefault(); .SingleOrDefaultAsync();
if (bookmarkContainer == null) if (bookmarkContainer == null)
return false; return false;
@ -83,7 +84,7 @@ namespace Start.Server.Data.Services {
return false; return false;
this.db.BookmarkContainers.Remove(bookmarkContainer); this.db.BookmarkContainers.Remove(bookmarkContainer);
this.db.SaveChanges(); await this.db.SaveChangesAsync();
return true; return true;
} }

View file

@ -1,11 +1,10 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Start.Server.Data.Services.Interfaces; using Start.Server.Data.Services.Interfaces;
using Start.Server.Extensions; using Start.Server.Extensions;
using Start.Server.Models; using Start.Server.Models;
using Start.Shared;
namespace Start.Server.Data.Services { namespace Start.Server.Data.Services {
public class BookmarkGroupService : IBookmarkGroupService { public class BookmarkGroupService : IBookmarkGroupService {
@ -15,12 +14,12 @@ namespace Start.Server.Data.Services {
this.db = dbContext; this.db = dbContext;
} }
public BookmarkGroup? GetBookmarkGroup(string userId, int bookmarkGroupId, public async Task<BookmarkGroup?> GetBookmarkGroup(string userId, int bookmarkGroupId,
bool includeBookmarks = false) { bool includeBookmarks = false) {
BookmarkGroup? group = db.BookmarkGroups BookmarkGroup? group = await db.BookmarkGroups
.Where(bg => bg.BookmarkGroupId == bookmarkGroupId) .Where(bg => bg.BookmarkGroupId == bookmarkGroupId)
.If(includeBookmarks, q => q.Include(bg => bg.Bookmarks)) .If(includeBookmarks, q => q.Include(bg => bg.Bookmarks))
.SingleOrDefault(); .SingleOrDefaultAsync();
if (!BookmarkOwnershipTools.IsBookmarkGroupOwner(db, userId, bookmarkGroupId)) if (!BookmarkOwnershipTools.IsBookmarkGroupOwner(db, userId, bookmarkGroupId))
return null; return null;
@ -28,31 +27,31 @@ namespace Start.Server.Data.Services {
return group; return group;
} }
public IList<BookmarkGroup> GetUserBookmarkGroups(string userId, public async Task<IList<BookmarkGroup>> GetUserBookmarkGroups(string userId,
bool includeBookmarkGroups = false) { bool includeBookmarkGroups = false) {
return this.db.BookmarkGroups return await this.db.BookmarkGroups
.Where(bg => bg.BookmarkContainer!.ApplicationUserId == userId) .Where(bg => bg.BookmarkContainer!.ApplicationUserId == userId)
.If(includeBookmarkGroups, q => q.Include(bg => bg.Bookmarks)) .If(includeBookmarkGroups, q => q.Include(bg => bg.Bookmarks))
.ToList(); .ToListAsync();
} }
public BookmarkGroup? CreateBookmarkGroup(string userId, string title, public async Task<BookmarkGroup?> CreateBookmarkGroup(string userId, string title,
string color, int bookmarkContainerId) { string color, int bookmarkContainerId) {
if (!BookmarkOwnershipTools if (!BookmarkOwnershipTools
.IsBookmarkContainerOwner(this.db, userId, bookmarkContainerId)) .IsBookmarkContainerOwner(this.db, userId, bookmarkContainerId))
return null; return null;
BookmarkGroup newBookmarkGroup = new(title, color, bookmarkContainerId); BookmarkGroup newBookmarkGroup = new(title, color, bookmarkContainerId);
this.db.BookmarkGroups.Add(newBookmarkGroup); await this.db.BookmarkGroups.AddAsync(newBookmarkGroup);
this.db.SaveChanges(); await this.db.SaveChangesAsync();
return newBookmarkGroup; return newBookmarkGroup;
} }
public BookmarkGroup? UpdateBookmarkGroup(string userId, public async Task<BookmarkGroup?> UpdateBookmarkGroup(string userId,
BookmarkGroup bookmarkGroup) { BookmarkGroup bookmarkGroup) {
BookmarkGroup? existingGroup = this.db.BookmarkGroups BookmarkGroup? existingGroup = await this.db.BookmarkGroups
.SingleOrDefault(bg => bg.BookmarkGroupId == bookmarkGroup.BookmarkGroupId); .SingleOrDefaultAsync(bg => bg.BookmarkGroupId == bookmarkGroup.BookmarkGroupId);
if (existingGroup == null) if (existingGroup == null)
return null; return null;
@ -66,14 +65,14 @@ namespace Start.Server.Data.Services {
return null; return null;
this.db.Entry(bookmarkGroup).State = EntityState.Modified; this.db.Entry(bookmarkGroup).State = EntityState.Modified;
this.db.SaveChanges(); await this.db.SaveChangesAsync();
return bookmarkGroup; return bookmarkGroup;
} }
public bool DeleteBookmarkGroup(string userId, int bookmarkGroupId) { public async Task<bool> DeleteBookmarkGroup(string userId, int bookmarkGroupId) {
BookmarkGroup? bookmarkGroup = this.db.BookmarkGroups BookmarkGroup? bookmarkGroup = await this.db.BookmarkGroups
.SingleOrDefault(bg => bg.BookmarkGroupId == bookmarkGroupId); .SingleOrDefaultAsync(bg => bg.BookmarkGroupId == bookmarkGroupId);
if (bookmarkGroup == null) if (bookmarkGroup == null)
return false; return false;
@ -82,7 +81,7 @@ namespace Start.Server.Data.Services {
return false; return false;
this.db.BookmarkGroups.Remove(bookmarkGroup); this.db.BookmarkGroups.Remove(bookmarkGroup);
this.db.SaveChanges(); await this.db.SaveChangesAsync();
return true; return true;
} }

View file

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Start.Server.Data.Services.Interfaces; using Start.Server.Data.Services.Interfaces;
using Start.Server.Models; using Start.Server.Models;
@ -12,29 +13,29 @@ namespace Start.Server.Data.Services {
this.db = dbContext; this.db = dbContext;
} }
public Bookmark? GetBookmark(string userId, int bookmarkId) { public async Task<Bookmark?> GetBookmark(string userId, int bookmarkId) {
if (!BookmarkOwnershipTools.IsBookmarkOwner(this.db, userId, bookmarkId)) if (!BookmarkOwnershipTools.IsBookmarkOwner(this.db, userId, bookmarkId))
return null; return null;
return this.db.Bookmarks return await this.db.Bookmarks
.SingleOrDefault(b => b.BookmarkId == bookmarkId); .SingleOrDefaultAsync(b => b.BookmarkId == bookmarkId);
} }
public IList<Bookmark> GetUserBookmarks(string userId) { public async Task<IList<Bookmark>> GetUserBookmarks(string userId) {
return this.db.Bookmarks return await this.db.Bookmarks
.Where(b => b.BookmarkGroup!.BookmarkContainer!.ApplicationUserId == userId) .Where(b => b.BookmarkGroup!.BookmarkContainer!.ApplicationUserId == userId)
.ToList(); .ToListAsync();
} }
public Bookmark? CreateBookmark(string userId, string title, string url, string? notes, public async Task<Bookmark?> CreateBookmark(string userId, string title, string url, string? notes,
int bookmarkGroupId) { int bookmarkGroupId) {
if (!BookmarkOwnershipTools.IsBookmarkGroupOwner(this.db, userId, bookmarkGroupId)) if (!BookmarkOwnershipTools.IsBookmarkGroupOwner(this.db, userId, bookmarkGroupId))
return null; return null;
Bookmark newBookmark = new(title, url, bookmarkGroupId); Bookmark newBookmark = new(title, url, bookmarkGroupId);
db.Bookmarks.Add(newBookmark); await db.Bookmarks.AddAsync(newBookmark);
db.SaveChanges(); await db.SaveChangesAsync();
if (newBookmark.BookmarkId <= 0) if (newBookmark.BookmarkId <= 0)
return null; return null;
@ -42,7 +43,7 @@ namespace Start.Server.Data.Services {
return newBookmark; return newBookmark;
} }
public Bookmark? UpdateBookmark(string userId, Bookmark bookmark) { public async Task<Bookmark?> UpdateBookmark(string userId, Bookmark bookmark) {
Bookmark? existingBookmark = db.Bookmarks Bookmark? existingBookmark = db.Bookmarks
.SingleOrDefault(b => b.BookmarkId == bookmark.BookmarkId); .SingleOrDefault(b => b.BookmarkId == bookmark.BookmarkId);
@ -55,12 +56,12 @@ namespace Start.Server.Data.Services {
return null; return null;
db.Entry(bookmark).State = EntityState.Modified; db.Entry(bookmark).State = EntityState.Modified;
db.SaveChanges(); await db.SaveChangesAsync();
return bookmark; return bookmark;
} }
public bool DeleteBookmark(string userId, int bookmarkId) { public async Task<bool> DeleteBookmark(string userId, int bookmarkId) {
Bookmark? bookmark = db.Bookmarks Bookmark? bookmark = db.Bookmarks
.SingleOrDefault(b => b.BookmarkId == bookmarkId); .SingleOrDefault(b => b.BookmarkId == bookmarkId);
@ -71,7 +72,7 @@ namespace Start.Server.Data.Services {
return false; return false;
db.Bookmarks.Remove(bookmark); db.Bookmarks.Remove(bookmark);
db.SaveChanges(); await db.SaveChangesAsync();
return true; return true;
} }

View file

@ -1,19 +1,18 @@
using System; using System.Collections.Generic;
using System.Collections.Generic; using System.Threading.Tasks;
using Start.Server.Models; using Start.Server.Models;
using Start.Shared;
namespace Start.Server.Data.Services.Interfaces { namespace Start.Server.Data.Services.Interfaces {
public interface IBookmarkContainerService { public interface IBookmarkContainerService {
public BookmarkContainer? GetBookmarkContainer(string userId, public Task<BookmarkContainer?> GetBookmarkContainer(string userId,
int bookmarkContainerId, bool includeGroups = false, bool includeBookmarks = false); int bookmarkContainerId, bool includeGroups = false, bool includeBookmarks = false);
public IList<BookmarkContainer> GetUserBookmarkContainers(string userId, public Task<IList<BookmarkContainer>> GetUserBookmarkContainers(string userId,
bool includeGroups = false, bool includeBookmarks = false); bool includeGroups = false, bool includeBookmarks = false);
public BookmarkContainer? CreateBookmarkContainer(string userId, public Task<BookmarkContainer?> CreateBookmarkContainer(string userId,
string title); string title);
public BookmarkContainer? UpdateBookmarkContainer(string userId, public Task<BookmarkContainer?> UpdateBookmarkContainer(string userId,
BookmarkContainer bookmarkContainer); BookmarkContainer bookmarkContainer);
public bool DeleteBookmarkContainer(string userId, int bookmarkContainerId); public Task<bool> DeleteBookmarkContainer(string userId, int bookmarkContainerId);
} }
} }

View file

@ -1,19 +1,20 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using Start.Server.Models; using Start.Server.Models;
using Start.Shared; using Start.Shared;
namespace Start.Server.Data.Services.Interfaces { namespace Start.Server.Data.Services.Interfaces {
public interface IBookmarkGroupService { public interface IBookmarkGroupService {
public BookmarkGroup? GetBookmarkGroup(string userId, public Task<BookmarkGroup?> GetBookmarkGroup(string userId,
int bookmarkGroupId, bool includeBookmarks = false); int bookmarkGroupId, bool includeBookmarks = false);
public IList<BookmarkGroup> GetUserBookmarkGroups(string userId, public Task<IList<BookmarkGroup>> GetUserBookmarkGroups(string userId,
bool includeBookmarks = false); bool includeBookmarks = false);
public BookmarkGroup? CreateBookmarkGroup(string userId, string title, public Task<BookmarkGroup?> CreateBookmarkGroup(string userId, string title,
string color, int bookmarkContainerId); string color, int bookmarkContainerId);
public BookmarkGroup? UpdateBookmarkGroup(string userId, public Task<BookmarkGroup?> UpdateBookmarkGroup(string userId,
BookmarkGroup bookmarkGroup); BookmarkGroup bookmarkGroup);
public bool DeleteBookmarkGroup(string userId, int bookmarkGroupId); public Task<bool> DeleteBookmarkGroup(string userId, int bookmarkGroupId);
} }
} }

View file

@ -1,16 +1,15 @@
using System; using System.Collections.Generic;
using System.Collections.Generic; using System.Threading.Tasks;
using Start.Server.Models; using Start.Server.Models;
using Start.Shared;
namespace Start.Server.Data.Services.Interfaces { namespace Start.Server.Data.Services.Interfaces {
public interface IBookmarkService { public interface IBookmarkService {
public Bookmark? GetBookmark(string userId, int bookmarkId); public Task<Bookmark?> GetBookmark(string userId, int bookmarkId);
public IList<Bookmark> GetUserBookmarks(string userId); public Task<IList<Bookmark>> GetUserBookmarks(string userId);
public Bookmark? CreateBookmark(string userId, string title, string url, public Task<Bookmark?> CreateBookmark(string userId, string title, string url,
string? notes, int bookmarkGroupId); string? notes, int bookmarkGroupId);
public Bookmark? UpdateBookmark(string userId, Bookmark bookmark); public Task<Bookmark?> UpdateBookmark(string userId, Bookmark bookmark);
public bool DeleteBookmark(string userId, int bookmarkId); public Task<bool> DeleteBookmark(string userId, int bookmarkId);
} }
} }

View file

@ -9,6 +9,8 @@ using Start.Server.Data;
using Start.Server.Models; using Start.Server.Models;
using Start.Server.Data.Services; using Start.Server.Data.Services;
using Start.Server.Data.Services.Interfaces; using Start.Server.Data.Services.Interfaces;
using Refit;
using Start.Shared.Api;
namespace Start.Server { namespace Start.Server {
public class Startup { public class Startup {

View file

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Refit;
namespace Start.Shared.Api {
public interface IBookmarkContainersApi {
[Get("/")]
Task<ApiResponse<IEnumerable<BookmarkContainerDto>>> GetAllBookmarkContainers();
[Get("/{bookmarkContainerId}")]
Task<ApiResponse<BookmarkContainerDto?>> GetBookmarkContainer(int bookmarkContainerId);
[Post("/Create")]
Task<ApiResponse<BookmarkContainerDto?>> CreateBookmarkContainer(
[Body(BodySerializationMethod.Serialized)] string title);
[Delete("/Delete/{bookmarkContainerId}")]
Task<HttpResponseMessage> DeleteBookmarkContainer(int bookmarkContainerId);
}
}

View file

@ -0,0 +1,15 @@
using System.Threading.Tasks;
using Refit;
namespace Start.Shared.Api {
public interface IBookmarksApi {
[Get("{bookmarkId}")]
Task<BookmarkDto?> GetBookmark(int bookmarkId);
[Post("/Create")]
Task CreateBookmark(string title, string url, string? notes, int bookmarkGroupId);
[Delete("/Delete/{bookmarkId}")]
Task DeleteBookmark(int bookmarkId);
}
}

View file

@ -9,4 +9,14 @@
<ItemGroup> <ItemGroup>
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Remove="Refit" />
<None Remove="Api\" />
</ItemGroup>
<ItemGroup>
<Folder Include="Api\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Refit" Version="6.1.15" />
</ItemGroup>
</Project> </Project>