Skip to content

refactor: consolidate SharePoint concerns into box2.sharepoint package #59

@gillespied

Description

@gillespied

Context

The SharePoint-related code in box2 is currently spread across three packages:

  • box2.sharepoint — API clients (session, list, docs, webhooks) and schema generation
  • box2.pipeline.mappers — generic to_sharepoint_fields() / from_sharepoint_fields() serialisation
  • box2.triage.models — SharePoint list schema models (SharepointInvitation, SharepointPQs, etc.)

This makes box2.sharepoint difficult to use standalone and creates awkward cross-package dependencies — for example, pipeline.mappers imports private helpers (_unwrap_optional, _contains_url_type) from sharepoint.graph_api_schema.

Consolidating everything into box2.sharepoint would make it a self-contained SharePoint client library that could potentially be extracted into its own repo in future.

What should move to box2.sharepoint

Serialisation functions (from pipeline/mappers.py)

Symbol Reason
to_sharepoint_fields() Generic serialiser — no pipeline/triage dependency, only uses Pydantic + sharepoint helpers
from_sharepoint_fields() Generic deserialiser — same
_split_list_field() SharePoint string format helper
_extract_urls_from_html() SharePoint HTML format helper
_HREF_PATTERN Regex constant
_SP_INTERNAL_NAME_MAX SharePoint platform constant

These could live in a new box2/sharepoint/serialisation.py (or fields.py) alongside graph_api_schema.py, since they are the inverse of generate_graph_schema.

SharePoint list schema models (from triage/models/)

Model File Dependencies
SharepointInvitation + EventType invitation_sharepoint.py Pydantic only
SharepointInvitationQA invitation_qa_sharepoint.py Extends SharepointInvitation
SharepointSubmission submission_sharepoint.py Pydantic only
SharepointAction actions_sharepoint.py Pydantic only
SharepointPQs parli_question_sharepoint.py Pydantic only

None of these import anything from box2.triage. They are standalone Pydantic schema definitions that describe SharePoint list columns.

They could move to box2/sharepoint/models/ (the directory exists but is currently empty).

Private helpers to promote

_unwrap_optional() and _contains_url_type() in graph_api_schema.py are currently imported as private names by pipeline.mappers. After the move, the serialisation code lives in the same package as these helpers, so the cross-package private import disappears.

What stays in pipeline/mappers.py

The domain-specific mapper functions that bridge triage/pipeline models to SharePoint models:

Function Why it stays
to_sharepoint_invitation_qa() Maps TriagedInvitation (pipeline) → SharepointInvitationQA
to_sharepoint_invitation() Maps SharepointInvitationQASharepointInvitation
to_sharepoint_submission() Maps Submission (triage) → SharepointSubmission
to_sharepoint_action() Maps Action + ActionReviewResultSharepointAction

These genuinely need both packages and are orchestration logic.

What stays in triage/models/

The domain models that describe triage concepts (not SharePoint schemas):

Invitation, NotInvitation, Submission, NotSubmission, Action, TriagedDecision, SafeDocument, RawDocument, MinisterPersona, CalendarEvent, etc.

Backwards compatibility

Re-export moved symbols from their original locations so existing imports continue to work:

  • box2.pipeline.mappers re-exports to_sharepoint_fields, from_sharepoint_fields
  • box2.pipeline.__init__ re-exports same
  • box2.triage.models.__init__ re-exports all Sharepoint* models

After the move: ListClient read-with-model methods

Once the serialisation functions live in box2.sharepoint, ListClient can offer typed read methods without cross-package imports:

model = client.get_item_as(item_id, SharepointInvitation)
models = client.get_items_as(SharepointInvitation)

These would use from_sharepoint_fields internally.

Import chain after the move

box2.sharepoint (self-contained)
  ├── session, list_client, docs_client, webhook_client
  ├── serialisation (to/from_sharepoint_fields)
  ├── graph_api_schema (generate_graph_schema)
  └── models (SharepointInvitation, SharepointPQs, etc.)

box2.pipeline.mappers (domain bridging only)
  → box2.sharepoint.serialisation
  → box2.sharepoint.models
  → box2.triage.models (domain models: Action, Submission, etc.)

box2.receiver
  → box2.pipeline
  → box2.sharepoint

No more private cross-package imports. box2.sharepoint has zero dependencies on pipeline or triage.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions