GraphBridge is a lightweight Microsoft Graph client that uses app-only (Azure AD) authentication and streamlines SharePoint site/list operations: metadata retrieval, feature-based queries, CRUD, upsert, and field key encoding/decoding.
Project description
GbSharePoint (GbAuth · GbSite · GbList)
A small Python wrapper to work with Microsoft Graph and SharePoint Lists using app-only authentication (Client Credentials).
The library includes the classes
GbAuth,GbSite,GbListand a couple of utilities (deduplicate_dicts, field name encoding/decoding).
Requirements
-
Python ≥ 3.10
-
An app registered in Microsoft Entra ID (Azure AD) with Application Permissions to Microsoft Graph:
- Read:
Sites.Read.All(minimum) - Write (CRUD):
Sites.ReadWrite.All - Alternatively (recommended for least privilege): Sites.Selected + grant site-level access.
- Read:
-
Admin consent granted for the app permissions.
Installation
pip install azure-identity requests
Key concepts
- GbAuth: handles credentials and acquires the Graph bearer token (
ClientSecretCredential). - GbSite: resolves a SharePoint site id from
hostnameandsite_path. - GbList: performs list operations (
list_items,list_rows,create,update,delete,upload, …).
All requests target the v1.0 Graph endpoint (https://graph.microsoft.com/v1.0).
Quickstart
from gbsharepoint import GbAuth, GbSite, GbList # or import from your own module file
# 1) App-only auth
auth = GbAuth(
tenant_id="00000000-0000-0000-0000-000000000000",
client_id="11111111-1111-1111-1111-111111111111",
client_secret="YOUR_CLIENT_SECRET"
)
# 2) SharePoint site (Graph uses hostname + site path)
site = GbSite(
hostname="contoso.sharepoint.com",
site_path="/sites/Marketing", # include the leading slash
gb_auth=auth
)
print("Site ID:", site.site_id)
# 3) SharePoint list
sp_list = GbList(
list_name="Campaigns 2025", # display name of the list
gb_site=site
)
# 4) Read
print("Fields:", sp_list.list_fields) # e.g. ['Title', 'Status', 'Owner', ...]
rows = sp_list.list_rows # list of dicts (the item's 'fields' object)
for r in rows:
print(r["Title"], r.get("Status"))
Usage examples
Read items
rows = sp_list.list_rows
print(len(rows), "rows")
print(rows[0]) # {'Title': 'Campaign A', 'Status': 'Active', ...}
list_fieldsreturns column internal names inferred from the first row. If the list is empty, it returns[].
Create items
new_item = {
"Title": "New Campaign",
"Status": "Active",
"Budget": 5000
}
result = sp_list.create(new_item)
# {'successes': [{'id': '123', 'success': True, 'item': {...}}], 'failures': []}
print(result["successes"])
create() accepts either a single dict or a list of dicts. Each dict is wrapped as {"fields": row} as required by Graph.
Update items (PATCH)
item_id = sp_list.list_ids[0] # Graph item id (string)
patch = {"Status": "Closed"}
result = sp_list.update(ids=item_id, rows=patch)
# For batch updates: ids=[...], rows=[{...}, {...}]
print(result)
Performs a PATCH to /lists/{list_id}/items/{item_id}/fields with the passed rows dict.
Delete items
to_delete = sp_list.list_ids[:2]
result = sp_list.delete(to_delete)
print("Deleted:", [s["id"] for s in result["successes"]])
Upsert / sync with upload()
# Synchronize the list with the provided rows:
# - if an id exists: update it (or fully replace if force=True)
# - if an id does NOT exist: create a new item
# - if delete=True: remove existing items not present in 'ids'
ids = ["10", "42"] # Graph item ids (strings)
rows = [
{"Title": "Row 10", "Status": "Active"},
{"Title": "Row 42", "Status": "Closed"}
]
report = sp_list.upload(ids=ids, rows=rows, force=False, delete=False)
print(report)
Important notes on upload()
- IDs must be Graph item ids (those from
sp_list.list_ids). - For new items (id not found), the function creates an item and reports
new_idin the result. force=Truemeans delete + create (hard replace).delete=Trueremoves items not included inids.
Filter items with get_items_by_features()
This method evaluates predicates against sp_list.list_items (the full Graph item object that includes the fields sub-object).
To filter on list columns, specify predicates nested under fields:
features = [
{"fields": {"Status": "Active"}}, # AND within the same dict
{"fields": {"Owner": "mario.rossi@contoso.com"}}
]
matched_items = sp_list.get_items_by_features(features)
# Returns a de-duplicated list of Graph items (with 'id', 'fields', etc.)
- The list of dicts in
featuresis combined in OR. - Key/value pairs inside a single dict are combined in AND.
- One level of nesting is supported (e.g.,
{"fields": {"Category": {"Name": "Premium"}}}if yourfieldscontains nested objects).
Field name encoding/decoding
SharePoint internal names may contain sequences like _x0020_ for spaces.
GbList exposes:
encode_row(row: dict) -> dict: replaces special characters in key names (' '→_x0020_, etc.)decode_row(row: dict) -> dict: reverse operation.
Example:
human = {"Customer name": "ACME", "Close date": "2025-08-01"}
encoded = sp_list.encode_row(human) # {'Customer_x0020_name': 'ACME', ...}
created = sp_list.create(encoded)
If you already use the correct internal names, you don’t need to encode.
API reference (main properties & methods)
GbAuth
token→ Graph bearer token (cached).headers→{'Authorization': 'Bearer <token>'}
Changing
tenant_id,client_id, orclient_secretclears cached auth/token.
GbSite
site_url→https://graph.microsoft.com/v1.0/sites/{hostname}:{site_path}site_data→ site JSON (cached)site_id→site_data["id"]
GbList
list_url→https://graph.microsoft.com/v1.0/sites/{site_id}/lists/{quote(list_name)}list_data→ list metadata (cached)list_id→list_data["id"]list_items→GET {list_url}/items?expand=fields(list of Graph items)list_rows→[item["fields"] for item in list_items]list_ids→[item["id"] for item in list_items]list_fields→ column names from the first row (or[])
CRUD:
create(rows)→ POST/items(accepts dict or list of dicts; wraps as{"fields": ...}).update(ids, rows)→ PATCH/items/{id}/fieldsdelete(ids)→ DELETE/items/{id}upload(ids, rows, force=False, delete=False)→ upsert + optional cleanup.
Utilities:
get_items_by_features(features)→ OR across groups of predicates (AND within each group).encode_row(row),decode_row(row)deduplicate_dicts(list_of_dicts)(free function)
Best practices & limitations
- Pagination:
list_itemsdoes not follow@odata.nextLink. For very large lists, implement paging if needed. - Field names: prefer internal names. If you rely on display names/spaces/special chars, use
encode_row. - Error handling: the code raises
ValueError,TypeError,RuntimeErrorwith Graph response details when available. - Rate limiting: Graph may return 429/503. Add retries/backoff for large batch operations.
- Security: never commit
client_secret. Use environment variables.
Example with environment variables:
import os
auth = GbAuth(
tenant_id=os.environ["AZURE_TENANT_ID"],
client_id=os.environ["AZURE_CLIENT_ID"],
client_secret=os.environ["AZURE_CLIENT_SECRET"]
)
End-to-end (quick CRUD)
# CREATE
created = sp_list.create([
{"Title": "Task A", "Status": "Active"},
{"Title": "Task B", "Status": "Active"},
])
new_ids = [s["id"] for s in created["successes"]]
# READ
print(sp_list.list_rows)
# UPDATE
sp_list.update(ids=new_ids[0], rows={"Status": "Closed"})
# DELETE
sp_list.delete(new_ids[1])
Project layout (minimal)
If you don’t publish a package, paste the classes into gbsharepoint.py and:
# file: gbsharepoint.py
# (paste the provided classes/utilities here)
# then in your script:
from gbsharepoint import GbAuth, GbSite, GbList
License
Add the license that applies to your repository (e.g., MIT).
If you want, I can tailor examples to your actual site/list—just share hostname, site_path, and the list name.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file graphbridge-0.0.2.tar.gz.
File metadata
- Download URL: graphbridge-0.0.2.tar.gz
- Upload date:
- Size: 16.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce91bde37c8f29b48c335ee918d60b320de6c91a6f1de9b19d2d72c4d074729e
|
|
| MD5 |
aa13f0167ab4c67cee08114eb8c0f395
|
|
| BLAKE2b-256 |
64657327e6f7c219f9375d1e2f41802dea2d7791f85042a09e7762b1f1132ed8
|
File details
Details for the file graphbridge-0.0.2-py3-none-any.whl.
File metadata
- Download URL: graphbridge-0.0.2-py3-none-any.whl
- Upload date:
- Size: 12.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d7ffc7731c5ee71f0ea1120251497b71bbd67048367ea3c04ba1b8a9fc9815a7
|
|
| MD5 |
bec3972b4deb98e21362c1ce313f2259
|
|
| BLAKE2b-256 |
e9ffdb2fcce984ddc545884c3a8127b54d6206b6fd2b0709d6177f8585cb4b8e
|