starting working on activity
This commit is contained in:
parent
e1ef37d504
commit
4875e71340
17 changed files with 329 additions and 5 deletions
30
src/BirdsiteLive.ActivityPub/ApDeserializer.cs
Normal file
30
src/BirdsiteLive.ActivityPub/ApDeserializer.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using Newtonsoft.Json;
|
||||
|
||||
namespace BirdsiteLive.ActivityPub
|
||||
{
|
||||
public class ApDeserializer
|
||||
{
|
||||
public static Activity ProcessActivity(string json)
|
||||
{
|
||||
var activity = JsonConvert.DeserializeObject<Activity>(json);
|
||||
switch (activity.type)
|
||||
{
|
||||
case "Follow":
|
||||
return JsonConvert.DeserializeObject<ActivityFollow>(json);
|
||||
case "Undo":
|
||||
var a = JsonConvert.DeserializeObject<ActivityUndo>(json);
|
||||
if(a.apObject.type == "Follow")
|
||||
return JsonConvert.DeserializeObject<ActivityUndoFollow>(json);
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private class Ac : Activity
|
||||
{
|
||||
[JsonProperty("object")]
|
||||
public Activity apObject { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="System.Text.Json" Version="4.7.2" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
16
src/BirdsiteLive.ActivityPub/Models/Activity.cs
Normal file
16
src/BirdsiteLive.ActivityPub/Models/Activity.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BirdsiteLive.ActivityPub
|
||||
{
|
||||
public class Activity
|
||||
{
|
||||
[JsonProperty("@context")]
|
||||
public string context { get; set; }
|
||||
public string id { get; set; }
|
||||
public string type { get; set; }
|
||||
public string actor { get; set; }
|
||||
//[JsonProperty("object")]
|
||||
//public string apObject { get; set; }
|
||||
}
|
||||
}
|
10
src/BirdsiteLive.ActivityPub/Models/ActivityFollow.cs
Normal file
10
src/BirdsiteLive.ActivityPub/Models/ActivityFollow.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using Newtonsoft.Json;
|
||||
|
||||
namespace BirdsiteLive.ActivityPub
|
||||
{
|
||||
public class ActivityFollow : Activity
|
||||
{
|
||||
[JsonProperty("object")]
|
||||
public string apObject { get; set; }
|
||||
}
|
||||
}
|
10
src/BirdsiteLive.ActivityPub/Models/ActivityUndo.cs
Normal file
10
src/BirdsiteLive.ActivityPub/Models/ActivityUndo.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using Newtonsoft.Json;
|
||||
|
||||
namespace BirdsiteLive.ActivityPub
|
||||
{
|
||||
public class ActivityUndo : Activity
|
||||
{
|
||||
[JsonProperty("object")]
|
||||
public Activity apObject { get; set; }
|
||||
}
|
||||
}
|
10
src/BirdsiteLive.ActivityPub/Models/ActivityUndoFollow.cs
Normal file
10
src/BirdsiteLive.ActivityPub/Models/ActivityUndoFollow.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using Newtonsoft.Json;
|
||||
|
||||
namespace BirdsiteLive.ActivityPub
|
||||
{
|
||||
public class ActivityUndoFollow : Activity
|
||||
{
|
||||
[JsonProperty("object")]
|
||||
public ActivityFollow apObject { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,12 +1,14 @@
|
|||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BirdsiteLive.ActivityPub
|
||||
{
|
||||
public class Actor
|
||||
{
|
||||
[JsonPropertyName("@context")]
|
||||
public string[] context { get; set; } = new[] {"https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"};
|
||||
//[JsonPropertyName("@context")]
|
||||
[JsonProperty("@context")]
|
||||
public object[] context { get; set; } = new[] {"https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1" };
|
||||
public string id { get; set; }
|
||||
public string type { get; set; }
|
||||
public string preferredUsername { get; set; }
|
26
src/BirdsiteLive.Domain/ActivityPubService.cs
Normal file
26
src/BirdsiteLive.Domain/ActivityPubService.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using BirdsiteLive.ActivityPub;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BirdsiteLive.Domain
|
||||
{
|
||||
public interface IActivityPubService
|
||||
{
|
||||
Task<Actor> GetUser(string objectId);
|
||||
}
|
||||
|
||||
public class ActivityPubService : IActivityPubService
|
||||
{
|
||||
public async Task<Actor> GetUser(string objectId)
|
||||
{
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||
var result = await httpClient.GetAsync(objectId);
|
||||
var content = await result.Content.ReadAsStringAsync();
|
||||
return JsonConvert.DeserializeObject<Actor>(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +1,33 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BirdsiteLive.ActivityPub;
|
||||
using BirdsiteLive.Common.Settings;
|
||||
using BirdsiteLive.Cryptography;
|
||||
using BirdsiteLive.Twitter.Models;
|
||||
using Tweetinvi.Core.Exceptions;
|
||||
|
||||
namespace BirdsiteLive.Domain
|
||||
{
|
||||
public interface IUserService
|
||||
{
|
||||
Actor GetUser(TwitterUser twitterUser);
|
||||
Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity);
|
||||
}
|
||||
|
||||
public class UserService : IUserService
|
||||
{
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IActivityPubService _activityPubService;
|
||||
private readonly string _host;
|
||||
|
||||
#region Ctor
|
||||
public UserService(InstanceSettings instanceSettings, ICryptoService cryptoService)
|
||||
public UserService(InstanceSettings instanceSettings, ICryptoService cryptoService, IActivityPubService activityPubService)
|
||||
{
|
||||
_cryptoService = cryptoService;
|
||||
_activityPubService = activityPubService;
|
||||
_host = $"https://{instanceSettings.Domain.Replace("https://",string.Empty).Replace("http://", string.Empty).TrimEnd('/')}";
|
||||
}
|
||||
#endregion
|
||||
|
@ -53,5 +62,78 @@ namespace BirdsiteLive.Domain
|
|||
};
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity)
|
||||
{
|
||||
// Validate
|
||||
if (!await ValidateSignature(activity.actor, signature, method, path, queryString, requestHeaders)) return false;
|
||||
|
||||
// Save Follow in DB
|
||||
|
||||
// Send Accept Activity
|
||||
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private async Task<bool> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary<string, string> requestHeaders)
|
||||
{
|
||||
var signatures = rawSig.Split(',');
|
||||
var signature_header = new Dictionary<string, string>();
|
||||
foreach (var signature in signatures)
|
||||
{
|
||||
var splitSig = signature.Replace("\"", string.Empty).Split('=');
|
||||
signature_header.Add(splitSig[0], splitSig[1]);
|
||||
}
|
||||
|
||||
signature_header["signature"] = signature_header["signature"] + "==";
|
||||
|
||||
var key_id = signature_header["keyId"];
|
||||
var headers = signature_header["headers"];
|
||||
var algorithm = signature_header["algorithm"];
|
||||
var sig = Convert.FromBase64String(signature_header["signature"]);
|
||||
|
||||
|
||||
var remoteUser = await _activityPubService.GetUser(actor);
|
||||
|
||||
var toDecode = remoteUser.publicKey.publicKeyPem.Trim().Remove(0, remoteUser.publicKey.publicKeyPem.IndexOf('\n'));
|
||||
toDecode = toDecode.Remove(toDecode.LastIndexOf('\n')).Replace("\n", "");
|
||||
var signKey = ASN1.ToRSA(Convert.FromBase64String(toDecode));
|
||||
|
||||
var toSign = new StringBuilder();
|
||||
//var comparisonString = headers.Split(' ').Select(signed_header_name =>
|
||||
//{
|
||||
// if (signed_header_name == "(request-target)")
|
||||
// return "(request-target): post /inbox";
|
||||
// else
|
||||
// return $"{signed_header_name}: {r.Headers[signed_header_name.ToUpperInvariant()]}";
|
||||
//});
|
||||
|
||||
foreach (var headerKey in headers.Split(' '))
|
||||
{
|
||||
if (headerKey == "(request-target)") toSign.Append($"(request-target): {method.ToLower()} {path}{queryString}\n");
|
||||
else toSign.Append($"{headerKey}: {string.Join(", ", requestHeaders[headerKey])}\n");
|
||||
}
|
||||
toSign.Remove(toSign.Length - 1, 1);
|
||||
|
||||
//var signKey = ASN1.ToRSA(Convert.FromBase64String(toDecode));
|
||||
|
||||
//new RSACryptoServiceProvider(keyId.publicKey.publicKeyPem);
|
||||
|
||||
//Create a new instance of RSACryptoServiceProvider.
|
||||
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
|
||||
|
||||
//Get an instance of RSAParameters from ExportParameters function.
|
||||
RSAParameters RSAKeyInfo = key.ExportParameters(false);
|
||||
|
||||
//Set RSAKeyInfo to the public key values.
|
||||
RSAKeyInfo.Modulus = Convert.FromBase64String(toDecode);
|
||||
|
||||
key.ImportParameters(RSAKeyInfo);
|
||||
|
||||
var result = signKey.VerifyData(Encoding.UTF8.GetBytes(toSign.ToString()), sig, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.Domain", "Bird
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.ActivityPub", "BirdsiteLive.ActivityPub\BirdsiteLive.ActivityPub.csproj", "{7463E1E2-9736-4A46-8507-010BDD8ECFBB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.ActivityPub.Tests", "Tests\BirdsiteLive.ActivityPub.Tests\BirdsiteLive.ActivityPub.Tests.csproj", "{1D713961-9926-41FF-8D6A-8A4B8D548484}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -55,6 +57,10 @@ Global
|
|||
{7463E1E2-9736-4A46-8507-010BDD8ECFBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7463E1E2-9736-4A46-8507-010BDD8ECFBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7463E1E2-9736-4A46-8507-010BDD8ECFBB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -66,6 +72,7 @@ Global
|
|||
{155D46A4-2D05-47F2-8FFC-0B7C412A7652} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
||||
{D48450EE-D8BD-4228-9864-043AC88F7EE0} = {4FEAD6BC-3C8E-451A-8CA1-FF1AF47D26CC}
|
||||
{7463E1E2-9736-4A46-8507-010BDD8ECFBB} = {4FEAD6BC-3C8E-451A-8CA1-FF1AF47D26CC}
|
||||
{1D713961-9926-41FF-8D6A-8A4B8D548484} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {69E8DCAD-4C37-4010-858F-5F94E6FBABCE}
|
||||
|
|
|
@ -4,9 +4,13 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BirdsiteLive.ActivityPub;
|
||||
using BirdsiteLive.Domain;
|
||||
using BirdsiteLive.Twitter;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BirdsiteLive.Controllers
|
||||
{
|
||||
|
@ -34,7 +38,8 @@ namespace BirdsiteLive.Controllers
|
|||
if (r.Contains("application/activity+json"))
|
||||
{
|
||||
var apUser = _userService.GetUser(user);
|
||||
return Json(apUser);
|
||||
var jsonApUser = JsonConvert.SerializeObject(apUser);
|
||||
return Content(jsonApUser, "application/json");
|
||||
}
|
||||
|
||||
return View(user);
|
||||
|
@ -48,11 +53,27 @@ namespace BirdsiteLive.Controllers
|
|||
using (var reader = new StreamReader(Request.Body))
|
||||
{
|
||||
var body = await reader.ReadToEndAsync();
|
||||
|
||||
var activity = ApDeserializer.ProcessActivity(body);
|
||||
// Do something
|
||||
|
||||
switch (activity.type)
|
||||
{
|
||||
case "Follow":
|
||||
var succeeded = await _userService.FollowRequestedAsync(r.Headers["Signature"].First(), r.Method, r.Path, r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow);
|
||||
if (succeeded) return Ok();
|
||||
else return Unauthorized();
|
||||
break;
|
||||
default:
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
private Dictionary<string, string> RequestHeaders(IHeaderDictionary header)
|
||||
{
|
||||
return header.ToDictionary<KeyValuePair<string, StringValues>, string, string>(h => h.Key, h => h.Value);
|
||||
}
|
||||
}
|
||||
}
|
33
src/Tests/BirdsiteLive.ActivityPub.Tests/ActivityTests.cs
Normal file
33
src/Tests/BirdsiteLive.ActivityPub.Tests/ActivityTests.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BirdsiteLive.ActivityPub.Tests
|
||||
{
|
||||
//[TestClass]
|
||||
//public class ActivityTests
|
||||
//{
|
||||
// [TestMethod]
|
||||
// public void FollowDeserializationTest()
|
||||
// {
|
||||
// var json = "{ \"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://mastodon.technology/c94567cf-1fda-42ba-82fc-a0f82f63ccbe\",\"type\":\"Follow\",\"actor\":\"https://mastodon.technology/users/testtest\",\"object\":\"https://4a120ca2680e.ngrok.io/users/manu\"}";
|
||||
|
||||
// var data = JsonConvert.DeserializeObject<Activity>(json);
|
||||
|
||||
// Assert.AreEqual("https://mastodon.technology/c94567cf-1fda-42ba-82fc-a0f82f63ccbe", data.id);
|
||||
// Assert.AreEqual("Follow", data.type);
|
||||
// Assert.AreEqual("https://4a120ca2680e.ngrok.io/users/manu", data.apObject);
|
||||
// }
|
||||
|
||||
// [TestMethod]
|
||||
// public void UndoDeserializationTest()
|
||||
// {
|
||||
// var json =
|
||||
// "{\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://mastodon.technology/users/testtest#follows/225982/undo\",\"type\":\"Undo\",\"actor\":\"https://mastodon.technology/users/testtest\",\"object\":{\"id\":\"https://mastodon.technology/c94567cf-1fda-42ba-82fc-a0f82f63ccbe\",\"type\":\"Follow\",\"actor\":\"https://mastodon.technology/users/testtest\",\"object\":\"https://4a120ca2680e.ngrok.io/users/manu\"}}";
|
||||
|
||||
// var data = JsonConvert.DeserializeObject<Activity>(json);
|
||||
// Assert.AreEqual("https://mastodon.technology/users/testtest#follows/225982/undo", data.id);
|
||||
// Assert.AreEqual("Undo", data.type);
|
||||
// Assert.AreEqual("https://4a120ca2680e.ngrok.io/users/manu", data.apObject);
|
||||
// }
|
||||
//}
|
||||
}
|
21
src/Tests/BirdsiteLive.ActivityPub.Tests/ActorTests.cs
Normal file
21
src/Tests/BirdsiteLive.ActivityPub.Tests/ActorTests.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BirdsiteLive.ActivityPub.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class ActorTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Deserialize()
|
||||
{
|
||||
var json =
|
||||
"{\"@context\":[\"https://www.w3.org/ns/activitystreams\",\"https://w3id.org/security/v1\",{\"manuallyApprovesFollowers\":\"as:manuallyApprovesFollowers\",\"toot\":\"http://joinmastodon.org/ns#\",\"featured\":{\"@id\":\"toot:featured\",\"@type\":\"@id\"},\"alsoKnownAs\":{\"@id\":\"as:alsoKnownAs\",\"@type\":\"@id\"},\"movedTo\":{\"@id\":\"as:movedTo\",\"@type\":\"@id\"},\"schema\":\"http://schema.org#\",\"PropertyValue\":\"schema:PropertyValue\",\"value\":\"schema:value\",\"IdentityProof\":\"toot:IdentityProof\",\"discoverable\":\"toot:discoverable\"}],\"id\":\"https://mastodon.technology/users/testtest\",\"type\":\"Person\",\"following\":\"https://mastodon.technology/users/testtest/following\",\"followers\":\"https://mastodon.technology/users/testtest/followers\",\"inbox\":\"https://mastodon.technology/users/testtest/inbox\",\"outbox\":\"https://mastodon.technology/users/testtest/outbox\",\"featured\":\"https://mastodon.technology/users/testtest/collections/featured\",\"preferredUsername\":\"testtest\",\"name\":\"TESTEST\",\"summary\":\"\u003cp\u003etest \u003cbr /\u003edsqdq65d4sq56d456q4d8zd4q685d45qd4sqd2q1d5zq56d465qsd4q65sd21qsd23q1s5d64qsd8q465d4s5q1d6qsd35qs4dq6sd84q\u003c/p\u003e\",\"url\":\"https://mastodon.technology/@testtest\",\"manuallyApprovesFollowers\":false,\"discoverable\":false,\"publicKey\":{\"id\":\"https://mastodon.technology/users/testtest#main-key\",\"owner\":\"https://mastodon.technology/users/testtest\",\"publicKeyPem\":\"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm7BlbWI/UD/YJj288h/5\nFB0gXZj0BjYVaK28uzTvb4w6eMu4qpbE9NI0bFqrloXzL3z6PaOCL4Myz9uJYolE\nZ9uNVi2OeZmHigNEOT3hkJWzddtrhkg8MLXKPdOETjhVWV3n+na7QWDDIXP7Fuvi\n+osA5LOoqtD1rYs87xUcWQPLCtVHs928FXsCdLO11ofXiNrancSzY17nkuufjWO+\ndLtvz1kx4Mt2V4Fu+DHskQAzPKU2tzGBrtlVQrk+1R63psIuZYDB6e4i7L6/d1Xl\nIQGmBeJfyxiuNIlbfZIbJ3xPYBQaVAnRKtyGVEFMWwZCqMySwc2LBX+rxI20zJ0R\n7wIDAQAB\n-----END PUBLIC KEY-----\n\"},\"tag\":[],\"attachment\":[],\"endpoints\":{\"sharedInbox\":\"https://mastodon.technology/inbox\"}}";
|
||||
|
||||
|
||||
var actor = JsonConvert.DeserializeObject<Actor>(json);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BirdsiteLive.ActivityPub.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class ApDeserializerTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void FollowDeserializationTest()
|
||||
{
|
||||
var json = "{ \"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://mastodon.technology/c94567cf-1fda-42ba-82fc-a0f82f63ccbe\",\"type\":\"Follow\",\"actor\":\"https://mastodon.technology/users/testtest\",\"object\":\"https://4a120ca2680e.ngrok.io/users/manu\"}";
|
||||
|
||||
var data = ApDeserializer.ProcessActivity(json) as ActivityFollow;
|
||||
|
||||
Assert.AreEqual("https://mastodon.technology/c94567cf-1fda-42ba-82fc-a0f82f63ccbe", data.id);
|
||||
Assert.AreEqual("Follow", data.type);
|
||||
Assert.AreEqual("https://4a120ca2680e.ngrok.io/users/manu", data.apObject);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UndoDeserializationTest()
|
||||
{
|
||||
var json =
|
||||
"{\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://mastodon.technology/users/testtest#follows/225982/undo\",\"type\":\"Undo\",\"actor\":\"https://mastodon.technology/users/testtest\",\"object\":{\"id\":\"https://mastodon.technology/c94567cf-1fda-42ba-82fc-a0f82f63ccbe\",\"type\":\"Follow\",\"actor\":\"https://mastodon.technology/users/testtest\",\"object\":\"https://4a120ca2680e.ngrok.io/users/manu\"}}";
|
||||
|
||||
var data = ApDeserializer.ProcessActivity(json) as ActivityUndoFollow;
|
||||
Assert.AreEqual("https://mastodon.technology/users/testtest#follows/225982/undo", data.id);
|
||||
Assert.AreEqual("Undo", data.type);
|
||||
Assert.AreEqual("Follow", data.apObject.type);
|
||||
Assert.AreEqual("https://mastodon.technology/users/testtest", data.apObject.actor);
|
||||
Assert.AreEqual("https://4a120ca2680e.ngrok.io/users/manu", data.apObject.apObject);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.0" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\BirdsiteLive.ActivityPub\BirdsiteLive.ActivityPub.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Reference in a new issue