{"id":15058824,"url":"https://github.com/joewlos/activitypubdantic","last_synced_at":"2025-08-08T05:45:36.404Z","repository":{"id":180462997,"uuid":"665181504","full_name":"joewlos/activitypubdantic","owner":"joewlos","description":"Pydantic Models for ActivityPub with Classes for Enabling Interactions","archived":false,"fork":false,"pushed_at":"2023-07-17T17:06:16.000Z","size":367,"stargazers_count":5,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-24T11:56:54.918Z","etag":null,"topics":["activitypub","fastapi","json-schema","parsing","pydantic","python","python3"],"latest_commit_sha":null,"homepage":"http://www.joewlos.com/activitypubdantic/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joewlos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-11T16:04:06.000Z","updated_at":"2025-02-22T04:52:42.000Z","dependencies_parsed_at":null,"dependency_job_id":"54f7a466-8243-4b2b-94d3-dc4075a0f269","html_url":"https://github.com/joewlos/activitypubdantic","commit_stats":null,"previous_names":["joewlos/activitypubdantic"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joewlos%2Factivitypubdantic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joewlos%2Factivitypubdantic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joewlos%2Factivitypubdantic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joewlos%2Factivitypubdantic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joewlos","download_url":"https://codeload.github.com/joewlos/activitypubdantic/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248225653,"owners_count":21068078,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activitypub","fastapi","json-schema","parsing","pydantic","python","python3"],"created_at":"2024-09-24T22:31:05.841Z","updated_at":"2025-04-10T13:13:22.071Z","avatar_url":"https://github.com/joewlos.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ActivityPubdantic\n\n![CI](https://github.com/joewlos/activitypubdantic/actions/workflows/test.yaml/badge.svg?branch=main)\n[![PyPI version](https://badge.fury.io/py/activitypubdantic.svg)](https://badge.fury.io/py/activitypubdantic)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Pydantic v2](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/pydantic/pydantic/5697b1e4c4a9790ece607654e6c02a160620c7e1/docs/badge/v2.json)](https://pydantic.dev)\n\n### Validate and Interact with ActivityPub JSON\n\n[GitHub Repository](https://github.com/joewlos/activitypubdantic)\n\n[ActivityPubdantic Documentation](https://www.joewlos.com/activitypubdantic/)\n\n[ActivityPub Protocol](https://www.w3.org/TR/activitypub/)\n\n[ActivityStreams Specification](https://www.w3.org/TR/activitystreams-vocabulary/)\n\n## What Is ActivityPubdantic?\n\n**ActivityPubdantic** is a suite of tools for validating ActivityPub JSON and constructing consistent representations of ActivityPub notifications and content. [Pydantic](https://docs.pydantic.dev/latest/) models enable the validation logic and can be imported for use in custom-coded classes or FastAPI routes.\n\n## Why Does ActivityPub JSON Require Validation?\n\n[ActivityPub](https://www.w3.org/TR/activitypub/) is a protocol for decentralized social networking. It defines client-to-server and server-to-server interactions and relies on [ActivityStreams](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-orderedcollection) for 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.\n\nHowever, that flexibility presents challenges for assessing data validity and simplifying developers' code. **ActivityPubdantic** helps developers overcome those challenges by using ActivityPub's `type` field to identify proper checks for other fields and standardize their structures. [Examples](https://github.com/joewlos/activitypubdantic/tree/main#examples) are available in the sections below.\n\n[Mastodon](https://docs.joinmastodon.org/spec/activitypub/) supports ActivityPub, and Meta's [Threads](https://apps.apple.com/us/app/threads-an-instagram-app/id6446901002) app plans to conform to the protocol sometime in the [near future](https://techcrunch.com/2023/07/05/adam-mosseri-says-metas-threads-app-wont-have-activitypub-support-at-launch/). **ActivityPubdantic** includes a `pytest` script, 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.\n\n## Installation\n\nInstall **ActivityPubdantic** with `pip`:\n\n```console\npip install activitypubdantic\n```\n\nMost developer use cases will require one or both of the following import statements, which serve different purposes:\n\n```python\n# Use classes for validation and common operations\nimport activitypubdantic as ap\n\n# Use models in FastAPI routes\nfrom activitypubdantic.models import *\n```\n\n## Examples\n\nThe following examples include simple use cases and code snippets for **ActivityPubdantic**. For a more thorough listing of **ActivityPubdantic**'s classes, functions, and models, check out its [documentation](https://www.joewlos.com/activitypubdantic/).\n\n### Parsing Activity, Collection, Link, and Object JSON\n\n`Activities`, `Collections`, `Links`, and `Objects` are the core concepts around which ActivityPub and ActivityStreams are built. By reducing their complexity and standardizing their representation, **ActivityPubdantic** helps resolve potential pain points for developers.\n\nActivityPub's protocol includes an [example](https://www.w3.org/TR/activitypub/#client-to-server-interactions) of a `Like` activity. The example's `to` field is a list, while its `cc` field is a string. Both formats are valid, but they require slightly different handling in subsequent lines of code. To resolve that difference, **ActivityPubdantic** copies and rewrites the JSON, so those fields are always represented as lists of dictionaries.\n\n```python\nimport activitypubdantic as ap\n\n# Example JSON from ActivityPub documentation\nexample_json = {\n  \"@context\": [\"https://www.w3.org/ns/activitystreams\",\n               {\"@language\": \"en\"}],\n  \"type\": \"Like\",\n  \"actor\": \"https://dustycloud.org/chris/\",\n  \"name\": \"Chris liked 'Minimal ActivityPub update client'\",\n  \"object\": \"https://rhiaro.co.uk/2016/05/minimal-activitypub\",\n  \"to\": [\"https://rhiaro.co.uk/#amy\",\n         \"https://dustycloud.org/followers\",\n         \"https://rhiaro.co.uk/followers/\"],\n  \"cc\": \"https://e14n.com/evan\"\n}\n\n# Get the appropriate class, which is determined by the type field\noutput_class = ap.get_class(example_json)\n\n# Produce the parsed and validated JSON string\noutput_json = output_class.json()\nprint(output_json)  # See JSON below\n```\n\n`get_class()` reads the `example_json` and 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.\n\nThe `output_json` is 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 – like `to` and `cc` – it is more descriptive and easier to consistently manipulate.\n\n```json\n{\n  \"@context\": [\n    \"https://www.w3.org/ns/activitystreams\",\n    {\n      \"@language\": \"en\"\n    }\n  ],\n  \"type\": \"Like\",\n  \"name\": \"Chris liked 'Minimal ActivityPub update client'\",\n  \"to\": [\n    {\n      \"@context\": \"https://www.w3.org/ns/activitystreams\",\n      \"type\": \"Object\",\n      \"id\": \"https://rhiaro.co.uk/#amy\"\n    },\n    {\n      \"@context\": \"https://www.w3.org/ns/activitystreams\",\n      \"type\": \"Object\",\n      \"id\": \"https://dustycloud.org/followers\"\n    },\n    {\n      \"@context\": \"https://www.w3.org/ns/activitystreams\",\n      \"type\": \"Object\",\n      \"id\": \"https://rhiaro.co.uk/followers/\"\n    }\n  ],\n  \"cc\": [\n    {\n      \"@context\": \"https://www.w3.org/ns/activitystreams\",\n      \"type\": \"Object\",\n      \"id\": \"https://e14n.com/evan\"\n    }\n  ],\n  \"actor\": [\n    {\n      \"@context\": \"https://www.w3.org/ns/activitystreams\",\n      \"type\": \"Object\",\n      \"id\": \"https://dustycloud.org/chris/\"\n    }\n  ],\n  \"object\": {\n    \"@context\": \"https://www.w3.org/ns/activitystreams\",\n    \"type\": \"Object\",\n    \"id\": \"https://rhiaro.co.uk/2016/05/minimal-activitypub\"\n  }\n}\n```\n\nHowever, not every project requires that degree of granularity. For example, some servers may already have logic that ignores additional fields and only iterates through `id` URLs in the JSON.\n\n```python\n# Use the verbose keyword argument\nshort_output_json = output_class.json(verbose=False)\nprint(short_output_json)  # See JSON below\n```\n\nSetting `verbose=False` shortens the output, retaining consistency but eliminating unneeded data for more concise tasks.\n\n```json\n{\n  \"@context\": [\n    \"https://www.w3.org/ns/activitystreams\",\n    {\n      \"@language\": \"en\"\n    }\n  ],\n  \"type\": \"Like\",\n  \"name\": \"Chris liked 'Minimal ActivityPub update client'\",\n  \"to\": [\n    \"https://rhiaro.co.uk/#amy\",\n    \"https://dustycloud.org/followers\",\n    \"https://rhiaro.co.uk/followers/\"\n  ],\n  \"cc\": [\"https://e14n.com/evan\"],\n  \"actor\": [\"https://dustycloud.org/chris/\"],\n  \"object\": \"https://rhiaro.co.uk/2016/05/minimal-activitypub\"\n}\n```\n\n### Validating FastAPI Request Bodies\n\n[FastAPI](https://fastapi.tiangolo.com/) uses Pydantic models to validate [request bodies](https://fastapi.tiangolo.com/tutorial/body/). After importing **ActivityPubdantic** models directly, developers can automatically validate requests and then use the `get_class_from_model()` function to smoothly interact with ActivityPub JSON.\n\nWhen the same `Like` activity is sent in the POST request to `/outbox`, the request body is validated by FastAPI and loaded into an **ActivityPubdantic** class to produce clean JSON.\n\n```python\nimport activitypubdantic as ap\nfrom activitypubdantic.models import *\nfrom fastapi import FastAPI, Response\n\napp = FastAPI()\n\n# Route for an ActivityPub outbox\n@app.post(\"/outbox\", status_code=201)\nasync def outbox(activity: ActivityModel, response: Response):\n\n    # Initialize the class and perform relevant data manipulations\n    activity_class = ap.get_class_from_model(activity)\n    activity_class.make_public()\n\n    # Save the JSON in the outbox in the database\n    print(activity_class.json())\n\n    # Use the type to set the header\n    response.headers[\"Location\"] = \"https://example.com/{0}/{1}\".format(\n        activity_class.type.lower(),\n        1,  # ID should come from the database\n    )\n\n    # Return with header and status code\n    return\n```\n\nMethods – like `make_public()` – perform common operations on the data. In this case, `make_public()` removes the `bto` and `bcc` attributes from the class instance, if they exist. Additionally, the `type` attribute specifies a location in the response header, per the ActivityPub [documentation](https://www.w3.org/TR/activitypub/#client-to-server-interactions) for client-to-server interactions.\n\n## Contributing\n\n**ActivityPubdantic** is 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 issue](https://github.com/joewlos/activitypubdantic/issues) or fork to [start a PR](https://github.com/joewlos/activitypubdantic/pulls).\n\nThe `developer_requirements.txt` file includes all of the packages your virtual environment needs, including `pdoc3` for generating new documentation, `black` for formatting, and `pytest` for unit tests.\n\nKeep in mind, all PRs require a successful run of the GitHub Workflow for testing, so if you significantly change **ActivityPubdantic**'s structure, be sure to add, alter, or remove relevant tests.\n\nThank you for your interest!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoewlos%2Factivitypubdantic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoewlos%2Factivitypubdantic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoewlos%2Factivitypubdantic/lists"}