Skip to content

Commit

Permalink
Merge pull request #639 from PinguApps/feature/realtime
Browse files Browse the repository at this point in the history
Added realtime SDK
  • Loading branch information
pingu2k4 authored Jan 23, 2025
2 parents 97069c5 + ae5ddd2 commit 2f4e393
Show file tree
Hide file tree
Showing 21 changed files with 604 additions and 38 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
env:
CLIENT_PROJECT_NAME: PinguApps.Appwrite.Client
SERVER_PROJECT_NAME: PinguApps.Appwrite.Server
REALTIME_PROJECT_NAME: PinguApps.Appwrite.Realtime
DOTNET_VERSION: '8.0'
NUGET_FEED: https://api.nuget.org/v3/index.json
NUGET_KEY: ${{ secrets.NUGET_KEY }}
Expand Down Expand Up @@ -62,6 +63,7 @@ jobs:
run: |
dotnet pack -c Release --verbosity normal --include-symbols --include-source -p:PackageVersion=$VERSION -o nupkg src/$CLIENT_PROJECT_NAME/$CLIENT_PROJECT_NAME.csproj
dotnet pack -c Release --verbosity normal --include-symbols --include-source -p:PackageVersion=$VERSION -o nupkg src/$SERVER_PROJECT_NAME/$SERVER_PROJECT_NAME.csproj
dotnet pack -c Release --verbosity normal --include-symbols --include-source -p:PackageVersion=$VERSION -o nupkg src/$REALTIME_PROJECT_NAME/$REALTIME_PROJECT_NAME.csproj
- name: Push to NuGet Feed
run: dotnet nuget push ./nupkg/*.{nupkg,snupkg} --source $NUGET_FEED --api-key $NUGET_KEY --skip-duplicate
Expand Down
7 changes: 7 additions & 0 deletions PinguApps.AppwriteSdk.sln
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PinguApps.Appwrite.Client.T
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PinguApps.Appwrite.Shared.Tests", "tests\PinguApps.Appwrite.Shared.Tests\PinguApps.Appwrite.Shared.Tests.csproj", "{5AED345C-1E3E-4397-807C-8EFC22B786FA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PinguApps.Appwrite.Realtime", "src\PinguApps.Appwrite.Realtime\PinguApps.Appwrite.Realtime.csproj", "{21B5C2DF-E38A-40C3-BFCA-8E69F3C0AA77}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -54,6 +56,10 @@ Global
{5AED345C-1E3E-4397-807C-8EFC22B786FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5AED345C-1E3E-4397-807C-8EFC22B786FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5AED345C-1E3E-4397-807C-8EFC22B786FA}.Release|Any CPU.Build.0 = Release|Any CPU
{21B5C2DF-E38A-40C3-BFCA-8E69F3C0AA77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{21B5C2DF-E38A-40C3-BFCA-8E69F3C0AA77}.Debug|Any CPU.Build.0 = Debug|Any CPU
{21B5C2DF-E38A-40C3-BFCA-8E69F3C0AA77}.Release|Any CPU.ActiveCfg = Release|Any CPU
{21B5C2DF-E38A-40C3-BFCA-8E69F3C0AA77}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -66,6 +72,7 @@ Global
{11D16276-FCD0-404D-BA8A-5CBEAF494CB0} = {187EBB4D-6799-492C-AAA7-518B2811D800}
{71C1431E-8098-4B9B-9CFA-A49F77242C79} = {187EBB4D-6799-492C-AAA7-518B2811D800}
{5AED345C-1E3E-4397-807C-8EFC22B786FA} = {187EBB4D-6799-492C-AAA7-518B2811D800}
{21B5C2DF-E38A-40C3-BFCA-8E69F3C0AA77} = {6F37E87C-BABE-4727-ADF5-157E65C583ED}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {82576E5A-09AA-47E0-98BC-E5CA47EF4EC0}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Appwrite SDK
This repository contains the source to both the Client and Server .net implimentation for Appwrite API. This is not a first party SDK, rather a third party SDK.

[![Client Version](https://img.shields.io/nuget/v/PinguApps.Appwrite.Client?logo=nuget&style=for-the-badge&label=Client%20Version)](https://www.nuget.org/packages/PinguApps.Appwrite.Client) [![Client Downloads](https://img.shields.io/nuget/dt/PinguApps.Appwrite.Client?style=for-the-badge&logo=nuget&label=Client%20Downloads)](https://www.nuget.org/packages/PinguApps.Appwrite.Client) [![Server Version](https://img.shields.io/nuget/v/PinguApps.Appwrite.Server?logo=nuget&style=for-the-badge&label=Server%20Version)](https://www.nuget.org/packages/PinguApps.Appwrite.Server) [![Server Downloads](https://img.shields.io/nuget/dt/PinguApps.Appwrite.Server?style=for-the-badge&logo=nuget&label=Server%20Downloads)](https://www.nuget.org/packages/PinguApps.Appwrite.Server) [![GitHub Repo stars](https://img.shields.io/github/stars/PinguApps/AppwriteSdk?style=for-the-badge&logo=github)](https://github.com/PinguApps/AppwriteSdk/stargazers) [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/PinguApps/AppwriteSdk/main.yml?style=for-the-badge&logo=github)](https://github.com/PinguApps/AppwriteSdk/actions/workflows/main.yml) [![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/PinguApps/AppwriteSdk?style=for-the-badge&logo=codefactor)](https://www.codefactor.io/repository/github/pinguapps/AppwriteSdk)
[![Client Version](https://img.shields.io/nuget/v/PinguApps.Appwrite.Client?logo=nuget&style=for-the-badge&label=Client%20Version)](https://www.nuget.org/packages/PinguApps.Appwrite.Client) [![Client Downloads](https://img.shields.io/nuget/dt/PinguApps.Appwrite.Client?style=for-the-badge&logo=nuget&label=Client%20Downloads)](https://www.nuget.org/packages/PinguApps.Appwrite.Client) [![Server Version](https://img.shields.io/nuget/v/PinguApps.Appwrite.Server?logo=nuget&style=for-the-badge&label=Server%20Version)](https://www.nuget.org/packages/PinguApps.Appwrite.Server) [![Server Downloads](https://img.shields.io/nuget/dt/PinguApps.Appwrite.Server?style=for-the-badge&logo=nuget&label=Server%20Downloads)](https://www.nuget.org/packages/PinguApps.Appwrite.Server) [![Realtime Version](https://img.shields.io/nuget/v/PinguApps.Appwrite.Realtime?logo=nuget&style=for-the-badge&label=Realtime%20Version)](https://www.nuget.org/packages/PinguApps.Appwrite.Realtime) [![Realtime Downloads](https://img.shields.io/nuget/dt/PinguApps.Appwrite.Realtime?style=for-the-badge&logo=nuget&label=Realtime%20Downloads)](https://www.nuget.org/packages/PinguApps.Appwrite.Realtime) [![GitHub Repo stars](https://img.shields.io/github/stars/PinguApps/AppwriteSdk?style=for-the-badge&logo=github)](https://github.com/PinguApps/AppwriteSdk/stargazers) [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/PinguApps/AppwriteSdk/main.yml?style=for-the-badge&logo=github)](https://github.com/PinguApps/AppwriteSdk/actions/workflows/main.yml) [![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/PinguApps/AppwriteSdk?style=for-the-badge&logo=codefactor)](https://www.codefactor.io/repository/github/pinguapps/AppwriteSdk)

![Repobeats Analytics](https://repobeats.axiom.co/api/embed/1ec2e55e97fb4516682f5e66feba33976870ab32.svg)

Expand Down
53 changes: 17 additions & 36 deletions src/PinguApps.Appwrite.Playground/App.cs
Original file line number Diff line number Diff line change
@@ -1,59 +1,40 @@
using System.Text.Json.Serialization;
using Microsoft.Extensions.Configuration;
using PinguApps.Appwrite.Shared.Requests.Databases;
using PinguApps.Appwrite.Realtime;
using PinguApps.Appwrite.Shared.Responses;

namespace PinguApps.Appwrite.Playground;
internal class App
{
private readonly Client.IClientAppwriteClient _client;
private readonly Server.Clients.IServerAppwriteClient _server;
private readonly IRealtimeClient _realtimeClient;
private readonly string? _session;

public App(Client.IClientAppwriteClient client, Server.Clients.IServerAppwriteClient server, IConfiguration config)
public App(Client.IClientAppwriteClient client, Server.Clients.IServerAppwriteClient server, IRealtimeClient realtimeClient, IConfiguration config)
{
_client = client;
_server = server;
_realtimeClient = realtimeClient;
_session = config.GetValue<string>("Session");
}

private class Rec
public record Table1
{
[JsonPropertyName("test")]
public string Test { get; set; } = string.Empty;

[JsonPropertyName("boolAttribute")]
public bool BoolAttribute { get; set; }
[JsonPropertyName("test")] public string? Test { get; init; }
[JsonPropertyName("boolAttribute")] public bool BoolAttribute { get; init; }
}

public async Task Run(string[] args)
{
var before = new Rec { Test = "test", BoolAttribute = false };
var after = new Rec { Test = "test", BoolAttribute = true };

var request = UpdateDocumentRequest.CreateBuilder()
.WithDatabaseId("67541a2800221703e717")
.WithCollectionId("67541a37001514b81821")
.WithDocumentId("67541af9000055e59e59")
.WithChanges(before, after)
.Build();

var serverResponse = await _server.Databases.UpdateDocument(request);

Console.WriteLine(serverResponse.Result.Match(
result => result.ToString(),
appwriteError => appwriteError.Message,
internalError => internalError.Message));

Console.WriteLine("###############################################################################");

//Console.ReadKey();
//request.Data["test"] = "Client Update";

//var clientResponse = await _client.Databases.UpdateDocument(request);

//Console.WriteLine(clientResponse.Result.Match(
// result => result.ToString(),
// appwriteError => appwriteError.Message,
// internalError => internalError.Message));
_realtimeClient.SetSession(_session);

using (_realtimeClient.Subscribe<Document<Table1>>("documents", x =>
{
Console.WriteLine(x.Payload);
}))
{
Console.ReadKey();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@

<ItemGroup>
<ProjectReference Include="..\PinguApps.Appwrite.Client\PinguApps.Appwrite.Client.csproj" />
<ProjectReference Include="..\PinguApps.Appwrite.Realtime\PinguApps.Appwrite.Realtime.csproj" />
<ProjectReference Include="..\PinguApps.Appwrite.Server\PinguApps.Appwrite.Server.csproj" />
<ProjectReference Include="..\PinguApps.Appwrite.Shared\PinguApps.Appwrite.Shared.csproj" />
</ItemGroup>

<ProjectExtensions><VisualStudio><UserProperties /></VisualStudio></ProjectExtensions>

</Project>
2 changes: 2 additions & 0 deletions src/PinguApps.Appwrite.Playground/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
using Microsoft.Extensions.Hosting;
using PinguApps.Appwrite.Client;
using PinguApps.Appwrite.Playground;
using PinguApps.Appwrite.Realtime;
using PinguApps.Appwrite.Server;

var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddAppwriteClient(builder.Configuration.GetValue<string>("ProjectId")!);
//builder.Services.AddAppwriteClientForServer(builder.Configuration.GetValue<string>("ProjectId")!);
builder.Services.AddAppwriteServer(builder.Configuration.GetValue<string>("ProjectId")!, builder.Configuration.GetValue<string>("ApiKey")!);
builder.Services.AddAppwriteRealtime(builder.Configuration.GetValue<string>("ProjectId")!);
builder.Services.AddSingleton<App>();

var app = builder.Build();
Expand Down
27 changes: 27 additions & 0 deletions src/PinguApps.Appwrite.Realtime/DummyRealtimeClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using PinguApps.Appwrite.Realtime.Models;

namespace PinguApps.Appwrite.Realtime;
internal class DummyRealtimeClient : IRealtimeClient
{
public bool IsConnected => false;

public Task DisconnectAsync() => Task.CompletedTask;

public void SetSession(string? session)
{
}

public IDisposable Subscribe<T>(List<string> channels, Action<RealtimeResponseEvent<T>> callback) => new DummyDisposable();

public IDisposable Subscribe<T>(string channel, Action<RealtimeResponseEvent<T>> callback) => new DummyDisposable();

private class DummyDisposable : IDisposable
{
public void Dispose()
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System;

namespace PinguApps.Appwrite.Realtime.Exceptions;
public class RealtimeException : Exception
{
public int Code { get; }
public RealtimeException(string message, int code) : base(message) => Code = code;
}
14 changes: 14 additions & 0 deletions src/PinguApps.Appwrite.Realtime/IRealtimeClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using PinguApps.Appwrite.Realtime.Models;

namespace PinguApps.Appwrite.Realtime;
public interface IRealtimeClient
{
bool IsConnected { get; }
Task DisconnectAsync();
void SetSession(string? session);
IDisposable Subscribe<T>(List<string> channels, Action<RealtimeResponseEvent<T>> callback);
IDisposable Subscribe<T>(string channel, Action<RealtimeResponseEvent<T>> callback);
}
10 changes: 10 additions & 0 deletions src/PinguApps.Appwrite.Realtime/IsExternalInit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.ComponentModel;

namespace System.Runtime.CompilerServices;

#if !NET5_0_OR_GREATER

[EditorBrowsable(EditorBrowsableState.Never)]
internal static class IsExternalInit { }

#endif // !NET5_0_OR_GREATER
8 changes: 8 additions & 0 deletions src/PinguApps.Appwrite.Realtime/Models/RealtimeMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace PinguApps.Appwrite.Realtime.Models;
public record RealtimeMessage(
[property: JsonPropertyName("type")] string Type,
[property: JsonPropertyName("data")] JsonElement Data
);
8 changes: 8 additions & 0 deletions src/PinguApps.Appwrite.Realtime/Models/RealtimeRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System.Text.Json.Serialization;

namespace PinguApps.Appwrite.Realtime.Models;

public record RealtimeRequest(
[property: JsonPropertyName("type")] string Type,
[property: JsonPropertyName("data")] RealtimeRequestAuthenticate? Data
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using System.Text.Json.Serialization;

namespace PinguApps.Appwrite.Realtime.Models;

public record RealtimeRequestAuthenticate(
[property: JsonPropertyName("session")] string? Session
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
using PinguApps.Appwrite.Shared.Responses;

namespace PinguApps.Appwrite.Realtime.Models;
public record RealtimeResponseConnected(
[property: JsonPropertyName("channels")] IReadOnlyList<string> Channels,
[property: JsonPropertyName("user")] User? User
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using System.Text.Json.Serialization;

namespace PinguApps.Appwrite.Realtime.Models;
public record RealtimeResponseError(
[property: JsonPropertyName("code")] int Code,
[property: JsonPropertyName("message")] string Message
);
12 changes: 12 additions & 0 deletions src/PinguApps.Appwrite.Realtime/Models/RealtimeResponseEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using PinguApps.Appwrite.Shared.Converters;

namespace PinguApps.Appwrite.Realtime.Models;
public record RealtimeResponseEvent<T>(
[property: JsonPropertyName("events")] IReadOnlyList<string> Events,
[property: JsonPropertyName("channels")] IReadOnlyList<string> Channels,
[property: JsonPropertyName("timestamp"), JsonConverter(typeof(NullableDateTimeConverter))] DateTime? Timestamp,
[property: JsonPropertyName("payload")] T Payload
);
18 changes: 18 additions & 0 deletions src/PinguApps.Appwrite.Realtime/Models/WebSocketOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Text.Json.Serialization;

namespace PinguApps.Appwrite.Realtime.Models;
public class WebSocketOptions
{
[JsonPropertyName("reconnectInterval")]
public TimeSpan ReconnectInterval { get; set; } = TimeSpan.FromSeconds(30);

[JsonPropertyName("heartbeatInterval")]
public TimeSpan HeartbeatInterval { get; set; } = TimeSpan.FromSeconds(20);

[JsonPropertyName("maxRetryAttempts")]
public int MaxRetryAttempts { get; set; } = 5;

[JsonPropertyName("retrySleepDurationProvider")]
public Func<int, TimeSpan> RetrySleepDurationProvider { get; set; } = retryAttempt => TimeSpan.FromSeconds(((retryAttempt - 1) * 2) + 1);
}
60 changes: 60 additions & 0 deletions src/PinguApps.Appwrite.Realtime/PinguApps.Appwrite.Realtime.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>

<PackageId>PinguApps.Appwrite.Realtime</PackageId>
<PackageTags>Appwrite;Pingu;PinguApps;Pingu Apps;CSharp;Sdk;Realtime;Websocket;Websockets</PackageTags>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/PinguApps/AppwriteClient</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<Authors>Pingu</Authors>
<Company>PinguApps</Company>
<Description>A .NET implementation of the Appwrite Realtime SDK. Ideal for developers looking to leverage Appwrite's powerful backend capabilities in their .NET applications.</Description>
<Copyright>Copyright 2025 (c) Pingu. All rights reserved.</Copyright>

<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.1" />
<PackageReference Include="Refit" Version="8.0.0" />
<PackageReference Include="System.Text.Json" Version="9.0.1" />
<PackageReference Include="Websocket.Client" Version="5.1.2" />
<PackageReference Include="Polly" Version="8.5.1" />
<PackageReference Include="System.Reactive" Version="6.0.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\PinguApps.Appwrite.Shared\PinguApps.Appwrite.Shared.csproj">
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
<IncludeAssets>PinguApps.Appwrite.Shared.dll;PinguApps.Appwrite.Shared.xml;PinguApps.Appwrite.Shared.pdb</IncludeAssets>
</ProjectReference>
</ItemGroup>

<ItemGroup>
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
<None Include="..\..\icon.png" Pack="true" PackagePath="\" />
</ItemGroup>

<Target DependsOnTargets="ResolveReferences" Name="CopyProjectReferencesToPackage">
<ItemGroup>
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths-&gt;WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths-&gt;WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')-&gt;Replace('.dll', '.xml'))" />
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths-&gt;WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')-&gt;Replace('.dll', '.pdb'))" />
</ItemGroup>
</Target>

</Project>
Loading

0 comments on commit 2f4e393

Please sign in to comment.