Understanding Request Models, Body Parameters, and Advanced Customization in python-alfresco-api V1.1
📊 V1.1 Achievement: 46% code coverage with 100% test pass rate - comprehensive validation of GET operations, factory patterns, model imports, and MCP integration.
IMPORTANT CLARIFICATION: The "detailed" naming in our V1.1 architecture comes from the raw clients and refers to HTTP Response details, NOT more detailed content or parameters.
The difference is in the return type:
# Non-detailed: Returns just the parsed content
result = client.nodes.create(parent_id, request) # → NodeResponse
content = result.entry # Direct access to Node data
# Detailed: Returns full HTTP Response with metadata
response = client.nodes.create_detailed(parent_id, request) # → Response[NodeEntry]
status = response.status_code # 201
headers = response.headers # {'Content-Type': 'application/json', ...}
content = response.parsed # NodeEntry (if successful)When to Use Detailed:
- MCP servers needing HTTP status validation
- Error handling requiring response headers
- Rate limiting based on response metadata
- Debugging API interactions
All high-level methods map directly to raw client operations:
# Pattern mapping (confirmed working):
high-level sync → raw_client.operation.sync() # Returns: Optional[T]
high-level async → raw_client.operation.asyncio() # Returns: Optional[T]
high-level detailed sync → raw_client.operation.sync_detailed() # Returns: Response[T]
high-level detailed async → raw_client.operation.asyncio_detailed() # Returns: Response[T]Purpose: Create files, folders, and other content types
from python_alfresco_api.clients.core.nodes.models import CreateNodeRequest, NodeType
# Simple folder creation
request = CreateNodeRequest(
name="Project Documents",
node_type=NodeType.FOLDER
)
# Advanced content creation with properties
request = CreateNodeRequest(
name="annual-report-2024.pdf",
node_type=NodeType.CONTENT,
properties={
"cm:title": "Annual Financial Report 2024",
"cm:description": "Comprehensive financial analysis",
"cm:author": "Finance Department",
"custom:department": "Finance",
"custom:confidentialityLevel": "Internal"
},
aspects=["cm:titled", "cm:author", "custom:confidential"],
auto_rename=True,
versioning_enabled=True,
major_version=True
)
# Create the node
result = client.nodes.create(parent_id="-my-", request=request)
print(f"Created: {result.entry.name} with ID: {result.entry.id}")Available Fields:
name(required): Node display namenode_type(required):NodeType.CONTENT,NodeType.FOLDER, etc.properties(optional): Custom metadata dictionaryaspects(optional): List of aspect names to applyauto_rename(optional): Handle name conflicts automaticallyversioning_enabled(optional): Enable versioning for new contentmajor_version(optional): Create as major (1.0) vs minor (0.1) version
Purpose: Modify existing node properties and metadata
from python_alfresco_api.clients.core.nodes.models import UpdateNodeRequest
# Update node name and properties
request = UpdateNodeRequest(
name="updated-filename.pdf",
properties={
"cm:title": "Updated Document Title",
"cm:description": "Revised description",
"custom:reviewStatus": "Approved",
"custom:lastReviewDate": "2024-01-15"
}
)
result = client.nodes.update(node_id="abc123", request=request)Available Fields:
name(optional): New node nameproperties(optional): Properties to update (merged with existing)
Purpose: Copy nodes to different locations
from python_alfresco_api.clients.core.nodes.models import CopyNodeRequest
request = CopyNodeRequest(
target_parent_id="target-folder-456",
name="copied-document.pdf" # Optional: defaults to original name
)
result = client.nodes.copy(node_id="source-123", request=request)Purpose: Move nodes to different locations
from python_alfresco_api.clients.core.nodes.models import MoveNodeRequest
request = MoveNodeRequest(
target_parent_id="new-parent-789",
name="moved-document.pdf" # Optional: defaults to current name
)
result = client.nodes.move(node_id="node-123", request=request)Purpose: Advanced search operations with full Alfresco Search API power
from python_alfresco_api.raw_clients.alfresco_search_client.search_client.models import SearchRequest, RequestQuery, RequestPagination, RequestQueryLanguage
# Simple text search
search_request = SearchRequest(
query=RequestQuery(
query="annual report",
language=RequestQueryLanguage.AFTS # Default: AFTS (Alfresco Full Text Search)
)
)
# Advanced search with filters, facets, and pagination
search_request = SearchRequest(
query=RequestQuery(
query="TYPE:cm:content AND cm:title:*report*",
language=RequestQueryLanguage.AFTS,
user_query="annual report" # Original user input
),
paging=RequestPagination(
max_items=50,
skip_count=0
),
fields=["id", "name", "cm:title", "cm:description", "content.size"],
include=["properties", "path", "allowableOperations"],
filter_queries=[
{"query": "TYPE:'cm:content'"},
{"query": "cm:creator:admin"},
{"query": "content.size:[0 TO 1048576]"} # Files under 1MB
],
facet_fields={
"facets": [
{"field": "creator", "mincount": 1},
{"field": "cm:modified", "mincount": 1}
]
},
sort=[
{"type": "FIELD", "field": "cm:modified", "ascending": False}
]
)
# Execute search
result = client.search.search(body=search_request)Advanced SearchRequest Fields:
query(required):RequestQuerywith search string and languagepaging(optional):RequestPaginationfor pagination controlfields(optional): List of fields to return (saves bandwidth)include(optional): Additional node information (properties, path, etc.)filter_queries(optional): Constraints that don't affect scoringfacet_queries(optional): Facet queries for result aggregationfacet_fields(optional): Field-based faceting configurationsort(optional): Sorting specificationhighlight(optional): Search result highlightingspellcheck(optional): Spell checking configurationscope(optional): Search scope (nodes, versions, deleted-nodes)defaults(optional): Query defaults and operatorslimits(optional): Performance limitslocalization(optional): Locale and timezone settingstemplates(optional): Query expansion templates
Purpose: SQL-like queries using CMIS standard
# CMIS query for structured searches
cmis_request = SearchRequest(
query=RequestQuery(
query="SELECT * FROM cmis:document WHERE cmis:contentStreamMimeType = 'application/pdf'",
language=RequestQueryLanguage.CMIS
),
paging=RequestPagination(max_items=25)
)
result = client.search.search(body=cmis_request)Purpose: Search with result categorization
# Faceted search for analytics and filtering
faceted_request = SearchRequest(
query=RequestQuery(query="presentation"),
facet_queries=[
{"query": "content.size:[0 TO 10240]", "label": "small"},
{"query": "content.size:[10240 TO 102400]", "label": "medium"},
{"query": "content.size:[102400 TO 1048576]", "label": "large"}
],
facet_fields={
"facets": [
{"field": "creator", "mincount": 1},
{"field": "cm:modified", "mincount": 1}
]
}
)
result = client.search.search(body=faceted_request)
# Access facet results in result.context.facetQueries and result.context.facetFieldsPurpose: Highlight matching terms in search results
# Search with result highlighting
highlight_request = SearchRequest(
query=RequestQuery(
query="workflow",
user_query="workflow"
),
highlight={
"prefix": "<mark>",
"postfix": "</mark>",
"mergeContiguous": True,
"fields": [
{"field": "cm:title"},
{"field": "cm:description", "prefix": "**", "postfix": "**"}
]
}
)
result = client.search.search(body=highlight_request)
# Access highlighting in result.entries[i].search.highlightAlfresco supports three query languages:
AFTS (Alfresco Full Text Search) - Default & Recommended:
# Text searches
"annual report"
"cm:title:budget AND cm:description:2024"
# Type and property searches
"TYPE:cm:content AND cm:creator:admin"
"PATH:\"/app:company_home/cm:Budget/*\""
# Content searches
"TEXT:\"financial analysis\" AND TYPE:cm:content"CMIS (Content Management Interoperability Services):
# SQL-like syntax
"SELECT * FROM cmis:document WHERE cmis:name LIKE '%report%'"
"SELECT cmis:objectId, cmis:name FROM cmis:folder WHERE IN_FOLDER('workspace://SpacesStore/abc123')"
"SELECT * FROM cmis:document WHERE cmis:contentStreamMimeType = 'application/pdf'"Lucene (Advanced Users):
# Raw Lucene syntax
"cm\\:title:budget AND cm\\:creator:admin"
"+TYPE:cm\\:content +TEXT:report"When to Use Each:
- AFTS: Most use cases, best performance, supports all Alfresco features
- CMIS: Standards compliance, integration with other CMIS systems
- Lucene: Advanced power users, complex query requirements
All current V1.1 methods support the same parameters as raw clients:
# High-level method with raw client parameters
result = client.nodes.list_children(
node_id="-my-",
skip_count=0, # Pagination offset
max_items=25, # Page size
order_by="name ASC", # Sorting
where="isFile=true", # Filtering
include=["properties", "path", "allowableOperations"], # Response enrichment
fields=["id", "name", "nodeType", "isFile", "properties"] # Field selection
)
# The parameters map directly to raw client:
# list_node_children.sync(client, node_id, skip_count, max_items, order_by, where, include, fields)Properties accept any valid Alfresco metadata:
# Standard Alfresco properties
properties = {
# Content Model properties
"cm:title": "Document Title",
"cm:description": "Document description",
"cm:author": "John Doe",
"cm:created": "2024-01-15T10:30:00.000Z",
# Custom model properties (if defined in Alfresco)
"custom:projectCode": "PROJ-2024-001",
"custom:department": "Engineering",
"custom:confidentiality": "Internal",
# System properties (read-only, don't set these)
# "sys:node-uuid": "...", # Don't set - system managed
# "sys:store-identifier": "...", # Don't set - system managed
}Apply multiple behaviors to nodes:
# Common aspects
aspects = [
"cm:titled", # Adds title/description fields
"cm:author", # Adds author field
"cm:geographic", # Adds latitude/longitude
"cm:complianceable", # Adds compliance metadata
"custom:projectInfo", # Custom aspect (if defined)
]
request = CreateNodeRequest(
name="project-doc.pdf",
node_type=NodeType.CONTENT,
aspects=aspects,
properties={
"cm:title": "Project Documentation",
"cm:author": "Project Manager",
"cm:latitude": 40.7128,
"cm:longitude": -74.0060,
"custom:projectPhase": "Planning"
}
)Control what additional data is returned:
# Available include options
include_options = [
"properties", # Node properties/metadata
"allowableOperations", # What operations user can perform
"path", # Full path from repository root
"isLink", # Whether node is a link
"isFavorite", # Whether user has favorited
"aspectNames", # Applied aspects
"association", # Association information
"permissions", # Detailed permissions
]
# Use in any GET operation
result = client.nodes.get(
node_id="abc123",
include=["properties", "path", "allowableOperations"]
)
# Rich response with full metadata
print(f"Path: {result.entry.path.name}")
print(f"Properties: {result.entry.properties}")
print(f"Can delete: {'delete' in result.entry.allowable_operations}")Limited Support: V1.1 primarily uses typed request models, not raw body dicts.
# ❌ Not supported (raw body dict)
body = {"name": "test", "nodeType": "cm:folder"}
# client.some_method(body=body) # Won't work
# ✅ Supported (typed request)
request = CreateNodeRequest(name="test", node_type=NodeType.FOLDER)
result = client.nodes.create(parent_id="-my-", request=request)Workaround for Raw Bodies:
# Access raw client for maximum flexibility
raw_client = client.nodes._get_raw_client()
# Use raw operations directly
from python_alfresco_api.raw_clients.alfresco_core_client.core_client.api.nodes import create_node
from python_alfresco_api.raw_clients.alfresco_core_client.core_client.models import NodeBodyCreate
body = NodeBodyCreate.from_dict({
"name": "test-folder",
"nodeType": "cm:folder",
"properties": {"cm:title": "Custom Title"}
})
result = create_node.sync(client=raw_client, node_id="-my-", body=body)Current Support: Search requires raw client models for full power
# ✅ Supported - basic search via raw client models
from python_alfresco_api.raw_clients.alfresco_search_client.search_client.models import SearchRequest, RequestQuery
search_request = SearchRequest(
query=RequestQuery(query="annual report")
)
result = client.search.search(body=search_request)
# ❌ Not yet supported - high-level search request models
# Simple search_request = SimpleSearchRequest(text="annual report", content_types=["pdf"])
# Use raw client SearchRequest for all search operationsCurrent Support: Limited to basic parameters
# ✅ Supported - basic file upload parameters
result = client.content.upload_file(
parent_id="-my-",
file_name="document.pdf",
content_type="application/pdf",
file_data=file_bytes
)
# ❌ Not yet supported - advanced multipart options
# Advanced file upload with custom headers, multiple files, etc.
# Use raw client for these casesWhen high-level methods aren't enough:
# Pattern 1: Get raw client from any high-level client
raw_client = client.nodes._get_raw_client()
# Pattern 2: Direct HTTPx access for maximum control
httpx_client = client.nodes._get_raw_client().get_httpx_client()
response = httpx_client.post("/nodes/-my-/children", json={...})
# Pattern 3: Raw operation imports
from python_alfresco_api.raw_clients.alfresco_core_client.core_client.api.nodes import create_node
result = create_node.sync(client=raw_client, node_id="-my-", body=typed_body)
# Pattern 4: Search-specific raw client access
from python_alfresco_api.raw_clients.alfresco_search_client.search_client.api.search import search
from python_alfresco_api.raw_clients.alfresco_search_client.search_client.models import SearchRequest, RequestQuery
search_raw_client = client.search._get_raw_client()
search_request = SearchRequest(query=RequestQuery(query="TYPE:cm:content"))
result = search.sync(client=search_raw_client, body=search_request)- 📖 Client Types Guide - Choose the right client level
- 🏗️ V1.1 Architecture - Understanding the structure
- 🎯 Examples - Working code examples
- 🧪 Tests - See parameters in action
# Use typed requests for safety and intellisense
request = CreateNodeRequest(name="doc.pdf", node_type=NodeType.CONTENT)
result = client.nodes.create(parent_id="-my-", request=request)# Add include/fields for richer responses
result = client.nodes.list_children(
node_id="-my-",
include=["properties", "allowableOperations"],
fields=["id", "name", "properties", "isFile"]
)# Search requires raw client models for full power
from python_alfresco_api.raw_clients.alfresco_search_client.search_client.models import SearchRequest, RequestQuery
search_request = SearchRequest(
query=RequestQuery(query="TYPE:cm:content AND cm:title:*report*"),
fields=["id", "name", "cm:title"]
)
result = client.search.search(body=search_request)# When you need maximum control
raw_client = client.nodes._get_raw_client()
# Direct raw operations...This architecture provides three clear levels: high-level convenience → typed requests → raw client control, allowing you to choose the right abstraction level for each use case.