release/1.0.7 #34
12 changed files with 43 additions and 43 deletions
refactor(model): rename Yavsc.Blogspot.BlogPost to BlogPostDto
When commit 0e95e283 moved BlogPost from PostIt.Models to
Yavsc.Blogspot, it created an unfortunate collision with the
server-side EF entity Yavsc.Models.Blog.BlogPost. The two
classes have nothing in common beyond the name; the DTO is
the wire shape PostIt exchanges with the Blogs API, the EF
entity is the persistence model. Server code that imports both
namespaces (BlogSpotService.cs, etc.) ended up with 'BlogPost
is an ambiguous reference between X and Y' errors.
Renaming the client DTO to BlogPostDto (matching the
naming convention of the other DTOs in Yavsc.Api.Client.Dtos
— CircleDto, CircleAuthorizationDto, UserSearchResultDto)
disambiguates without renaming the EF entity on the server.
The namespace stays Yavsc.Blogspot; only the class name
changes. All call sites (client code, tests, XAML DataTemplates,
XML doc comments) are updated mechanically.
commit
1b289c1387
|
|
@ -97,7 +97,7 @@ public class BearerScopeTests
|
||||||
// CapturingHttpHandler is the assertion point. It
|
// CapturingHttpHandler is the assertion point. It
|
||||||
// records the first request's Authorization header and
|
// records the first request's Authorization header and
|
||||||
// returns 200 with an empty array (BlogApiClient
|
// returns 200 with an empty array (BlogApiClient
|
||||||
// deserialises to List<BlogPost>).
|
// deserialises to List<BlogPostDto>).
|
||||||
var captured = new CapturingHttpHandler();
|
var captured = new CapturingHttpHandler();
|
||||||
var client = new YavscApiClient(
|
var client = new YavscApiClient(
|
||||||
settings,
|
settings,
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ internal sealed class CallRecorder
|
||||||
|
|
||||||
/// <summary>Test fake that records every CallAsync invocation
|
/// <summary>Test fake that records every CallAsync invocation
|
||||||
/// and answers them with a canned sequence: the first call gets
|
/// and answers them with a canned sequence: the first call gets
|
||||||
/// a server-issued BlogPost (Id=42), the second call gets a
|
/// a server-issued BlogPostDto (Id=42), the second call gets a
|
||||||
/// single-element list containing that post. Used by the ViewModel
|
/// single-element list containing that post. Used by the ViewModel
|
||||||
/// tests and the headless UI test to capture exactly what the
|
/// tests and the headless UI test to capture exactly what the
|
||||||
/// Save button posts to the server.</summary>
|
/// Save button posts to the server.</summary>
|
||||||
|
|
@ -44,20 +44,20 @@ internal sealed class RecordingYavscApiClient : YavscApiClient
|
||||||
public override Task<T> CallAsync<T>(HttpMethod method, string path, object? body = null, CancellationToken ct = default)
|
public override Task<T> CallAsync<T>(HttpMethod method, string path, object? body = null, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
_recorder.Calls.Add((method, path, body));
|
_recorder.Calls.Add((method, path, body));
|
||||||
// BlogPost? boxes to BlogPost at runtime, so we test the
|
// BlogPostDto? boxes to BlogPostDto at runtime, so we test the
|
||||||
// non-nullable type — typeof(BlogPost?) is a C# error
|
// non-nullable type — typeof(BlogPostDto?) is a C# error
|
||||||
// (CS8639: "typeof cannot be used on a nullable reference
|
// (CS8639: "typeof cannot be used on a nullable reference
|
||||||
// type").
|
// type").
|
||||||
if (typeof(T) == typeof(BlogPost))
|
if (typeof(T) == typeof(BlogPostDto))
|
||||||
return Task.FromResult((T)(object)new BlogPost
|
return Task.FromResult((T)(object)new BlogPostDto
|
||||||
{
|
{
|
||||||
Id = 42,
|
Id = 42,
|
||||||
Title = "Mon premier billet",
|
Title = "Mon premier billet",
|
||||||
AuthorId = "tester",
|
AuthorId = "tester",
|
||||||
Article = "Contenu du billet de test.",
|
Article = "Contenu du billet de test.",
|
||||||
});
|
});
|
||||||
if (typeof(T) == typeof(List<BlogPost>))
|
if (typeof(T) == typeof(List<BlogPostDto>))
|
||||||
return Task.FromResult((T)(object)new List<BlogPost>
|
return Task.FromResult((T)(object)new List<BlogPostDto>
|
||||||
{
|
{
|
||||||
new() { Id = 42, Title = "Mon premier billet" }
|
new() { Id = 42, Title = "Mon premier billet" }
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace PostIt.Tests;
|
||||||
/// in which a brand-new post can be created), the binding has
|
/// in which a brand-new post can be created), the binding has
|
||||||
/// no target and the user's keystrokes are silently dropped.
|
/// no target and the user's keystrokes are silently dropped.
|
||||||
/// Clicking "Save" then routes to the VM branch
|
/// Clicking "Save" then routes to the VM branch
|
||||||
/// <c>if (SelectedPost is null) { new BlogPost { Title = string.Empty, ... } }</c>
|
/// <c>if (SelectedPost is null) { new BlogPostDto { Title = string.Empty, ... } }</c>
|
||||||
/// which the controller rejects with 400 "The Title field is
|
/// which the controller rejects with 400 "The Title field is
|
||||||
/// required." This test fails on that branch today and will
|
/// required." This test fails on that branch today and will
|
||||||
/// pass once the VM owns a dedicated <c>Title</c>/<c>Article</c>
|
/// pass once the VM owns a dedicated <c>Title</c>/<c>Article</c>
|
||||||
|
|
@ -77,14 +77,14 @@ public class MainPageSaveTests
|
||||||
// we inspect the recorder.
|
// we inspect the recorder.
|
||||||
await Task.Delay(200);
|
await Task.Delay(200);
|
||||||
|
|
||||||
// Assert: the first POST to "blog" carried a BlogPost
|
// Assert: the first POST to "blog" carried a BlogPostDto
|
||||||
// whose Title is exactly what the user typed. The bug
|
// whose Title is exactly what the user typed. The bug
|
||||||
// fails this assertion with Title == string.Empty.
|
// fails this assertion with Title == string.Empty.
|
||||||
Assert.NotEmpty(recorder.Calls);
|
Assert.NotEmpty(recorder.Calls);
|
||||||
var (method, path, body) = recorder.FirstCall;
|
var (method, path, body) = recorder.FirstCall;
|
||||||
Assert.Equal(HttpMethod.Post, method);
|
Assert.Equal(HttpMethod.Post, method);
|
||||||
Assert.Equal("blog", path);
|
Assert.Equal("blog", path);
|
||||||
var sent = Assert.IsType<BlogPost>(body);
|
var sent = Assert.IsType<BlogPostDto>(body);
|
||||||
Assert.Equal(typed, sent.Title);
|
Assert.Equal(typed, sent.Title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ public class PostItViewModelTests
|
||||||
var blog = new BlogApiClient(fakeApi, "http://localhost/");
|
var blog = new BlogApiClient(fakeApi, "http://localhost/");
|
||||||
var viewModel = new MainPageViewModel(blog);
|
var viewModel = new MainPageViewModel(blog);
|
||||||
|
|
||||||
viewModel.Posts.Add(new BlogPost { Id = 1, Title = "First post", Article = "Hello world", AuthorId = "alice" });
|
viewModel.Posts.Add(new BlogPostDto { Id = 1, Title = "First post", Article = "Hello world", AuthorId = "alice" });
|
||||||
viewModel.Posts.Add(new BlogPost { Id = 2, Title = "Second post", Article = "Nothing here", AuthorId = "bob" });
|
viewModel.Posts.Add(new BlogPostDto { Id = 2, Title = "Second post", Article = "Nothing here", AuthorId = "bob" });
|
||||||
viewModel.Posts.Add(new BlogPost { Id = 3, Title = "Third post", Article = "Search me", AuthorId = "carol" });
|
viewModel.Posts.Add(new BlogPostDto { Id = 3, Title = "Third post", Article = "Search me", AuthorId = "carol" });
|
||||||
|
|
||||||
viewModel.SearchText = "search";
|
viewModel.SearchText = "search";
|
||||||
viewModel.SearchCommand.Execute(null);
|
viewModel.SearchCommand.Execute(null);
|
||||||
|
|
@ -41,7 +41,7 @@ public class PostItViewModelTests
|
||||||
// The new BlogApiClient delegates transport to YavscApiClient.
|
// The new BlogApiClient delegates transport to YavscApiClient.
|
||||||
// We feed it a fake YavscApiClient that returns the expected
|
// We feed it a fake YavscApiClient that returns the expected
|
||||||
// list straight from CallAsync.
|
// list straight from CallAsync.
|
||||||
var expected = new List<BlogPost>
|
var expected = new List<BlogPostDto>
|
||||||
{
|
{
|
||||||
new() { Id = 1, Title = "Hello" },
|
new() { Id = 1, Title = "Hello" },
|
||||||
new() { Id = 2, Title = "World" }
|
new() { Id = 2, Title = "World" }
|
||||||
|
|
@ -77,8 +77,8 @@ public class PostItViewModelTests
|
||||||
/// <summary>Test fake that hands back a canned list of posts from any CallAsync.</summary>
|
/// <summary>Test fake that hands back a canned list of posts from any CallAsync.</summary>
|
||||||
private sealed class StubYavscApiClient : YavscApiClient
|
private sealed class StubYavscApiClient : YavscApiClient
|
||||||
{
|
{
|
||||||
private readonly List<BlogPost> _posts;
|
private readonly List<BlogPostDto> _posts;
|
||||||
public StubYavscApiClient(List<BlogPost> posts)
|
public StubYavscApiClient(List<BlogPostDto> posts)
|
||||||
: base(
|
: base(
|
||||||
new Settings
|
new Settings
|
||||||
{
|
{
|
||||||
|
|
@ -98,7 +98,7 @@ public class PostItViewModelTests
|
||||||
{
|
{
|
||||||
// The canned fake only knows about a list of posts; the
|
// The canned fake only knows about a list of posts; the
|
||||||
// BlogApiClient test asserts on that list directly.
|
// BlogApiClient test asserts on that list directly.
|
||||||
if (typeof(T) == typeof(List<BlogPost>))
|
if (typeof(T) == typeof(List<BlogPostDto>))
|
||||||
return Task.FromResult((T)(object)_posts);
|
return Task.FromResult((T)(object)_posts);
|
||||||
return Task.FromResult(default(T)!);
|
return Task.FromResult(default(T)!);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ public partial class MainPageViewModel : ViewModelBase
|
||||||
/// previous "{Binding SelectedPost.Title}" binding, the user's
|
/// previous "{Binding SelectedPost.Title}" binding, the user's
|
||||||
/// keystrokes were silently dropped whenever
|
/// keystrokes were silently dropped whenever
|
||||||
/// <c>SelectedPost was null</c>, which made the editor a trap
|
/// <c>SelectedPost was null</c>, which made the editor a trap
|
||||||
/// and caused Save to POST a <c>BlogPost</c> with an empty
|
/// and caused Save to POST a <c>BlogPostDto</c> with an empty
|
||||||
/// title — hence the 400 "The Title field is required".</summary>
|
/// title — hence the 400 "The Title field is required".</summary>
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial string DraftTitle { get; set; }
|
public partial string DraftTitle { get; set; }
|
||||||
|
|
@ -47,13 +47,13 @@ public partial class MainPageViewModel : ViewModelBase
|
||||||
public partial string SearchText { get; set; }
|
public partial string SearchText { get; set; }
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial ObservableCollection<BlogPost> Posts { get; set; }
|
public partial ObservableCollection<BlogPostDto> Posts { get; set; }
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial ObservableCollection<BlogPost> FilteredPosts { get; set; }
|
public partial ObservableCollection<BlogPostDto> FilteredPosts { get; set; }
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial BlogPost? SelectedPost { get; set; }
|
public partial BlogPostDto? SelectedPost { get; set; }
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial bool IsBusy { get; set; }
|
public partial bool IsBusy { get; set; }
|
||||||
|
|
@ -83,8 +83,8 @@ public partial class MainPageViewModel : ViewModelBase
|
||||||
private void Init(Settings? settings)
|
private void Init(Settings? settings)
|
||||||
{
|
{
|
||||||
SearchText = string.Empty;
|
SearchText = string.Empty;
|
||||||
Posts = new ObservableCollection<BlogPost>();
|
Posts = new ObservableCollection<BlogPostDto>();
|
||||||
FilteredPosts = new ObservableCollection<BlogPost>();
|
FilteredPosts = new ObservableCollection<BlogPostDto>();
|
||||||
SelectedPost = null;
|
SelectedPost = null;
|
||||||
IsBusy = false;
|
IsBusy = false;
|
||||||
StatusMessage = "Ready";
|
StatusMessage = "Ready";
|
||||||
|
|
@ -120,7 +120,7 @@ public partial class MainPageViewModel : ViewModelBase
|
||||||
|
|
||||||
partial void OnSearchTextChanged(string value) => ApplyFilter();
|
partial void OnSearchTextChanged(string value) => ApplyFilter();
|
||||||
|
|
||||||
partial void OnSelectedPostChanged(BlogPost? value)
|
partial void OnSelectedPostChanged(BlogPostDto? value)
|
||||||
{
|
{
|
||||||
// Mirror the selection into the editor buffer so the
|
// Mirror the selection into the editor buffer so the
|
||||||
// XAML-bound TextBox/TextEditor show the right content
|
// XAML-bound TextBox/TextEditor show the right content
|
||||||
|
|
@ -177,7 +177,7 @@ public partial class MainPageViewModel : ViewModelBase
|
||||||
|
|
||||||
await ExecuteAsync(async () =>
|
await ExecuteAsync(async () =>
|
||||||
{
|
{
|
||||||
// Build a fresh BlogPost from the editor buffer on
|
// Build a fresh BlogPostDto from the editor buffer on
|
||||||
// every Save — we no longer mutate SelectedPost in
|
// every Save — we no longer mutate SelectedPost in
|
||||||
// place. The previous behaviour copied the buffer
|
// place. The previous behaviour copied the buffer
|
||||||
// (which was a no-op when SelectedPost was null)
|
// (which was a no-op when SelectedPost was null)
|
||||||
|
|
@ -189,7 +189,7 @@ public partial class MainPageViewModel : ViewModelBase
|
||||||
// the update path.
|
// the update path.
|
||||||
if (SelectedPost is null || SelectedPost.Id == 0)
|
if (SelectedPost is null || SelectedPost.Id == 0)
|
||||||
{
|
{
|
||||||
var draft = new BlogPost
|
var draft = new BlogPostDto
|
||||||
{
|
{
|
||||||
Title = DraftTitle,
|
Title = DraftTitle,
|
||||||
Article = DraftArticle ?? string.Empty,
|
Article = DraftArticle ?? string.Empty,
|
||||||
|
|
@ -205,7 +205,7 @@ public partial class MainPageViewModel : ViewModelBase
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var update = new BlogPost
|
var update = new BlogPostDto
|
||||||
{
|
{
|
||||||
Id = SelectedPost.Id,
|
Id = SelectedPost.Id,
|
||||||
AuthorId = SelectedPost.AuthorId,
|
AuthorId = SelectedPost.AuthorId,
|
||||||
|
|
@ -327,7 +327,7 @@ public partial class MainPageViewModel : ViewModelBase
|
||||||
/// because the navigation surface (<c>NavigationPage</c>) lives
|
/// because the navigation surface (<c>NavigationPage</c>) lives
|
||||||
/// in the View layer.
|
/// in the View layer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<BlogPost>? ManageAclRequested;
|
public event EventHandler<BlogPostDto>? ManageAclRequested;
|
||||||
|
|
||||||
[RelayCommand(CanExecute = nameof(CanManageAcl))]
|
[RelayCommand(CanExecute = nameof(CanManageAcl))]
|
||||||
public void ManageAcl()
|
public void ManageAcl()
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ public partial class PostAclDialogViewModel : ViewModelBase
|
||||||
|
|
||||||
/// <summary>The post whose ACL is being edited. Set by the
|
/// <summary>The post whose ACL is being edited. Set by the
|
||||||
/// caller (MainPage) when opening the dialog.</summary>
|
/// caller (MainPage) when opening the dialog.</summary>
|
||||||
public BlogPost Post { get; }
|
public BlogPostDto Post { get; }
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial ObservableCollection<CircleDto> MyCircles { get; set; } = new();
|
public partial ObservableCollection<CircleDto> MyCircles { get; set; } = new();
|
||||||
|
|
@ -52,7 +52,7 @@ public partial class PostAclDialogViewModel : ViewModelBase
|
||||||
public partial string StatusMessage { get; set; } = string.Empty;
|
public partial string StatusMessage { get; set; } = string.Empty;
|
||||||
|
|
||||||
public PostAclDialogViewModel(
|
public PostAclDialogViewModel(
|
||||||
BlogPost post,
|
BlogPostDto post,
|
||||||
BlogAclApiClient aclClient,
|
BlogAclApiClient aclClient,
|
||||||
CircleApiClient circleClient)
|
CircleApiClient circleClient)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
<ListBox ItemsSource="{Binding FilteredPosts}" SelectedItem="{Binding SelectedPost, Mode=TwoWay}"
|
<ListBox ItemsSource="{Binding FilteredPosts}" SelectedItem="{Binding SelectedPost, Mode=TwoWay}"
|
||||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate x:DataType="models:BlogPost">
|
<DataTemplate x:DataType="models:BlogPostDto">
|
||||||
<StackPanel Spacing="4">
|
<StackPanel Spacing="4">
|
||||||
<TextBlock Text="{Binding Title}" FontWeight="SemiBold" />
|
<TextBlock Text="{Binding Title}" FontWeight="SemiBold" />
|
||||||
<TextBlock Text="{Binding DateModified, StringFormat='Updated: {0:yyyy-MM-dd HH:mm}'}" FontSize="10" Foreground="Gray" />
|
<TextBlock Text="{Binding DateModified, StringFormat='Updated: {0:yyyy-MM-dd HH:mm}'}" FontSize="10" Foreground="Gray" />
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ public partial class MainPage : ContentPage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnManageAclRequested(object? sender, BlogPost post)
|
void OnManageAclRequested(object? sender, BlogPostDto post)
|
||||||
{
|
{
|
||||||
var app = Application.Current as App;
|
var app = Application.Current as App;
|
||||||
var services = app?.ServiceProvider;
|
var services = app?.ServiceProvider;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ public partial class PostAclDialog : ContentPage
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public PostAclDialog(BlogPost post, BlogAclApiClient aclClient, CircleApiClient circleClient)
|
public PostAclDialog(BlogPostDto post, BlogAclApiClient aclClient, CircleApiClient circleClient)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = new PostAclDialogViewModel(post, aclClient, circleClient);
|
DataContext = new PostAclDialogViewModel(post, aclClient, circleClient);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ using Yavsc.Abstract.Identity.Security;
|
||||||
|
|
||||||
namespace Yavsc.Blogspot;
|
namespace Yavsc.Blogspot;
|
||||||
|
|
||||||
public class BlogPost : IBlogPost
|
public class BlogPostDto : IBlogPost
|
||||||
{
|
{
|
||||||
public string AuthorId { get; set; }
|
public string AuthorId { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ namespace Yavsc.Api.Client;
|
||||||
/// HTTP client for <c>/api/blogacl</c> on the Yavsc Blogs server.
|
/// HTTP client for <c>/api/blogacl</c> on the Yavsc Blogs server.
|
||||||
///
|
///
|
||||||
/// <para>Each <see cref="CircleAuthorizationDto"/> grants a single
|
/// <para>Each <see cref="CircleAuthorizationDto"/> grants a single
|
||||||
/// <c>Circle</c> access to a single <c>BlogPost</c>. The server
|
/// <c>Circle</c> access to a single <c>BlogPostDto</c>. The server
|
||||||
/// scopes every endpoint to the caller's uid: only the author of
|
/// scopes every endpoint to the caller's uid: only the author of
|
||||||
/// the underlying blog post can list, create, modify, or delete
|
/// the underlying blog post can list, create, modify, or delete
|
||||||
/// its ACL entries.</para>
|
/// its ACL entries.</para>
|
||||||
|
|
|
||||||
|
|
@ -53,19 +53,19 @@ public sealed class BlogApiClient
|
||||||
_pathPrefix = pathPrefix?.TrimStart('/') ?? DefaultPathPrefix;
|
_pathPrefix = pathPrefix?.TrimStart('/') ?? DefaultPathPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<List<BlogPost>> GetPostsAsync(int start = 0, int take = 25, CancellationToken ct = default)
|
public Task<List<BlogPostDto>> GetPostsAsync(int start = 0, int take = 25, CancellationToken ct = default)
|
||||||
=> _api.CallAsync<List<BlogPost>>(
|
=> _api.CallAsync<List<BlogPostDto>>(
|
||||||
HttpMethod.Get,
|
HttpMethod.Get,
|
||||||
$"{_pathPrefix}?start={start}&take={take}",
|
$"{_pathPrefix}?start={start}&take={take}",
|
||||||
ct: ct);
|
ct: ct);
|
||||||
|
|
||||||
public Task<BlogPost?> GetPostAsync(long id, CancellationToken ct = default)
|
public Task<BlogPostDto?> GetPostAsync(long id, CancellationToken ct = default)
|
||||||
=> _api.CallAsync<BlogPost?>(HttpMethod.Get, $"{_pathPrefix}/{id}", ct: ct);
|
=> _api.CallAsync<BlogPostDto?>(HttpMethod.Get, $"{_pathPrefix}/{id}", ct: ct);
|
||||||
|
|
||||||
public Task<BlogPost?> CreatePostAsync(BlogPost post, CancellationToken ct = default)
|
public Task<BlogPostDto?> CreatePostAsync(BlogPostDto post, CancellationToken ct = default)
|
||||||
=> _api.CallAsync<BlogPost?>(HttpMethod.Post, _pathPrefix, body: post, ct: ct);
|
=> _api.CallAsync<BlogPostDto?>(HttpMethod.Post, _pathPrefix, body: post, ct: ct);
|
||||||
|
|
||||||
public Task UpdatePostAsync(long id, BlogPost post, CancellationToken ct = default)
|
public Task UpdatePostAsync(long id, BlogPostDto post, CancellationToken ct = default)
|
||||||
=> _api.CallAsync(HttpMethod.Put, $"{_pathPrefix}/{id}", body: post, ct: ct);
|
=> _api.CallAsync(HttpMethod.Put, $"{_pathPrefix}/{id}", body: post, ct: ct);
|
||||||
|
|
||||||
public Task DeletePostAsync(long id, CancellationToken ct = default)
|
public Task DeletePostAsync(long id, CancellationToken ct = default)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue