AP: move types to their own directory

This commit is contained in:
2024-02-23 13:33:37 -03:00
parent b27e6cb2ec
commit 6f7e223b3d
6 changed files with 165 additions and 141 deletions

74
source/ap/types/actor.d Normal file
View File

@@ -0,0 +1,74 @@
module ap.types.actor;
import std.json;
import ap.types.object;
import util;
/++
* Represents an ActivityPub Actor
* https://www.w3.org/TR/activitypub/#actors
+/
class Actor : ASObject {
/* Required fields */
string inbox; /// Link to messages received by this actor. Required
string outbox; /// Link to messages produced by this actor. Required
string following; /// Link to a collection of the actors that this actor is following. Required
string followers; /// Link to a collection of the actors that follow this actor. Required
/* Optional fields */
string liked; /// Link to a collection of objects this actor has liked
string streams; /// Supplementary list of Collections of interest
string preferredUsername; /// A short username for referencing this actor
ActorEndpoints endpoints; /// Useful endpoints for this actor
JSONValue raw;
/++
+ Creates an Actor object based on its json representation
+ Params:
+ json = ActivityPub actor JSON representation
+/
this(JSONValue json) {
super(json);
with (this) {
required(json, "inbox", inbox);
required(json, "outbox", outbox);
required(json, "following", following);
required(json, "followers", followers);
optional(json, "liked", liked);
optional(json, "streams", streams);
optional(json, "preferredUsername", preferredUsername);
endpoints = ActorEndpoints.fromJson(json.optional!JSONValue("endpoints"));
raw = json;
}
}
}
struct ActorEndpoints {
string proxyUrl;
string oauthAuthorizationEndpoint;
string oauthTokenEndpoint;
string provideClientKey;
string signClientKey;
string sharedInbox;
static ActorEndpoints fromJson(JSONValue json) {
ActorEndpoints endpoints = ActorEndpoints();
with (endpoints) {
optional(json, "proxyUrl", proxyUrl);
optional(json, "oauthAuthorizationEndpoint", oauthAuthorizationEndpoint);
optional(json, "oauthTokenEndpoint", oauthTokenEndpoint);
optional(json, "provideClientKey", provideClientKey);
optional(json, "signClientKey", signClientKey);
optional(json, "sharedInbox", sharedInbox);
}
return endpoints;
}
}

View File

@@ -0,0 +1,28 @@
module ap.types.collection;
import std.json;
import ap.types.object;
import ap.types.collection_page;
import util;
class Collection : ASObject {
int totalItems;
CollectionPage current;
CollectionPage first;
CollectionPage last;
ASObject items;
this(JSONValue json) {
super(json);
if (this.m_objType != ObjectType.Object)
throw new Exception("Wrong Collection format?");
optional(json, "totalItems", totalItems);
current = new CollectionPage(json.optional!JSONValue("current"));
first = new CollectionPage(json.optional!JSONValue("first"));
last = new CollectionPage(json.optional!JSONValue("last"));
items = new ASObject(json.optional!JSONValue("items"));
}
}

View File

@@ -0,0 +1,24 @@
module ap.types.collection_page;
import std.json;
import ap.types.collection;
import ap.types.object;
import util;
class CollectionPage : Collection {
CollectionPage partOf;
CollectionPage next;
CollectionPage prev;
this(JSONValue json) {
super(json);
if (this.m_objType != ObjectType.Object)
throw new Exception("Wrong CollectionPage format?");
partOf = new CollectionPage(json.optional!JSONValue("partOf"));
next = new CollectionPage(json.optional!JSONValue("next"));
prev = new CollectionPage(json.optional!JSONValue("prev"));
}
}

105
source/ap/types/link.d Normal file
View File

@@ -0,0 +1,105 @@
module ap.types.link;
import std.algorithm;
import std.array;
import std.format;
import std.json;
import ap.types.object;
import util;
class Link {
string type;
string href;
string[] rel;
string mediaType;
string name;
int height, width;
ASObject preview;
protected Store m_store;
protected ObjectType m_objType;
JSONValue raw;
this() {
}
this(JSONValue json) {
switch (json.type) {
case JSONType.ARRAY:
m_objType = ObjectType.Array;
m_store.links = json.array.map!(item => new Link(item)).array;
break;
case JSONType.OBJECT:
m_objType = ObjectType.Object;
optional(json, "type", type);
optional(json, "href", href);
optional(json, "mediaType", mediaType);
optional(json, "name", name);
optional(json, "height", height);
optional(json, "width", width);
const(JSONValue)* j;
if ((j = "rel" in json) != null)
rel = (*j).array.map!(item => item.str).array;
preview = new ASObject(json.optional!JSONValue("preview"));
break;
case JSONType.STRING:
m_objType = ObjectType.String;
m_store.str = json.str;
break;
case JSONType.NULL:
break;
default:
throw new Exception("Unrecognized ActivityStream Link JSON format");
}
this.raw = json;
}
string stringRep(int indentation = 0, string indentStr = " ") const {
string[] output;
string indent = "";
string j;
if (this.m_objType == ObjectType.String)
return format("%s%s", indent, this.m_store.str);
for (int i = 0; i < indentation; i++)
indent ~= indentStr;
output ~= format("%s {", this.type);
if (href)
output ~= format("%s%shref = %s", indent, indentStr, href);
if (mediaType)
output ~= format("%s%smediaType = %s", indent, indentStr, mediaType);
if (name)
output ~= format("%s%sname = %s", indent, indentStr, name);
if (height)
output ~= format("%s%sheight = %d", indent, indentStr, height);
if (width)
output ~= format("%s%swidth = %d", indent, indentStr, width);
if (preview && (j = preview.stringRep(indentation + 1)) != string.init)
output ~= format("%s%spreview = %s", indent, indentStr, j);
output ~= indent ~ "}";
if (output.length == 2)
return "";
return output.join("\n");
}
}

