mirror of
https://github.com/trailbaseio/trailbase.git
synced 2026-04-24 09:29:02 -05:00
Add documentation to dotnet client.
This commit is contained in:
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 7.1 KiB |
@@ -1,2 +1,6 @@
|
||||
bin/
|
||||
obj/
|
||||
|
||||
# docfx documentation.
|
||||
_site/
|
||||
api/
|
||||
|
||||
@@ -7,67 +7,124 @@ using System.Text.Json;
|
||||
|
||||
namespace TrailBase;
|
||||
|
||||
/// <summary>
|
||||
/// Representation of User JSON objects.
|
||||
/// </summary>
|
||||
public class User {
|
||||
/// <summary>Auth subject, i.e. user id.</summary>
|
||||
public string sub { get; }
|
||||
/// <summary>The user's email address.</summary>
|
||||
public string email { get; }
|
||||
|
||||
/// <summary>
|
||||
/// User constructor.
|
||||
/// </summary>
|
||||
/// <param name="sub">Auth subject, i.e. user id.</param>
|
||||
/// <param name="email">User's email address</param>
|
||||
public User(string sub, string email) {
|
||||
this.sub = sub;
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Representation of Credentials JSON objects used for log in.
|
||||
/// </summary>
|
||||
public class Credentials {
|
||||
/// <summary>The user's email address.</summary>
|
||||
public string email { get; }
|
||||
/// <summary>The user's password.</summary>
|
||||
public string password { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Credentials constructor.
|
||||
/// </summary>
|
||||
/// <param name="email">User's email address</param>
|
||||
/// <param name="password">User's password</param>
|
||||
public Credentials(string email, string password) {
|
||||
this.email = email;
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
|
||||
public class RefreshToken {
|
||||
/// <summary>
|
||||
/// Representation of RefreshTokenRequest JSON objects.
|
||||
/// </summary>
|
||||
public class RefreshTokenRequest {
|
||||
/// <summary>The refresh token received at login.</summary>
|
||||
public string refresh_token { get; }
|
||||
|
||||
public RefreshToken(string refreshToken) {
|
||||
/// <summary>
|
||||
/// RefreshTokenRequest constructor.
|
||||
/// </summary>
|
||||
/// <param name="refreshToken">The refresh token.</param>
|
||||
public RefreshTokenRequest(string refreshToken) {
|
||||
refresh_token = refreshToken;
|
||||
}
|
||||
}
|
||||
|
||||
public class TokenResponse {
|
||||
/// <summary>
|
||||
/// Representation of RefreshTokenResponse JSON objects provided on refresh.
|
||||
/// </summary>
|
||||
public class RefreshTokenResponse {
|
||||
/// <summary>New auth token in exchange for the refresh token.</summary>
|
||||
public string auth_token { get; }
|
||||
/// <summary>Cross-site request forgery token.</summary>
|
||||
public string? csrf_token { get; }
|
||||
|
||||
public TokenResponse(string authToken, string? csrfToken) {
|
||||
/// <summary>
|
||||
/// RefreshTokenResponse constructor.
|
||||
/// </summary>
|
||||
/// <param name="authToken">User authentication token.</param>
|
||||
/// <param name="csrfToken">User Cross-site request forgery token.</param>
|
||||
public RefreshTokenResponse(string authToken, string? csrfToken) {
|
||||
auth_token = authToken;
|
||||
csrf_token = csrfToken;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Representation of Token JSON objects provided on login.
|
||||
/// </summary>
|
||||
public class Tokens {
|
||||
/// <summary>User auth token.</summary>
|
||||
public string auth_token { get; }
|
||||
/// <summary>User refresh token for future auth token exchanges.</summary>
|
||||
public string? refresh_token { get; }
|
||||
/// <summary>Cross-site request forgery token.</summary>
|
||||
public string? csrf_token { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Tokens constructor.
|
||||
/// </summary>
|
||||
public Tokens(string auth_token, string? refresh_token, string? csrf_token) {
|
||||
this.auth_token = auth_token;
|
||||
this.refresh_token = refresh_token;
|
||||
this.csrf_token = csrf_token;
|
||||
}
|
||||
|
||||
/// <summary>Serialize Tokens.</summary>
|
||||
public override string ToString() {
|
||||
return $"Tokens({auth_token}, {refresh_token}, {csrf_token})";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Representation of JwtToken JSON objects.
|
||||
/// </summary>
|
||||
public class JwtToken {
|
||||
/// <summary>Auth subject, i.e. user id.</summary>
|
||||
public string sub { get; }
|
||||
/// <summary>JWT token issue timestamp.</summary>
|
||||
public long iat { get; }
|
||||
/// <summary>Expiration timestamp.</summary>
|
||||
public long exp { get; }
|
||||
/// <summary>User's email address.</summary>
|
||||
public string email { get; }
|
||||
/// <summary>Cross-site request forgery token.</summary>
|
||||
public string csrf_token { get; }
|
||||
|
||||
/// <summary>JwtToken constructor.</summary>
|
||||
[JsonConstructor]
|
||||
public JwtToken(
|
||||
string sub,
|
||||
@@ -88,8 +145,8 @@ public class JwtToken {
|
||||
[JsonSerializable(typeof(Credentials))]
|
||||
[JsonSerializable(typeof(JwtToken))]
|
||||
[JsonSerializable(typeof(Tokens))]
|
||||
[JsonSerializable(typeof(TokenResponse))]
|
||||
[JsonSerializable(typeof(RefreshToken))]
|
||||
[JsonSerializable(typeof(RefreshTokenResponse))]
|
||||
[JsonSerializable(typeof(RefreshTokenRequest))]
|
||||
[JsonSerializable(typeof(User))]
|
||||
internal partial class SourceGenerationContext : JsonSerializerContext {
|
||||
}
|
||||
@@ -111,7 +168,10 @@ class TokenState {
|
||||
var json = jwtToken.Payload.SerializeToJson();
|
||||
|
||||
return new TokenState(
|
||||
(tokens, JsonSerializer.Deserialize<JwtToken>(json, SourceGenerationContext.Default.JwtToken))!,
|
||||
(
|
||||
tokens,
|
||||
JsonSerializer.Deserialize<JwtToken>(json, SourceGenerationContext.Default.JwtToken)
|
||||
)!,
|
||||
buildHeaders(tokens)
|
||||
);
|
||||
}
|
||||
@@ -139,7 +199,7 @@ class TokenState {
|
||||
}
|
||||
}
|
||||
|
||||
class ThinClient {
|
||||
internal class ThinClient {
|
||||
static readonly HttpClient client = new HttpClient();
|
||||
|
||||
string site;
|
||||
@@ -188,21 +248,33 @@ class ThinClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The main API for interacting with TrailBase servers.
|
||||
/// </summary>
|
||||
public class Client {
|
||||
static readonly string _authApi = "api/auth/v1";
|
||||
static readonly ILogger logger = LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger("TrailBase.Client");
|
||||
static readonly ILogger logger = LoggerFactory.Create(
|
||||
builder => builder.AddConsole()).CreateLogger("TrailBase.Client");
|
||||
|
||||
ThinClient client;
|
||||
/// <summary>Site this Client is connected to.</summary>
|
||||
public string site { get; }
|
||||
TokenState tokenState;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a TrailBase Client for the given [site] and [tokens].
|
||||
/// </summary>
|
||||
/// <param name="site">Site URL, e.g. https://trailbase.io:4000.</param>
|
||||
/// <param name="tokens">Optional tokens for a given authenticated user</param>
|
||||
public Client(String site, Tokens? tokens) {
|
||||
client = new ThinClient(site);
|
||||
this.site = site;
|
||||
tokenState = TokenState.build(tokens);
|
||||
}
|
||||
|
||||
/// <summary>Get tokens of the logged-in user if any.</summary>
|
||||
public Tokens? Tokens() => tokenState.state?.Item1;
|
||||
/// <summary>Get the logged-in user if any.</summary>
|
||||
public User? User() {
|
||||
var authToken = Tokens()?.auth_token;
|
||||
if (authToken != null) {
|
||||
@@ -215,10 +287,12 @@ public class Client {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Construct a record API object for the API with the given name.</summary>
|
||||
public RecordApi Records(string name) {
|
||||
return new RecordApi(this, name);
|
||||
}
|
||||
|
||||
/// <summary>Log in with the given credentials.</summary>
|
||||
public async Task<Tokens> Login(string email, string password) {
|
||||
var response = await Fetch(
|
||||
$"{_authApi}/login",
|
||||
@@ -233,12 +307,16 @@ public class Client {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/// <summary>Log out the current user.</summary>
|
||||
public async Task<bool> Logout() {
|
||||
var refreshToken = tokenState.state?.Item1.refresh_token;
|
||||
|
||||
try {
|
||||
if (refreshToken != null) {
|
||||
var tokenJson = JsonContent.Create(new RefreshToken(refreshToken), SourceGenerationContext.Default.RefreshToken);
|
||||
var tokenJson = JsonContent.Create(
|
||||
new RefreshTokenRequest(refreshToken),
|
||||
SourceGenerationContext.Default.RefreshTokenRequest
|
||||
);
|
||||
await Fetch($"{_authApi}/logout", HttpMethod.Post, tokenJson, null);
|
||||
}
|
||||
else {
|
||||
@@ -280,6 +358,7 @@ public class Client {
|
||||
return ts;
|
||||
}
|
||||
|
||||
/// <summary>Refresh the current auth token.</summary>
|
||||
public async Task RefreshAuthToken() {
|
||||
var refreshToken = shouldRefresh(tokenState);
|
||||
if (refreshToken != null) {
|
||||
@@ -291,13 +370,19 @@ public class Client {
|
||||
var response = await client.Fetch(
|
||||
$"{_authApi}/refresh",
|
||||
tokenState,
|
||||
JsonContent.Create(new RefreshToken(refreshToken), SourceGenerationContext.Default.RefreshToken),
|
||||
JsonContent.Create(
|
||||
new RefreshTokenRequest(refreshToken),
|
||||
SourceGenerationContext.Default.RefreshTokenRequest
|
||||
),
|
||||
HttpMethod.Post,
|
||||
null
|
||||
);
|
||||
|
||||
string json = await response.Content.ReadAsStringAsync();
|
||||
TokenResponse tokenResponse = JsonSerializer.Deserialize<TokenResponse>(json, SourceGenerationContext.Default.TokenResponse)!;
|
||||
RefreshTokenResponse tokenResponse = JsonSerializer.Deserialize<RefreshTokenResponse>(
|
||||
json,
|
||||
SourceGenerationContext.Default.RefreshTokenResponse
|
||||
)!;
|
||||
|
||||
return TokenState.build(new Tokens(
|
||||
tokenResponse.auth_token,
|
||||
@@ -306,7 +391,7 @@ public class Client {
|
||||
));
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> Fetch(
|
||||
internal async Task<HttpResponseMessage> Fetch(
|
||||
string path,
|
||||
HttpMethod? method,
|
||||
HttpContent? data,
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace TrailBase;
|
||||
|
||||
public static class Constants {
|
||||
static class Constants {
|
||||
public static int Port = 4010 + System.Environment.Version.Major;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,57 +7,78 @@ using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace TrailBase;
|
||||
|
||||
public class RecordId { }
|
||||
/// <summary>Base for RecordId representations.</summary>
|
||||
public abstract class RecordId {
|
||||
/// <summary>Serialize RecordId.</summary>
|
||||
public abstract override string ToString();
|
||||
}
|
||||
|
||||
/// <summary>Un-typed record id.</summary>
|
||||
public class ResponseRecordId : RecordId {
|
||||
/// <summary>Serialized id, could be integer or UUID.</summary>
|
||||
public string id { get; }
|
||||
|
||||
/// <summary>ResponseRecordId constructor.</summary>
|
||||
public ResponseRecordId(string id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/// <summary>Serialize RecordId.</summary>
|
||||
public override string ToString() => id;
|
||||
}
|
||||
|
||||
/// <summary>Integer record id.</summary>
|
||||
public class IntegerRecordId : RecordId {
|
||||
public long id { get; }
|
||||
long id { get; }
|
||||
|
||||
/// <summary>Integer record id constructor.</summary>
|
||||
public IntegerRecordId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/// <summary>Serialize RecordId.</summary>
|
||||
public override string ToString() => id.ToString();
|
||||
}
|
||||
|
||||
/// <summary>UUID record id.</summary>
|
||||
public class UuidRecordId : RecordId {
|
||||
public Guid id { get; }
|
||||
Guid id { get; }
|
||||
|
||||
/// <summary>UUID record id constructor.</summary>
|
||||
public UuidRecordId(Guid id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/// <summary>UUID record id constructor.</summary>
|
||||
public UuidRecordId(string id) {
|
||||
var bytes = System.Convert.FromBase64String(id.Replace('-', '+').Replace('_', '/'));
|
||||
this.id = new Guid(bytes);
|
||||
}
|
||||
|
||||
/// <summary>Serialize UuidRecordId.</summary>
|
||||
public override string ToString() {
|
||||
var bytes = id.ToByteArray();
|
||||
return System.Convert.ToBase64String(bytes).Replace('+', '-').Replace('/', '_');
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Pagination state representation.</summary>
|
||||
public class Pagination {
|
||||
/// <summary>Offset cursor.</summary>
|
||||
public string? cursor { get; }
|
||||
/// <summary>Limit of elements per page.</summary>
|
||||
public int? limit { get; }
|
||||
|
||||
/// <summary>Pagination constructor.</summary>
|
||||
public Pagination(string? cursor, int? limit) {
|
||||
this.cursor = cursor;
|
||||
this.limit = limit;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Realtime event for change subscriptions.</summary>
|
||||
public abstract class Event {
|
||||
/// <summary>Get associated record value as JSON object.</summary>
|
||||
public abstract JsonNode? Value { get; }
|
||||
|
||||
internal static Event Parse(string message) {
|
||||
@@ -88,44 +109,61 @@ public abstract class Event {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Record insertion event.</summary>
|
||||
public class InsertEvent : Event {
|
||||
/// <summary>Get associated record value as JSON object.</summary>
|
||||
public override JsonNode? Value { get; }
|
||||
|
||||
/// <summary>InsertEvent constructor.</summary>
|
||||
public InsertEvent(JsonNode? value) {
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>Serialize InsertEvent.</summary>
|
||||
public override string ToString() => $"InsertEvent({Value})";
|
||||
}
|
||||
|
||||
/// <summary>Record update event.</summary>
|
||||
public class UpdateEvent : Event {
|
||||
/// <summary>Get associated record value as JSON object.</summary>
|
||||
public override JsonNode? Value { get; }
|
||||
|
||||
/// <summary>UpdateEvent constructor.</summary>
|
||||
public UpdateEvent(JsonNode? value) {
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>Serialize UpdateEvent.</summary>
|
||||
public override string ToString() => $"UpdateEvent({Value})";
|
||||
}
|
||||
|
||||
/// <summary>Record deletion event.</summary>
|
||||
public class DeleteEvent : Event {
|
||||
/// <summary>Get associated record value as JSON object.</summary>
|
||||
public override JsonNode? Value { get; }
|
||||
|
||||
/// <summary>DeleteEvent constructor.</summary>
|
||||
public DeleteEvent(JsonNode? value) {
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>Serialize DeleteEvent.</summary>
|
||||
public override string ToString() => $"DeleteEvent({Value})";
|
||||
}
|
||||
|
||||
/// <summary>Error event.</summary>
|
||||
public class ErrorEvent : Event {
|
||||
/// <summary>Get associated record value as JSON object.</summary>
|
||||
public override JsonNode? Value { get { return null; } }
|
||||
/// <summary>Get associated error message.</summary>
|
||||
public string ErrorMessage { get; }
|
||||
|
||||
/// <summary>ErrorEvent constructor.</summary>
|
||||
public ErrorEvent(string errorMsg) {
|
||||
this.ErrorMessage = errorMsg;
|
||||
}
|
||||
|
||||
/// <summary>Serialize ErrorEvent.</summary>
|
||||
public override string ToString() => $"ErrorEvent({ErrorMessage})";
|
||||
}
|
||||
|
||||
@@ -134,6 +172,7 @@ public class ErrorEvent : Event {
|
||||
internal partial class SerializeResponseRecordIdContext : JsonSerializerContext {
|
||||
}
|
||||
|
||||
/// <summary>Main API to interact with Records.</summary>
|
||||
public class RecordApi {
|
||||
static readonly string _recordApi = "api/records/v1";
|
||||
const string DynamicCodeMessage = "Use overload with JsonTypeInfo instead";
|
||||
@@ -142,30 +181,38 @@ public class RecordApi {
|
||||
Client client { get; }
|
||||
string name { get; }
|
||||
|
||||
public RecordApi(Client client, string name) {
|
||||
internal RecordApi(Client client, string name) {
|
||||
this.client = client;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/// <summary>Read the record with given id.</summary>
|
||||
[RequiresDynamicCode(DynamicCodeMessage)]
|
||||
[RequiresUnreferencedCode(UnreferencedCodeMessage)]
|
||||
public async Task<T?> Read<T>(RecordId id) {
|
||||
string json = await (await ReadImpl(id)).ReadAsStringAsync();
|
||||
return JsonSerializer.Deserialize<T>(json);
|
||||
}
|
||||
/// <summary>Read the record with given id.</summary>
|
||||
[RequiresDynamicCode(DynamicCodeMessage)]
|
||||
[RequiresUnreferencedCode(UnreferencedCodeMessage)]
|
||||
public async Task<T?> Read<T>(string id) => await Read<T>(new UuidRecordId(id));
|
||||
/// <summary>Read the record with given id.</summary>
|
||||
[RequiresDynamicCode(DynamicCodeMessage)]
|
||||
[RequiresUnreferencedCode(UnreferencedCodeMessage)]
|
||||
public async Task<T?> Read<T>(long id) => await Read<T>(new IntegerRecordId(id));
|
||||
|
||||
/// <summary>Read the record with given id.</summary>
|
||||
public async Task<T?> Read<T>(RecordId id, JsonTypeInfo<T> jsonTypeInfo) {
|
||||
string json = await (await ReadImpl(id)).ReadAsStringAsync();
|
||||
return JsonSerializer.Deserialize<T>(json, jsonTypeInfo);
|
||||
}
|
||||
public async Task<T?> Read<T>(string id, JsonTypeInfo<T> jsonTypeInfo) => await Read<T>(new UuidRecordId(id), jsonTypeInfo);
|
||||
public async Task<T?> Read<T>(long id, JsonTypeInfo<T> jsonTypeInfo) => await Read<T>(new IntegerRecordId(id), jsonTypeInfo);
|
||||
/// <summary>Read the record with given id.</summary>
|
||||
public async Task<T?> Read<T>(string id, JsonTypeInfo<T> jsonTypeInfo)
|
||||
=> await Read<T>(new UuidRecordId(id), jsonTypeInfo);
|
||||
/// <summary>Read the record with given id.</summary>
|
||||
public async Task<T?> Read<T>(long id, JsonTypeInfo<T> jsonTypeInfo)
|
||||
=> await Read<T>(new IntegerRecordId(id), jsonTypeInfo);
|
||||
|
||||
private async Task<HttpContent> ReadImpl(RecordId id) {
|
||||
var response = await client.Fetch(
|
||||
@@ -177,6 +224,7 @@ public class RecordApi {
|
||||
return response.Content;
|
||||
}
|
||||
|
||||
/// <summary>Create a new record with the given value.</summary>
|
||||
[RequiresDynamicCode(DynamicCodeMessage)]
|
||||
[RequiresUnreferencedCode(UnreferencedCodeMessage)]
|
||||
public async Task<RecordId> Create<T>(T record) {
|
||||
@@ -187,6 +235,7 @@ public class RecordApi {
|
||||
return await CreateImpl(recordJson);
|
||||
}
|
||||
|
||||
/// <summary>Create a new record with the given value.</summary>
|
||||
public async Task<RecordId> Create<T>(T record, JsonTypeInfo<T> jsonTypeInfo) {
|
||||
var recordJson = JsonContent.Create(record, jsonTypeInfo, default);
|
||||
return await CreateImpl(recordJson);
|
||||
@@ -201,9 +250,18 @@ public class RecordApi {
|
||||
);
|
||||
|
||||
string json = await response.Content.ReadAsStringAsync();
|
||||
return JsonSerializer.Deserialize<ResponseRecordId>(json, SerializeResponseRecordIdContext.Default.ResponseRecordId)!;
|
||||
return JsonSerializer.Deserialize<ResponseRecordId>(
|
||||
json,
|
||||
SerializeResponseRecordIdContext.Default.ResponseRecordId
|
||||
)!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List records.
|
||||
/// </summary>
|
||||
/// <param name="pagination">Pagination state.</param>
|
||||
/// <param name="order">Sort results by the given columns in ascending/descending order, e.g. "-col_name".</param>
|
||||
/// <param name="filters">Results filters, e.g. "col0[gte]=100".</param>
|
||||
[RequiresDynamicCode(DynamicCodeMessage)]
|
||||
[RequiresUnreferencedCode(UnreferencedCodeMessage)]
|
||||
public async Task<List<T>> List<T>(
|
||||
@@ -215,10 +273,18 @@ public class RecordApi {
|
||||
return JsonSerializer.Deserialize<List<T>>(json) ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List records.
|
||||
/// </summary>
|
||||
/// <param name="pagination">Pagination state.</param>
|
||||
/// <param name="order">Sort results by the given columns in ascending/descending order, e.g. "-col_name".</param>
|
||||
/// <param name="filters">Results filters, e.g. "col0[gte]=100".</param>
|
||||
/// <param name="jsonTypeInfo">Serialization type info for AOT mode.</param>
|
||||
public async Task<List<T>> List<T>(
|
||||
Pagination? pagination,
|
||||
List<string>? order,
|
||||
List<string>? filters, JsonTypeInfo<List<T>> jsonTypeInfo
|
||||
List<string>? filters,
|
||||
JsonTypeInfo<List<T>> jsonTypeInfo
|
||||
) {
|
||||
string json = await (await ListImpl(pagination, order, filters)).ReadAsStringAsync();
|
||||
return JsonSerializer.Deserialize<List<T>>(json, jsonTypeInfo) ?? [];
|
||||
@@ -268,6 +334,7 @@ public class RecordApi {
|
||||
return response.Content;
|
||||
}
|
||||
|
||||
/// <summary>Update record with the given id with the given values.</summary>
|
||||
[RequiresDynamicCode(DynamicCodeMessage)]
|
||||
[RequiresUnreferencedCode(UnreferencedCodeMessage)]
|
||||
public async Task Update<T>(
|
||||
@@ -281,6 +348,7 @@ public class RecordApi {
|
||||
await UpdateImpl(id, recordJson);
|
||||
}
|
||||
|
||||
/// <summary>Update record with the given id with the given values.</summary>
|
||||
public async Task Update<T>(
|
||||
RecordId id,
|
||||
T record,
|
||||
@@ -302,6 +370,7 @@ public class RecordApi {
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Delete record with the given id.</summary>
|
||||
public async Task Delete(RecordId id) {
|
||||
var response = await client.Fetch(
|
||||
$"{RecordApi._recordApi}/{name}/{id}",
|
||||
@@ -311,11 +380,13 @@ public class RecordApi {
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Listen for changes to record with given id.</summary>
|
||||
public async Task<IAsyncEnumerable<Event>> Subscribe(RecordId id) {
|
||||
var response = await SubscribeImpl(id.ToString()!);
|
||||
return StreamToEnumerableImpl(await response.ReadAsStreamAsync());
|
||||
}
|
||||
|
||||
/// <summary>Listen for all accessible changes to this Record API.</summary>
|
||||
public async Task<IAsyncEnumerable<Event>> SubscribeAll() {
|
||||
var response = await SubscribeImpl("*");
|
||||
return StreamToEnumerableImpl(await response.ReadAsStreamAsync());
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
<!-- <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault> -->
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json",
|
||||
"metadata": [
|
||||
{
|
||||
"src": [
|
||||
{
|
||||
"src": ".",
|
||||
"files": [
|
||||
"**/*.csproj"
|
||||
],
|
||||
"exclude": [
|
||||
"test/**",
|
||||
"_site/**",
|
||||
"**/bin/**",
|
||||
"**/obj/**"
|
||||
]
|
||||
}
|
||||
],
|
||||
"dest": "api"
|
||||
}
|
||||
],
|
||||
"build": {
|
||||
"content": [
|
||||
{
|
||||
"files": [
|
||||
"**/*.{md,yml}"
|
||||
],
|
||||
"exclude": [
|
||||
"_site/**"
|
||||
]
|
||||
}
|
||||
],
|
||||
"resource": [
|
||||
{
|
||||
"files": [
|
||||
"images/**"
|
||||
]
|
||||
}
|
||||
],
|
||||
"output": "_site",
|
||||
"template": [
|
||||
"default",
|
||||
"modern"
|
||||
],
|
||||
"globalMetadata": {
|
||||
"_appName": "TrailBase",
|
||||
"_appTitle": "TrailBase",
|
||||
"_appLogoPath": "images/logo_48.svg",
|
||||
"_enableSearch": true,
|
||||
"pdf": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../assets/logo_48.svg
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
_layout: landing
|
||||
---
|
||||
|
||||
# TrailBase Client
|
||||
|
||||
Auto-generate reference documentation.
|
||||
@@ -0,0 +1,2 @@
|
||||
- name: API
|
||||
href: api/
|
||||
Reference in New Issue
Block a user