fix Accept Follow serialization
This commit is contained in:
parent
8b5d03e0f1
commit
29ba6baddb
8 changed files with 141 additions and 54 deletions
|
@ -56,10 +56,5 @@ namespace BirdsiteLive.ActivityPub
|
|||
return null;
|
||||
}
|
||||
|
||||
private class Ac : Activity
|
||||
{
|
||||
[JsonPropertyName("object")]
|
||||
public Activity apObject { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ namespace BirdsiteLive.ActivityPub
|
|||
public class Activity
|
||||
{
|
||||
[JsonPropertyName("@context")]
|
||||
public object context { get; set; }
|
||||
public string context { get; set; }
|
||||
public string id { get; set; }
|
||||
public string type { get; set; }
|
||||
public string actor { get; set; }
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace BirdsiteLive.ActivityPub
|
|||
public class NestedActivity
|
||||
{
|
||||
[JsonPropertyName("@context")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public object context { get; set; }
|
||||
public string id { get; set; }
|
||||
public string type { get; set; }
|
||||
|
|
|
@ -21,6 +21,8 @@ namespace BirdsiteLive.Domain
|
|||
Task<HttpStatusCode> PostDataAsync<T>(T data, string targetHost, string actorUrl, string inbox = null);
|
||||
Task PostNewActivity(ActivityCreateNote note, string username, string noteId, string targetHost,
|
||||
string targetInbox);
|
||||
|
||||
ActivityAcceptFollow BuildAcceptFollow(ActivityFollow activity);
|
||||
}
|
||||
|
||||
public class ActivityPubService : IActivityPubService
|
||||
|
@ -73,13 +75,33 @@ namespace BirdsiteLive.Domain
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<HttpStatusCode> PostDataAsync<T>(T data, string targetHost, string actorUrl, string inbox = null)
|
||||
public ActivityAcceptFollow BuildAcceptFollow(ActivityFollow activity)
|
||||
{
|
||||
var acceptFollow = new ActivityAcceptFollow()
|
||||
{
|
||||
context = "https://www.w3.org/ns/activitystreams",
|
||||
id = $"{activity.apObject}#accepts/follows/{Guid.NewGuid()}",
|
||||
type = "Accept",
|
||||
actor = activity.apObject,
|
||||
apObject = new NestedActivity()
|
||||
{
|
||||
id = activity.id,
|
||||
type = activity.type,
|
||||
actor = activity.actor,
|
||||
apObject = activity.apObject
|
||||
}
|
||||
};
|
||||
return acceptFollow;
|
||||
}
|
||||
public HttpRequestMessage BuildRequest<T>(T data, string targetHost, string actorUrl,
|
||||
string inbox = null)
|
||||
{
|
||||
var usedInbox = $"/inbox";
|
||||
if (!string.IsNullOrWhiteSpace(inbox))
|
||||
usedInbox = inbox;
|
||||
|
||||
var json = JsonSerializer.Serialize(data, new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
|
||||
var json = JsonSerializer.Serialize(data,
|
||||
new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
|
||||
|
||||
var date = DateTime.UtcNow.ToUniversalTime();
|
||||
var httpDate = date.ToString("r");
|
||||
|
@ -88,26 +110,33 @@ namespace BirdsiteLive.Domain
|
|||
|
||||
var signature = _cryptoService.SignAndGetSignatureHeader(date, actorUrl, targetHost, digest, usedInbox);
|
||||
|
||||
var client = _httpClientFactory.CreateClient();
|
||||
client.Timeout = TimeSpan.FromSeconds(2);
|
||||
var httpRequestMessage = new HttpRequestMessage
|
||||
{
|
||||
Method = HttpMethod.Post,
|
||||
RequestUri = new Uri($"https://{targetHost}{usedInbox}"),
|
||||
Headers =
|
||||
{
|
||||
{"Host", targetHost},
|
||||
{"Date", httpDate},
|
||||
{"Signature", signature},
|
||||
{"Digest", $"SHA-256={digest}"}
|
||||
{ "Host", targetHost },
|
||||
{ "Date", httpDate },
|
||||
{ "Signature", signature },
|
||||
{ "Digest", $"SHA-256={digest}" }
|
||||
},
|
||||
Content = new StringContent(json, Encoding.UTF8, "application/ld+json")
|
||||
};
|
||||
|
||||
return httpRequestMessage;
|
||||
}
|
||||
|
||||
public async Task<HttpStatusCode> PostDataAsync<T>(T data, string targetHost, string actorUrl, string inbox = null)
|
||||
{
|
||||
var httpRequestMessage = BuildRequest(data, targetHost, actorUrl, inbox);
|
||||
|
||||
var client = _httpClientFactory.CreateClient();
|
||||
client.Timeout = TimeSpan.FromSeconds(2);
|
||||
|
||||
var response = await client.SendAsync(httpRequestMessage);
|
||||
response.EnsureSuccessStatusCode();
|
||||
_logger.LogInformation("Sent tweet to " + targetHost);
|
||||
_logger.LogInformation("Tweet content is " + json);
|
||||
|
||||
return response.StatusCode;
|
||||
}
|
||||
|
|
|
@ -182,20 +182,7 @@ namespace BirdsiteLive.Domain
|
|||
|
||||
private async Task<bool> SendAcceptFollowAsync(ActivityFollow activity, string followerHost)
|
||||
{
|
||||
var acceptFollow = new ActivityAcceptFollow()
|
||||
{
|
||||
context = "https://www.w3.org/ns/activitystreams",
|
||||
id = $"{activity.apObject}#accepts/follows/{Guid.NewGuid()}",
|
||||
type = "Accept",
|
||||
actor = activity.apObject,
|
||||
apObject = new NestedActivity()
|
||||
{
|
||||
id = activity.id,
|
||||
type = activity.type,
|
||||
actor = activity.actor,
|
||||
apObject = activity.apObject
|
||||
}
|
||||
};
|
||||
var acceptFollow = _activityPubService.BuildAcceptFollow(activity);
|
||||
var result = await _activityPubService.PostDataAsync(acceptFollow, followerHost, activity.apObject);
|
||||
return result == HttpStatusCode.Accepted ||
|
||||
result == HttpStatusCode.OK; //TODO: revamp this for better error handling
|
||||
|
|
|
@ -3,31 +3,21 @@ 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\"}";
|
||||
[TestClass]
|
||||
public class ActivityTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Serialize()
|
||||
{
|
||||
var obj = new Actor
|
||||
{
|
||||
type = "Person",
|
||||
id = "id"
|
||||
};
|
||||
|
||||
// var data = JsonConvert.DeserializeObject<Activity>(json);
|
||||
var json = JsonConvert.SerializeObject(obj);
|
||||
|
||||
// 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);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using BirdsiteLive.ActivityPub.Models;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace BirdsiteLive.ActivityPub.Tests
|
||||
{
|
||||
|
@ -28,9 +28,11 @@ namespace BirdsiteLive.ActivityPub.Tests
|
|||
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("https://www.w3.org/ns/activitystreams", data.context);
|
||||
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);
|
||||
Assert.AreEqual(null, data.apObject.context);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -62,6 +64,9 @@ namespace BirdsiteLive.ActivityPub.Tests
|
|||
Assert.AreEqual("https://mastodon.technology/users/deleteduser", data.actor);
|
||||
Assert.AreEqual("https://mastodon.technology/users/deleteduser", data.apObject);
|
||||
}
|
||||
// {"object":{"object":"https://bird.makeup/users/spectatorindex","id":"https://masto.ai/b89eb86e-c902-48bc-956f-94f081617f18","type":"Follow","actor":"https://masto.ai/users/singha"},"@context":"https://www.w3.org/ns/activitystreams","id":"https://bird.makeup/users/spectatorindex#accepts/follows/27363118-e61e-4710-a41c-75dd5d54912f","type":"Accept","actor":"https://bird.makeup/users/spectatorindex"}
|
||||
// {"object":{"object":"https://bird.makeup/users/moltke","id":"https://universeodon.com/81cddd78-d7d6-4665-aa21-7bcfbea82b6b","type":"Follow","actor":"https://universeodon.com/users/amhrasmussen"},"@context":"https://www.w3.org/ns/activitystreams","id":"https://bird.makeup/users/moltke#accepts/follows/d28146be-e884-4e91-8385-19fa004f35b3","type":"Accept","actor":"https://bird.makeup/users/moltke"}
|
||||
|
||||
|
||||
//[TestMethod]
|
||||
//public void NoteDeserializationTest()
|
||||
|
|
80
src/Tests/BirdsiteLive.Domain.Tests/ActivityServiceTests.cs
Normal file
80
src/Tests/BirdsiteLive.Domain.Tests/ActivityServiceTests.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using BirdsiteLive.ActivityPub;
|
||||
using BirdsiteLive.Common.Settings;
|
||||
using BirdsiteLive.Domain.Factories;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
|
||||
|
||||
namespace BirdsiteLive.Domain.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class ActivityServiceTests
|
||||
{
|
||||
private readonly InstanceSettings _settings;
|
||||
|
||||
#region Ctor
|
||||
public ActivityServiceTests()
|
||||
{
|
||||
_settings = new InstanceSettings
|
||||
{
|
||||
Domain = "domain.name"
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
[TestMethod]
|
||||
public async Task ActivityTest()
|
||||
{
|
||||
var logger1 = new Mock<ILogger<ActivityPubService>>();
|
||||
var httpFactory = new Mock<IHttpClientFactory>();
|
||||
var keyFactory = new Mock<MagicKeyFactory>();
|
||||
var cryptoService = new CryptoService(keyFactory.Object);
|
||||
httpFactory.Setup(_ => _.CreateClient(string.Empty)).Returns(new HttpClient());
|
||||
var service = new ActivityPubService(cryptoService, _settings, httpFactory.Object, logger1.Object);
|
||||
|
||||
var activity = new ActivityAcceptFollow()
|
||||
{
|
||||
id = "awef",
|
||||
};
|
||||
var json = "{\"id\":\"awef\"}";
|
||||
#region Validations
|
||||
|
||||
var req = service.BuildRequest(activity, "google.com", "tata", "awef");
|
||||
|
||||
Assert.AreEqual(await req.Content.ReadAsStringAsync(), json);
|
||||
|
||||
#endregion
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task AcceptFollow()
|
||||
{
|
||||
|
||||
|
||||
var logger1 = new Mock<ILogger<ActivityPubService>>();
|
||||
var httpFactory = new Mock<IHttpClientFactory>();
|
||||
var keyFactory = new Mock<MagicKeyFactory>();
|
||||
var cryptoService = new CryptoService(keyFactory.Object);
|
||||
httpFactory.Setup(_ => _.CreateClient(string.Empty)).Returns(new HttpClient());
|
||||
var service = new ActivityPubService(cryptoService, _settings, httpFactory.Object, logger1.Object);
|
||||
|
||||
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 activity = ApDeserializer.ProcessActivity(json) as ActivityFollow;
|
||||
|
||||
var jsonres =
|
||||
"{\"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\"},\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://4a120ca2680e.ngrok.io/users/manu#accepts/follows/32e5fbda-9159-4ede-8249-9d008092d26f\",\"type\":\"Accept\",\"actor\":\"https://4a120ca2680e.ngrok.io/users/manu\"}";
|
||||
var activityRes = ApDeserializer.ProcessActivity(jsonres) as ActivityAcceptFollow;
|
||||
#region Validations
|
||||
|
||||
var req = service.BuildAcceptFollow(activity);
|
||||
|
||||
Assert.AreEqual(req.actor, activityRes.actor);
|
||||
Assert.AreEqual(req.context, activityRes.context);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Reference in a new issue