201
source/ap/types/object.d Normal file
View File

@@ -0,0 +1,201 @@
module ap.types.object;
import std.algorithm;
import std.array;
import std.datetime;
import std.format;
import std.json;
import ap.types.link;
import ap.types.collection;
import util;
union Store {
string str;
ASObject[] array;
Link[] links;
}
enum ObjectType {
Object,
String,
Array,
}
/++
* Basic ActivityStream Object
* https://www.w3.org/TR/activitypub/#obj
+/
class ASObject {
// Required fields (for root objects)
JSONValue context; /// Must be activitystream context
string id; /// AP requirement for unique identifier
string type; /// AP requirement for type of object
// Optional fields
ASObject attachment;
ASObject attributedTo;
ASObject audience;
string content;
string name;
DateTime endTime;
ASObject generator;
ASObject icon;
ASObject image;
ASObject inReplyTo;
ASObject location;
ASObject preview;
DateTime published;
Collection replies;
DateTime startTime;
string summary;
ASObject tag;
DateTime updated;
Link url;
ASObject to;
ASObject bto;
ASObject cc;
ASObject bcc;
string mediaType;
string duration;
JSONValue raw;
protected Store m_store;
protected ObjectType m_objType;
this() {
}
/++
+ Constructs the object according to json source
+ Params:
+ json = the source json
+ Returns: Object
+/
this(JSONValue json) {
switch (json.type) {
case JSONType.ARRAY:
this.m_objType = ObjectType.Array;
this.m_store.array = json.array.map!(item => new ASObject(item)).array;
break;
case JSONType.OBJECT:
this.m_objType = ObjectType.Object;
// String properties
optional(json, "context", context);
optional(json, "id", id);
optional(json, "type", type);
optional(json, "content", content);
optional(json, "name", name);
optional(json, "summary", summary);
optional(json, "mediaType", mediaType);
optional(json, "duration", duration);
// AS Object properties
generator = new ASObject(json.optional!JSONValue("generator"));
icon = new ASObject(json.optional!JSONValue("icon"));
image = new ASObject(json.optional!JSONValue("image"));
inReplyTo = new ASObject(json.optional!JSONValue("inReplyTo"));
location = new ASObject(json.optional!JSONValue("location"));
preview = new ASObject(json.optional!JSONValue("preview"));
tag = new ASObject(json.optional!JSONValue("tag"));
to = new ASObject(json.optional!JSONValue("to"));
bto = new ASObject(json.optional!JSONValue("bto"));
cc = new ASObject(json.optional!JSONValue("cc"));
bcc = new ASObject(json.optional!JSONValue("bcc"));
const(JSONValue)* j;
// Date properties
// if ((j = "endTime" in json) != null)
// endTime = DateTime.fromISOString(json["endTime"].str);
// if ((j = "published" in json) != null)
// published = DateTime.fromISOString(json["published"].str);
// if ((j = "startTime" in json) != null)
// startTime = DateTime.fromISOString(json["startTime"].str);
// if ((j = "updated" in json) != null)
// updated = DateTime.fromISOString(json["updated"].str);
url = new Link(json.optional!JSONValue("url"));
break;
case JSONType.STRING:
this.m_objType = ObjectType.String;
this.m_store.str = json.str;
break;
case JSONType.NULL:
break;
default:
throw new Exception("Unrecognized ActivityStream Object JSON format: " ~ json.type);
}
raw = json;
}
string stringRep(int indentation = 0, string indentStr = " ") const {
string[] output;
string indent = "";
string j;
if (this.m_objType == ObjectType.String)
return format("%s%s\n", indent, this.m_store.str);
if (this.m_objType == ObjectType.Array) {
output ~= "[";
foreach (obj; this.m_store.array)
output ~= format("%s%s%s%s,", indent, indentStr, indentStr, obj.stringRep(indentation + 1));
output ~= format("%s%s]", indent, indentStr);
return output.length == 2 ? "" : output.join("\n");
}
for (int i = 0; i < indentation; i++)
indent ~= indentStr;
output ~= format("%s {", this.type);
if (id)
output ~= format("%s%sid = %s", indent, indentStr, id);
if (content)
output ~= format("%s%scontent = %s", indent, indentStr, content);
if (name)
output ~= format("%s%sname = %s", indent, indentStr, name);
// if (summary)
// output ~= format("%s%ssummary = %s", indent, indentStr, summary);
if (mediaType)
output ~= format("%s%smediaType = %s", indent, indentStr, mediaType);
if (duration)
output ~= format("%s%sduration = %s", indent, indentStr, duration);
if (url && (j = url.stringRep(indentation + 1)) != string.init)
output ~= format("%s%surl = %s", indent, indentStr, j);
if (icon && (j = icon.stringRep(indentation + 1)) != string.init)
output ~= format("%s%sicon = %s", indent, indentStr, j);
if (image && (j = image.stringRep(indentation + 1)) != string.init)
output ~= format("%s%simage = %s", indent, indentStr, j);
if (tag && (j = tag.stringRep(indentation + 1)) != string.init)
output ~= format("%s%stag = %s", indent, indentStr, j);
output ~= indent ~ "}";
return output.length == 2 ? "" : output.join("\n");
}
}