Json (de)serilazion works with some tests
This commit is contained in:
parent
b4540af9db
commit
a7a087b637
31
Discord.API.Tests/Discord.API.Tests.csproj
Normal file
31
Discord.API.Tests/Discord.API.Tests.csproj
Normal file
@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
|
||||
<PackageReference Include="xunit" Version="2.5.3"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Discord.API\Discord.API.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
149
Discord.API.Tests/JsonTests.cs
Normal file
149
Discord.API.Tests/JsonTests.cs
Normal file
@ -0,0 +1,149 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Discord.API.Tests;
|
||||
using Xunit;
|
||||
using Discord.API;
|
||||
|
||||
public class JsonTests
|
||||
{
|
||||
[Fact]
|
||||
public void GatewayPacketEncodeJsonTest()
|
||||
{
|
||||
var gateway_packet = new GatewayPacket()
|
||||
{
|
||||
Op = GatewayPacket.Opcode.Heartbeat
|
||||
};
|
||||
|
||||
string json = JsonSerializer.Serialize(gateway_packet, SourceGenerationContext.Default.GatewayPacket);
|
||||
|
||||
Assert.Equal("""{"op":1}""", json);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GatewayPacketDecodeTest()
|
||||
{
|
||||
const string src = """{"op":1}""";
|
||||
var gateway_packet =
|
||||
JsonSerializer.Deserialize<GatewayPacket>(src, SourceGenerationContext.Default.GatewayPacket);
|
||||
Assert.Equal(GatewayPacket.Opcode.Heartbeat, gateway_packet?.Op);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadyPacketDeserialize()
|
||||
{
|
||||
const string src = """
|
||||
{
|
||||
"op":0,
|
||||
"t":"READY",
|
||||
"s":1,
|
||||
"d":{
|
||||
"v":10,
|
||||
"user":{
|
||||
"id":"1234",
|
||||
"username":"abrakadabra",
|
||||
"discriminator":"1111",
|
||||
"global_name":"glblname"
|
||||
},
|
||||
"guilds":[
|
||||
{
|
||||
"id":"5678",
|
||||
"unavailable":true
|
||||
}
|
||||
],
|
||||
"session_id":"abcd",
|
||||
"resume_gateway_url":"dfgh",
|
||||
"application":{
|
||||
"id":"3333",
|
||||
"flags":5555
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
GatewayPacket? packet =
|
||||
JsonSerializer.Deserialize<GatewayPacket>(src, SourceGenerationContext.Default.GatewayPacket);
|
||||
|
||||
Assert.NotNull(packet);
|
||||
Assert.IsType<ReadyPacket>(packet);
|
||||
|
||||
Assert.True(packet is ReadyPacket
|
||||
{
|
||||
Op: GatewayPacket.Opcode.Dispatch,
|
||||
Event: "READY",
|
||||
Sequence: 1,
|
||||
Data:
|
||||
{
|
||||
User:
|
||||
{
|
||||
Id:1234,
|
||||
Username: "abrakadabra",
|
||||
Discriminator: "1111",
|
||||
GlobalName: "glblname"
|
||||
},
|
||||
Version: 10,
|
||||
SessionId: "abcd",
|
||||
ResumeGatewayUrl: "dfgh",
|
||||
Guilds:[
|
||||
{
|
||||
Id: 5678,
|
||||
Unavailable: true
|
||||
}
|
||||
],
|
||||
Application:
|
||||
{
|
||||
Id: 3333,
|
||||
Flags: 5555
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChannelCreateDeserialize()
|
||||
{
|
||||
string src = """
|
||||
{
|
||||
"op":0,
|
||||
"t":"CHANNEL_CREATE",
|
||||
"s":1,
|
||||
"d":{
|
||||
"id": "922243411795390570",
|
||||
"type": 2,
|
||||
"guild_id": "5678",
|
||||
"position": 3,
|
||||
"name": "voice csennel",
|
||||
"topic": "A very interesting topic",
|
||||
"nsfw": true,
|
||||
"last_message_id":"6969",
|
||||
"bitrate":420,
|
||||
"parent_id": "5555"
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
var gateway_packet =
|
||||
JsonSerializer.Deserialize(src, SourceGenerationContext.Default.GatewayPacket);
|
||||
|
||||
Assert.IsType<ChannelCreatePacket>(gateway_packet);
|
||||
|
||||
Assert.True(gateway_packet is ChannelCreatePacket
|
||||
{
|
||||
Op: GatewayPacket.Opcode.Dispatch,
|
||||
Event: "CHANNEL_CREATE",
|
||||
Sequence: 1,
|
||||
Data:
|
||||
{
|
||||
Id: 922243411795390570,
|
||||
Name: "voice csennel",
|
||||
GuildId: 5678,
|
||||
Type: 2,
|
||||
Position: 3,
|
||||
Topic: "A very interesting topic",
|
||||
Nsfw: true,
|
||||
Bitrate: 420,
|
||||
ParentId: 5555,
|
||||
LastMessageId: 6969
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@ -3,8 +3,7 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace Discord.API;
|
||||
|
||||
[JsonDerivedType(typeof(DispatchPacket), "CHANNEL_CREATE")]
|
||||
public class ChannelCreatePacket : DispatchPacket
|
||||
internal class ChannelCreatePacket : DispatchPacket
|
||||
{
|
||||
[JsonRequired]
|
||||
[JsonPropertyName("d")]
|
||||
|
||||
@ -3,8 +3,7 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace Discord.API;
|
||||
|
||||
[JsonDerivedType(typeof(DispatchPacket), "CHANNEL_DELETE")]
|
||||
public class ChannelDeletePacket : DispatchPacket
|
||||
internal class ChannelDeletePacket : DispatchPacket
|
||||
{
|
||||
[JsonRequired]
|
||||
[JsonPropertyName("d")]
|
||||
|
||||
@ -3,8 +3,7 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace Discord.API;
|
||||
|
||||
[JsonDerivedType(typeof(DispatchPacket), "CHANNEL_UPDATE")]
|
||||
public class ChannelUpdatePacket : DispatchPacket
|
||||
internal class ChannelUpdatePacket : DispatchPacket
|
||||
{
|
||||
[JsonRequired]
|
||||
[JsonPropertyName("d")]
|
||||
|
||||
@ -3,9 +3,12 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace Discord.API;
|
||||
|
||||
[JsonDerivedType(typeof(GatewayPacket), (int)Opcode.Dispatch)]
|
||||
[JsonPolymorphic(IgnoreUnrecognizedTypeDiscriminators = true, TypeDiscriminatorPropertyName = "t", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)]
|
||||
public class DispatchPacket : GatewayPacket
|
||||
[JsonDerivedType(typeof(ChannelCreatePacket))]
|
||||
[JsonDerivedType(typeof(ChannelUpdatePacket))]
|
||||
[JsonDerivedType(typeof(ChannelDeletePacket))]
|
||||
[JsonDerivedType(typeof(ReadyPacket))]
|
||||
internal class DispatchPacket : GatewayPacket
|
||||
{
|
||||
[JsonPropertyName("t")]
|
||||
[JsonRequired]
|
||||
|
||||
@ -3,8 +3,9 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace Discord.API;
|
||||
|
||||
[JsonDerivedType(typeof(DispatchPacket), "READY")]
|
||||
public class ReadyPacket : DispatchPacket
|
||||
[JsonPolymorphic(TypeDiscriminatorPropertyName = "t", IgnoreUnrecognizedTypeDiscriminators = true, UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)]
|
||||
[JsonDerivedType(typeof(ReadyPacket))]
|
||||
internal class ReadyPacket : DispatchPacket
|
||||
{
|
||||
public sealed class ReadyData
|
||||
{
|
||||
@ -15,13 +16,13 @@ public class ReadyPacket : DispatchPacket
|
||||
public UserData User;
|
||||
|
||||
[JsonRequired]
|
||||
public UnavailableGuildData Guilds;
|
||||
public UnavailableGuildData[] Guilds;
|
||||
|
||||
[JsonRequired]
|
||||
public string SessionId;
|
||||
|
||||
[JsonRequired]
|
||||
public string ResumeUrl;
|
||||
public string ResumeGatewayUrl;
|
||||
|
||||
[JsonRequired]
|
||||
public PartialApplicationData Application;
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Discord.API;
|
||||
|
||||
[JsonPolymorphic(IgnoreUnrecognizedTypeDiscriminators = false, TypeDiscriminatorPropertyName = "op", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)]
|
||||
public class GatewayPacket
|
||||
[JsonPolymorphic(IgnoreUnrecognizedTypeDiscriminators = true, TypeDiscriminatorPropertyName = "op", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)]
|
||||
[JsonDerivedType(typeof(IdentifyPacket))]
|
||||
[JsonDerivedType(typeof(DispatchPacket))]
|
||||
internal class GatewayPacket
|
||||
{
|
||||
public enum Opcode
|
||||
{
|
||||
|
||||
72
Discord.API/GatewayPacketTypes/GatewayPacketConverter.cs
Normal file
72
Discord.API/GatewayPacketTypes/GatewayPacketConverter.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Discord.API;
|
||||
|
||||
internal class GatewayPacketConverter : JsonConverter<GatewayPacket>
|
||||
{
|
||||
public override GatewayPacket? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
JsonDocument json_doc = JsonDocument.ParseValue(ref reader);
|
||||
if(json_doc.RootElement.TryGetProperty("op", out JsonElement opcode_element))
|
||||
{
|
||||
if (opcode_element.ValueKind != JsonValueKind.Number)
|
||||
{
|
||||
throw new JsonException("Opcode is not a number");
|
||||
}
|
||||
|
||||
GatewayPacket.Opcode op = (GatewayPacket.Opcode)opcode_element.GetInt32();
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GatewayPacket.Opcode.Dispatch:
|
||||
if (json_doc.RootElement.TryGetProperty("t", out JsonElement event_element))
|
||||
{
|
||||
if (event_element.ValueKind != JsonValueKind.String)
|
||||
{
|
||||
throw new Exception("Event name not string");
|
||||
}
|
||||
|
||||
string? event_name = event_element.GetString();
|
||||
switch (event_name)
|
||||
{
|
||||
case "READY":
|
||||
return json_doc.Deserialize(SourceGenerationContext.Default.ReadyPacket);
|
||||
case "CHANNEL_CREATE":
|
||||
return json_doc.Deserialize(SourceGenerationContext.Default
|
||||
.ChannelCreatePacket);
|
||||
case "CHANNEL_UPDATE":
|
||||
return json_doc.Deserialize(SourceGenerationContext.Default.ChannelUpdatePacket);
|
||||
case "CHANNEL_DELETE":
|
||||
return json_doc.Deserialize(SourceGenerationContext.Default.ChannelDeletePacket);
|
||||
default:
|
||||
throw new NotSupportedException($"Packet {event_name} is not supported");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new JsonException("Event name not found");
|
||||
}
|
||||
default:
|
||||
throw new NotSupportedException($"Opcode {op} is not supported in json deserialization");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Opcode not found");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, GatewayPacket value, JsonSerializerOptions options)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case IdentifyPacket id_packet:
|
||||
writer.WriteRawValue(JsonSerializer.SerializeToUtf8Bytes(id_packet, SourceGenerationContext.Default.IdentifyPacket));
|
||||
break;
|
||||
default:
|
||||
writer.WriteRawValue(JsonSerializer.SerializeToUtf8Bytes(value, SourceGenerationContext.Default.IdentifyPacket));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
using System.Text.Json.Serialization;
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
|
||||
namespace Discord.API;
|
||||
|
||||
//[JsonDerivedType(typeof(GatewayPacket), (int)Opcode.Identify)]
|
||||
public class IdentifyPacket : GatewayPacket
|
||||
internal class IdentifyPacket : GatewayPacket
|
||||
{
|
||||
public sealed class IdentifyData
|
||||
{
|
||||
@ -19,7 +19,7 @@ public class IdentifyPacket : GatewayPacket
|
||||
[JsonInclude]
|
||||
[JsonRequired]
|
||||
[JsonPropertyName("token")]
|
||||
public required string Token { get; set; }
|
||||
public required string Token { get; init; }
|
||||
[JsonInclude]
|
||||
public PropertiesClass Properties => PropertiesClass.Instance;
|
||||
[JsonInclude]
|
||||
@ -27,10 +27,10 @@ public class IdentifyPacket : GatewayPacket
|
||||
public int LargeThreshold = 250;
|
||||
public int[]? Shard = null;
|
||||
//presence
|
||||
public ulong Intents { get; init; }
|
||||
public required ulong Intents { get; init; }
|
||||
|
||||
}
|
||||
|
||||
[JsonPropertyName("d")]
|
||||
public IdentifyData Data;
|
||||
public required IdentifyData Data { get; init; }
|
||||
}
|
||||
3
Discord.API/InternalsVisible.cs
Normal file
3
Discord.API/InternalsVisible.cs
Normal file
@ -0,0 +1,3 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly:InternalsVisibleTo("Discord.API.Tests")]
|
||||
@ -1,3 +1,4 @@
|
||||
using System.Runtime.InteropServices.JavaScript;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
|
||||
@ -5,9 +6,20 @@ namespace Discord.API;
|
||||
|
||||
using System.Text.Json;
|
||||
|
||||
[JsonSourceGenerationOptions(IgnoreReadOnlyFields = false, IgnoreReadOnlyProperties = false, IncludeFields = true, PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower)]
|
||||
[JsonSourceGenerationOptions(IgnoreReadOnlyFields = false,
|
||||
IgnoreReadOnlyProperties = false,
|
||||
IncludeFields = true,
|
||||
PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower,
|
||||
Converters = [typeof(GatewayPacketConverter)],
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString
|
||||
)]
|
||||
[JsonSerializable(typeof(GatewayPacket))]
|
||||
[JsonSerializable(typeof(IdentifyPacket))]
|
||||
[JsonSerializable(typeof(ChannelCreatePacket))]
|
||||
[JsonSerializable(typeof(ChannelUpdatePacket))]
|
||||
[JsonSerializable(typeof(ChannelDeletePacket))]
|
||||
[JsonSerializable(typeof(DispatchPacket))]
|
||||
[JsonSerializable(typeof(ReadyPacket))]
|
||||
internal partial class SourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.API", "Discord.API\
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example_bot", "example_bot\example_bot.csproj", "{331F5928-8E65-4782-8311-BC1E80F1C002}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.API.Tests", "Discord.API.Tests\Discord.API.Tests.csproj", "{E89333A2-11C4-4CFF-9F45-32D951E871CA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -30,5 +32,9 @@ Global
|
||||
{331F5928-8E65-4782-8311-BC1E80F1C002}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{331F5928-8E65-4782-8311-BC1E80F1C002}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{331F5928-8E65-4782-8311-BC1E80F1C002}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E89333A2-11C4-4CFF-9F45-32D951E871CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E89333A2-11C4-4CFF-9F45-32D951E871CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E89333A2-11C4-4CFF-9F45-32D951E871CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E89333A2-11C4-4CFF-9F45-32D951E871CA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
Loading…
Reference in New Issue
Block a user