Package: activitypubdantic
ActivityPubdanticValidate and Interact with ActivityPub JSONGitHub RepositoryActivityPubdantic DocumentationActivityPub ProtocolActivityStreams SpecificationWhat Is ActivityPubdantic?ActivityPubdanticis a suite of tools for validating ActivityPub JSON and constructing consistent representations of ActivityPub notifications and content.Pydanticmodels enable the validation logic and can be imported for use in custom-coded classes or FastAPI routes.Why Does ActivityPub JSON Require Validation?ActivityPubis a protocol for decentralized social networking. It defines client-to-server and server-to-server interactions and relies onActivityStreamsfor its vocabulary. Many of the protocol's specifications are purposefully unrestrictive, giving developers the freedom to implement only the features relevant to their products or to adjust to meet their particular requirements.However, that flexibility presents challenges for assessing data validity and simplifying developers' code.ActivityPubdantichelps developers overcome those challenges by using ActivityPub'stypefield to identify proper checks for other fields and standardize their structures.Examplesare available in the sections below.Mastodonsupports ActivityPub, and Meta'sThreadsapp plans to conform to the protocol sometime in thenear future.ActivityPubdanticincludes apytestscript, which uses examples from ActivityPub, ActivityStreams, and Mastodon to test its parsing and validation. As Threads and other platforms implement ActivityPub, those tests (and more broadly, this package) will be updated to stay current.InstallationInstallActivityPubdanticwithpip:pip install activitypubdanticMost developer use cases will require one or both of the following import statements, which serve different purposes:# Use classes for validation and common operationsimportactivitypubdanticasap# Use models in FastAPI routesfromactivitypubdantic.modelsimport*ExamplesThe following examples include simple use cases and code snippets forActivityPubdantic. For a more thorough listing ofActivityPubdantic's classes, functions, and models, check out itsdocumentation.Parsing Activity, Collection, Link, and Object JSONActivities,Collections,Links, andObjectsare the core concepts around which ActivityPub and ActivityStreams are built. By reducing their complexity and standardizing their representation,ActivityPubdantichelps resolve potential pain points for developers.ActivityPub's protocol includes anexampleof aLikeactivity. The example'stofield is a list, while itsccfield is a string. Both formats are valid, but they require slightly different handling in subsequent lines of code. To resolve that difference,ActivityPubdanticcopies and rewrites the JSON, so those fields are always represented as lists of dictionaries.importactivitypubdanticasap# Example JSON from ActivityPub documentationexample_json={"@context":["https://www.w3.org/ns/activitystreams",{"@language":"en"}],"type":"Like","actor":"https://dustycloud.org/chris/","name":"Chris liked 'Minimal ActivityPub update client'","object":"https://rhiaro.co.uk/2016/05/minimal-activitypub","to":["https://rhiaro.co.uk/#amy","https://dustycloud.org/followers","https://rhiaro.co.uk/followers/"],"cc":"https://e14n.com/evan"}# Get the appropriate class, which is determined by the type fieldoutput_class=ap.get_class(example_json)# Produce the parsed and validated JSON stringoutput_json=output_class.json()print(output_json)# See JSON belowget_class()reads theexample_jsonand uses its type to select the applicable Pydantic model. That model then uses validators for each field to assert they comply with the protocol and then restructures them.Theoutput_jsonis longer and, at first glance, more difficult to read. But because it contains types for each item in its fields and it standardizes the structures of similar fields – liketoandcc– it is more descriptive and easier to consistently manipulate.{"@context":["https://www.w3.org/ns/activitystreams",{"@language":"en"}],"type":"Like","name":"Chris liked 'Minimal ActivityPub update client'","to":[{"@context":"https://www.w3.org/ns/activitystreams","type":"Object","id":"https://rhiaro.co.uk/#amy"},{"@context":"https://www.w3.org/ns/activitystreams","type":"Object","id":"https://dustycloud.org/followers"},{"@context":"https://www.w3.org/ns/activitystreams","type":"Object","id":"https://rhiaro.co.uk/followers/"}],"cc":[{"@context":"https://www.w3.org/ns/activitystreams","type":"Object","id":"https://e14n.com/evan"}],"actor":[{"@context":"https://www.w3.org/ns/activitystreams","type":"Object","id":"https://dustycloud.org/chris/"}],"object":{"@context":"https://www.w3.org/ns/activitystreams","type":"Object","id":"https://rhiaro.co.uk/2016/05/minimal-activitypub"}}However, not every project requires that degree of granularity. For example, some servers may already have logic that ignores additional fields and only iterates throughidURLs in the JSON.# Use the verbose keyword argumentshort_output_json=output_class.json(verbose=False)print(short_output_json)# See JSON belowSettingverbose=Falseshortens the output, retaining consistency but eliminating unneeded data for more concise tasks.{"@context":["https://www.w3.org/ns/activitystreams",{"@language":"en"}],"type":"Like","name":"Chris liked 'Minimal ActivityPub update client'","to":["https://rhiaro.co.uk/#amy","https://dustycloud.org/followers","https://rhiaro.co.uk/followers/"],"cc":["https://e14n.com/evan"],"actor":["https://dustycloud.org/chris/"],"object":"https://rhiaro.co.uk/2016/05/minimal-activitypub"}Validating FastAPI Request BodiesFastAPIuses Pydantic models to validaterequest bodies. After importingActivityPubdanticmodels directly, developers can automatically validate requests and then use theget_class_from_model()function to smoothly interact with ActivityPub JSON.When the sameLikeactivity is sent in the POST request to/outbox, the request body is validated by FastAPI and loaded into anActivityPubdanticclass to produce clean JSON.importactivitypubdanticasapfromactivitypubdantic.modelsimport*fromfastapiimportFastAPI,Responseapp=FastAPI()# Route for an ActivityPub outbox@app.post("/outbox",status_code=201)asyncdefoutbox(activity:ActivityModel,response:Response):# Initialize the class and perform relevant data manipulationsactivity_class=ap.get_class_from_model(activity)activity_class.make_public()# Save the JSON in the outbox in the databaseprint(activity_class.json())# Use the type to set the headerresponse.headers["Location"]="https://example.com/{0}/{1}".format(activity_class.type.lower(),1,# ID should come from the database)# Return with header and status codereturnMethods – likemake_public()– perform common operations on the data. In this case,make_public()removes thebtoandbccattributes from the class instance, if they exist. Additionally, thetypeattribute specifies a location in the response header, per the ActivityPubdocumentationfor client-to-server interactions.ContributingActivityPubdanticis still a work in progress. If you find it valuable for your project but notice bugs, need changes, or require additional features or support for other ActivityPub platforms,open an issueor fork tostart a PR.Thedeveloper_requirements.txtfile includes all of the packages your virtual environment needs, includingpdoc3for generating new documentation,blackfor formatting, andpytestfor unit tests.Keep in mind, all PRs require a successful run of the GitHub Workflow for testing, so if you significantly changeActivityPubdantic's structure, be sure to add, alter, or remove relevant tests.Thank you for your interest!