running MVP2

This commit is contained in:
ValueOn AG 2025-08-23 14:40:36 +02:00
parent 1b59810e0d
commit 2a02bb1c01
12 changed files with 199 additions and 110 deletions

View file

@ -642,8 +642,13 @@ class HandlingTasks:
action.result = first_doc.documentData.get("result", "") action.result = first_doc.documentData.get("result", "")
elif hasattr(first_doc, 'documentData') and isinstance(first_doc.documentData, str): elif hasattr(first_doc, 'documentData') and isinstance(first_doc.documentData, str):
action.result = first_doc.documentData action.result = first_doc.documentData
action.execResultLabel = result.resultLabel or result_label # Preserve the action's execResultLabel for document routing
await self.createActionMessage(action, result, workflow, result.resultLabel or result_label, created_documents, task_step, task_index) # Action methods should NOT return resultLabel - this is managed by the action handler
if not action.execResultLabel:
logger.warning(f"Action {action.execMethod}.{action.execAction} has no execResultLabel set")
# Always use the action's execResultLabel for message creation to ensure proper document routing
message_result_label = action.execResultLabel
await self.createActionMessage(action, result, workflow, message_result_label, created_documents, task_step, task_index)
# Log action results # Log action results
logger.info(f"✓ Action completed successfully") logger.info(f"✓ Action completed successfully")
@ -719,7 +724,7 @@ class HandlingTasks:
return ActionResult( return ActionResult(
success=result.success, success=result.success,
documents=original_documents, # Preserve original documents field from method result documents=original_documents, # Preserve original documents field from method result
resultLabel=result.resultLabel or result_label, resultLabel=action.execResultLabel, # Always use action's execResultLabel
error=result.error or "" error=result.error or ""
) )
except Exception as e: except Exception as e:
@ -728,7 +733,7 @@ class HandlingTasks:
return ActionResult( return ActionResult(
success=False, success=False,
documents=[], # Empty documents for error case documents=[], # Empty documents for error case
resultLabel=result_label, resultLabel=action.execResultLabel,
error=str(e) error=str(e)
) )

View file

@ -10,7 +10,17 @@ import inspect
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def action(func): def action(func):
"""Decorator to mark a method as an available action""" """Decorator to mark a method as an available action
IMPORTANT: Action methods should NOT return resultLabel in their ActionResult.
The resultLabel is managed by the action handler using the action's execResultLabel
from the action plan. This ensures consistent document routing throughout the workflow.
Action methods should only return:
- success: bool
- documents: List[ActionDocument]
- error: str (if success=False)
"""
@wraps(func) @wraps(func)
async def wrapper(self, parameters: Dict[str, Any], *args, **kwargs): async def wrapper(self, parameters: Dict[str, Any], *args, **kwargs):
return await func(self, parameters, *args, **kwargs) return await func(self, parameters, *args, **kwargs)

View file

@ -468,7 +468,8 @@ class ServiceCenter:
token = None token = None
token_status = "unknown" token_status = "unknown"
try: try:
token = self.interfaceApp.getToken(connection.authority.value) # Use getConnectionToken to find token for this specific connection
token = self.interfaceApp.getConnectionToken(connection.id)
if token: if token:
if hasattr(token, 'expiresAt') and token.expiresAt: if hasattr(token, 'expiresAt') and token.expiresAt:
current_time = get_utc_timestamp() current_time = get_utc_timestamp()

View file

@ -740,9 +740,13 @@ class AppObjects:
"message": f"Error checking username availability: {str(e)}" "message": f"Error checking username availability: {str(e)}"
} }
def saveToken(self, token: Token) -> None: def saveAccessToken(self, token: Token) -> None:
"""Save a token for the current user""" """Save an access token for the current user (must NOT have connectionId)"""
try: try:
# Validate that this is NOT a connection token
if token.connectionId:
raise ValueError("Access tokens cannot have connectionId - use saveConnectionToken instead")
# Validate user context # Validate user context
if not self.currentUser or not self.currentUser.id: if not self.currentUser or not self.currentUser.id:
raise ValueError("No valid user context available for token storage") raise ValueError("No valid user context available for token storage")
@ -758,7 +762,7 @@ class AppObjects:
# Convert to dict and ensure all fields are properly set # Convert to dict and ensure all fields are properly set
token_dict = token.dict() token_dict = token.dict()
# Ensure userId is set to current user (this might override the token's userId) # Ensure userId is set to current user
token_dict["userId"] = self.currentUser.id token_dict["userId"] = self.currentUser.id
# Save to database # Save to database
@ -767,19 +771,57 @@ class AppObjects:
# Clear cache to ensure fresh data # Clear cache to ensure fresh data
self._clearTableCache("tokens") self._clearTableCache("tokens")
except Exception as e: except Exception as e:
logger.error(f"Error saving token: {str(e)}") logger.error(f"Error saving access token: {str(e)}")
raise raise
def getToken(self, authority: str, auto_refresh: bool = True) -> Optional[Token]: def saveConnectionToken(self, token: Token) -> None:
"""Get the latest valid token for the current user and authority, optionally auto-refresh if expired""" """Save a connection token (must have connectionId)"""
try: try:
# Get tokens for this user and authority # Validate that this IS a connection token
if not token.connectionId:
raise ValueError("Connection tokens must have connectionId - use saveAccessToken instead")
# Validate user context
if not self.currentUser or not self.currentUser.id:
raise ValueError("No valid user context available for token storage")
# Set the user ID for the connection token
token.userId = self.currentUser.id
# Ensure token has required fields
if not token.id:
token.id = str(uuid.uuid4())
if not token.createdAt:
token.createdAt = get_utc_timestamp()
# Convert to dict and ensure all fields are properly set
token_dict = token.dict()
# Ensure userId is set to current user
token_dict["userId"] = self.currentUser.id
# Save to database
self.db.recordCreate("tokens", token_dict)
# Clear cache to ensure fresh data
self._clearTableCache("tokens")
except Exception as e:
logger.error(f"Error saving connection token: {str(e)}")
raise
def getAccessToken(self, authority: str, auto_refresh: bool = True) -> Optional[Token]:
"""Get the latest valid access token for the current user and authority, optionally auto-refresh if expired"""
try:
# Validate that we're not looking for connection tokens
if not self.currentUser or not self.currentUser.id:
raise ValueError("No valid user context available for token retrieval")
# Get access tokens for this user and authority (must NOT have connectionId)
tokens = self.db.getRecordset("tokens", recordFilter={ tokens = self.db.getRecordset("tokens", recordFilter={
"userId": self.currentUser.id, "userId": self.currentUser.id,
"authority": authority "authority": authority,
"connectionId": None # Ensure we only get access tokens
}) })
if not tokens: if not tokens:
@ -792,8 +834,6 @@ class AppObjects:
# Check if token is expired # Check if token is expired
if latest_token.expiresAt and latest_token.expiresAt < get_utc_timestamp(): if latest_token.expiresAt and latest_token.expiresAt < get_utc_timestamp():
if auto_refresh: if auto_refresh:
# Import TokenManager here to avoid circular imports # Import TokenManager here to avoid circular imports
from modules.security.tokenManager import TokenManager from modules.security.tokenManager import TokenManager
token_manager = TokenManager() token_manager = TokenManager()
@ -802,45 +842,58 @@ class AppObjects:
refreshed_token = token_manager.refresh_token(latest_token) refreshed_token = token_manager.refresh_token(latest_token)
if refreshed_token: if refreshed_token:
# Save the new token and delete the old one # Save the new token and delete the old one
self.saveToken(refreshed_token) self.saveAccessToken(refreshed_token)
self.deleteToken(authority) self.deleteAccessToken(authority)
return refreshed_token return refreshed_token
else: else:
logger.warning(f"Failed to refresh expired token for {authority}") logger.warning(f"Failed to refresh expired access token for {authority}")
return None return None
else: else:
logger.warning(f"Token for {authority} is expired (expiresAt: {latest_token.expiresAt})") logger.warning(f"Access token for {authority} is expired (expiresAt: {latest_token.expiresAt})")
return None return None
return latest_token return latest_token
except Exception as e: except Exception as e:
logger.error(f"Error getting token: {str(e)}") logger.error(f"Error getting access token: {str(e)}")
return None return None
def getTokenForConnection(self, connectionId: str, auto_refresh: bool = True) -> Optional[Token]: def getConnectionToken(self, connectionId: str, auto_refresh: bool = True) -> Optional[Token]:
"""Get the token for a specific connection, optionally auto-refresh if expired""" """Get the connection token for a specific connectionId, optionally auto-refresh if expired"""
try: try:
logger.debug(f"Getting connection token for connectionId: {connectionId}")
# Validate connectionId
if not connectionId:
raise ValueError("connectionId is required for getConnectionToken")
# Get token for this specific connection # Get token for this specific connection
logger.debug(f"Querying tokens table with connectionId: {connectionId}")
# Query for specific connection
tokens = self.db.getRecordset("tokens", recordFilter={ tokens = self.db.getRecordset("tokens", recordFilter={
"connectionId": connectionId "connectionId": connectionId
}) })
logger.debug(f"Raw tokens from database for connectionId {connectionId}: {tokens}")
logger.debug(f"Tokens count: {len(tokens) if tokens else 0}")
if not tokens: if not tokens:
logger.warning(f"No token found for connection: {connectionId}") logger.warning(f"No connection token found for connectionId: {connectionId}")
return None return None
# Sort by expiration date and get the latest (most recent expiration) # Sort by expiration date and get the latest (most recent expiration)
logger.debug(f"Sorting tokens by expiresAt, current tokens: {tokens}")
tokens.sort(key=lambda x: x.get("expiresAt", 0), reverse=True) tokens.sort(key=lambda x: x.get("expiresAt", 0), reverse=True)
latest_token = Token(**tokens[0]) latest_token = Token(**tokens[0])
logger.debug(f"Latest connection token: {latest_token}")
logger.debug(f"Token expiresAt: {latest_token.expiresAt}, type: {type(latest_token.expiresAt)}")
logger.debug(f"Current UTC timestamp: {get_utc_timestamp()}, type: {type(get_utc_timestamp())}")
# Check if token is expired # Check if token is expired
if latest_token.expiresAt and latest_token.expiresAt < get_utc_timestamp(): if latest_token.expiresAt and latest_token.expiresAt < get_utc_timestamp():
if auto_refresh: if auto_refresh:
# Import TokenManager here to avoid circular imports # Import TokenManager here to avoid circular imports
from modules.security.tokenManager import TokenManager from modules.security.tokenManager import TokenManager
token_manager = TokenManager() token_manager = TokenManager()
@ -849,31 +902,36 @@ class AppObjects:
refreshed_token = token_manager.refresh_token(latest_token) refreshed_token = token_manager.refresh_token(latest_token)
if refreshed_token: if refreshed_token:
# Save the new token and delete the old one # Save the new token and delete the old one
self.saveToken(refreshed_token) self.saveConnectionToken(refreshed_token)
self.deleteTokenByConnectionId(connectionId) self.deleteConnectionTokenByConnectionId(connectionId)
return refreshed_token return refreshed_token
else: else:
logger.warning(f"Failed to refresh expired token for connection {connectionId}") logger.warning(f"Failed to refresh expired connection token for connectionId {connectionId}")
return None return None
else: else:
logger.warning(f"Token for connection {connectionId} is expired (expiresAt: {latest_token.expiresAt})") logger.warning(f"Connection token for connectionId {connectionId} is expired (expiresAt: {latest_token.expiresAt})")
return None return None
logger.debug(f"Returning valid connection token: {latest_token}")
return latest_token return latest_token
except Exception as e: except Exception as e:
logger.error(f"Error getting token for connection {connectionId}: {str(e)}") logger.error(f"Error getting connection token for connectionId {connectionId}: {str(e)}")
return None return None
def deleteToken(self, authority: str) -> None: def deleteAccessToken(self, authority: str) -> None:
"""Delete all tokens for the current user and authority""" """Delete all access tokens for the current user and authority"""
try: try:
# Get tokens to delete # Validate user context
if not self.currentUser or not self.currentUser.id:
raise ValueError("No valid user context available for token deletion")
# Get access tokens to delete (must NOT have connectionId)
tokens = self.db.getRecordset("tokens", recordFilter={ tokens = self.db.getRecordset("tokens", recordFilter={
"userId": self.currentUser.id, "userId": self.currentUser.id,
"authority": authority "authority": authority,
"connectionId": None # Ensure we only delete access tokens
}) })
# Delete each token # Delete each token
@ -884,13 +942,17 @@ class AppObjects:
self._clearTableCache("tokens") self._clearTableCache("tokens")
except Exception as e: except Exception as e:
logger.error(f"Error deleting token: {str(e)}") logger.error(f"Error deleting access token: {str(e)}")
raise raise
def deleteTokenByConnectionId(self, connectionId: str) -> None: def deleteConnectionTokenByConnectionId(self, connectionId: str) -> None:
"""Delete all tokens for a specific connection""" """Delete all connection tokens for a specific connectionId"""
try: try:
# Get tokens to delete # Validate connectionId
if not connectionId:
raise ValueError("connectionId is required for deleteConnectionTokenByConnectionId")
# Get connection tokens to delete
tokens = self.db.getRecordset("tokens", recordFilter={ tokens = self.db.getRecordset("tokens", recordFilter={
"connectionId": connectionId "connectionId": connectionId
}) })
@ -903,9 +965,15 @@ class AppObjects:
self._clearTableCache("tokens") self._clearTableCache("tokens")
except Exception as e: except Exception as e:
logger.error(f"Error deleting token for connection {connectionId}: {str(e)}") logger.error(f"Error deleting connection token for connectionId {connectionId}: {str(e)}")
raise raise
# Backward compatibility method
def getTokenForConnection(self, connectionId: str, auto_refresh: bool = True) -> Optional[Token]:
"""Backward compatibility method - use getConnectionToken instead"""
logger.warning("getTokenForConnection is deprecated, use getConnectionToken instead")
return self.getConnectionToken(connectionId, auto_refresh)
def cleanupExpiredTokens(self) -> int: def cleanupExpiredTokens(self) -> int:
"""Clean up expired tokens for all connections, returns count of cleaned tokens""" """Clean up expired tokens for all connections, returns count of cleaned tokens"""
try: try:

View file

@ -31,32 +31,41 @@ register_model_labels(
) )
class ActionResult(BaseModel, ModelMixin): class ActionResult(BaseModel, ModelMixin):
"""Clean action result with documents as primary output""" """Clean action result with documents as primary output
IMPORTANT: Action methods should NOT set resultLabel in their return value.
The resultLabel is managed by the action handler using the action's execResultLabel
from the action plan. This ensures consistent document routing throughout the workflow.
"""
# Core result # Core result
success: bool = Field(description="Whether execution succeeded") success: bool = Field(description="Whether execution succeeded")
error: Optional[str] = Field(None, description="Error message if failed") error: Optional[str] = Field(None, description="Error message if failed")
# Primary output - documents # Primary output - documents
documents: List[ActionDocument] = Field(default_factory=list, description="Document outputs") documents: List[ActionDocument] = Field(default_factory=list, description="Document outputs")
resultLabel: Optional[str] = Field(None, description="Label for document routing") resultLabel: Optional[str] = Field(None, description="Label for document routing (set by action handler, not by action methods)")
@classmethod @classmethod
def success(cls, documents: List[ActionDocument] = None, resultLabel: str = None) -> 'ActionResult': def success(cls, documents: List[ActionDocument] = None) -> 'ActionResult':
"""Create a successful action result""" """Create a successful action result
Note: Do not set resultLabel - this is managed by the action handler
"""
return cls( return cls(
success=True, success=True,
documents=documents or [], documents=documents or []
resultLabel=resultLabel
) )
@classmethod @classmethod
def failure(cls, error: str, documents: List[ActionDocument] = None, resultLabel: str = None) -> 'ActionResult': def failure(cls, error: str, documents: List[ActionDocument] = None) -> 'ActionResult':
"""Create a failed action result""" """Create a failed action result
Note: Do not set resultLabel - this is managed by the action handler
"""
return cls( return cls(
success=False, success=False,
documents=documents or [], documents=documents or [],
error=error, error=error
resultLabel=resultLabel
) )
# Register labels for ActionResult # Register labels for ActionResult

View file

@ -102,18 +102,25 @@ class MethodOutlook(MethodBase):
Helper function to get Microsoft connection details. Helper function to get Microsoft connection details.
""" """
try: try:
logger.debug(f"Getting Microsoft connection for reference: {connectionReference}")
# Get the connection from the service # Get the connection from the service
userConnection = self.service.getUserConnectionFromConnectionReference(connectionReference) userConnection = self.service.getUserConnectionFromConnectionReference(connectionReference)
if not userConnection: if not userConnection:
logger.error(f"Connection not found: {connectionReference}") logger.error(f"Connection not found: {connectionReference}")
return None return None
logger.debug(f"Found connection: {userConnection.id}, status: {userConnection.status.value}, authority: {userConnection.authority.value}")
# Get the token for this specific connection # Get the token for this specific connection
token = self.service.interfaceApp.getTokenForConnection(userConnection.id) token = self.service.interfaceApp.getConnectionToken(userConnection.id)
if not token: if not token:
logger.error(f"Token not found for connection: {userConnection.id}") logger.error(f"Token not found for connection: {userConnection.id}")
logger.debug(f"Connection details: {userConnection}")
return None return None
logger.debug(f"Token retrieved for connection {userConnection.id}")
# Check if token is expired # Check if token is expired
if hasattr(token, 'expiresAt') and token.expiresAt: if hasattr(token, 'expiresAt') and token.expiresAt:
current_time = get_utc_timestamp() current_time = get_utc_timestamp()
@ -126,8 +133,6 @@ class MethodOutlook(MethodBase):
logger.error(f"Connection is not active: {userConnection.id}, status: {userConnection.status.value}") logger.error(f"Connection is not active: {userConnection.id}, status: {userConnection.status.value}")
return None return None
return { return {
"id": userConnection.id, "id": userConnection.id,
"accessToken": token.tokenAccess, "accessToken": token.tokenAccess,
@ -482,8 +487,7 @@ class MethodOutlook(MethodBase):
"timestamp": get_utc_timestamp() "timestamp": get_utc_timestamp()
}, },
"mimeType": "application/json" "mimeType": "application/json"
}], }]
resultLabel="outlook_emails"
) )
except Exception as e: except Exception as e:
@ -529,8 +533,10 @@ class MethodOutlook(MethodBase):
) )
# Get Microsoft connection # Get Microsoft connection
logger.debug(f"Getting Microsoft connection for sendEmail action")
connection = self._getMicrosoftConnection(connectionReference) connection = self._getMicrosoftConnection(connectionReference)
if not connection: if not connection:
logger.error(f"Failed to get Microsoft connection for reference: {connectionReference}")
return ActionResult.failure(error="Failed to get Microsoft connection") return ActionResult.failure(error="Failed to get Microsoft connection")
# Get the composed email document # Get the composed email document
@ -777,8 +783,7 @@ class MethodOutlook(MethodBase):
"timestamp": get_utc_timestamp() "timestamp": get_utc_timestamp()
}, },
"mimeType": "application/json" "mimeType": "application/json"
}], }]
resultLabel="email_draft_created"
) )
else: else:
logger.error(f"Failed to create draft. Status: {response.status_code}, Response: {response.text}") logger.error(f"Failed to create draft. Status: {response.status_code}, Response: {response.text}")
@ -815,8 +820,7 @@ class MethodOutlook(MethodBase):
"timestamp": get_utc_timestamp() "timestamp": get_utc_timestamp()
}, },
"mimeType": "application/json" "mimeType": "application/json"
}], }]
resultLabel="outlook_email_draft"
) )
except Exception as e: except Exception as e:
@ -998,8 +1002,7 @@ class MethodOutlook(MethodBase):
"timestamp": get_utc_timestamp() "timestamp": get_utc_timestamp()
}, },
"mimeType": "application/json" "mimeType": "application/json"
}], }]
resultLabel="outlook_email_search"
) )
except Exception as e: except Exception as e:
@ -1122,8 +1125,7 @@ class MethodOutlook(MethodBase):
"timestamp": get_utc_timestamp() "timestamp": get_utc_timestamp()
}, },
"mimeType": "application/json" "mimeType": "application/json"
}], }]
resultLabel="outlook_drafts_list"
) )
except Exception as e: except Exception as e:
@ -1233,8 +1235,7 @@ class MethodOutlook(MethodBase):
"timestamp": get_utc_timestamp() "timestamp": get_utc_timestamp()
}, },
"mimeType": "application/json" "mimeType": "application/json"
}], }]
resultLabel="outlook_drafts_found"
) )
except Exception as e: except Exception as e:
@ -1377,8 +1378,7 @@ class MethodOutlook(MethodBase):
"timestamp": get_utc_timestamp() "timestamp": get_utc_timestamp()
}, },
"mimeType": "application/json" "mimeType": "application/json"
}], }]
resultLabel="outlook_drafts_folder_check"
) )
except Exception as e: except Exception as e:
@ -1618,8 +1618,7 @@ class MethodOutlook(MethodBase):
"documentName": f"composed_email_{int(get_utc_timestamp())}.json", "documentName": f"composed_email_{int(get_utc_timestamp())}.json",
"documentData": result_data, "documentData": result_data,
"mimeType": "application/json" "mimeType": "application/json"
}], }]
resultLabel="composed_email"
) )
except Exception as e: except Exception as e:
@ -1662,8 +1661,7 @@ class MethodOutlook(MethodBase):
"status": "ready" "status": "ready"
}, },
"mimeType": "application/json" "mimeType": "application/json"
}], }]
resultLabel="permissions_ready"
) )
else: else:
return ActionResult( return ActionResult(
@ -1680,8 +1678,7 @@ class MethodOutlook(MethodBase):
}, },
"mimeType": "application/json" "mimeType": "application/json"
}], }],
error="Connection lacks necessary permissions for Outlook operations", error="Connection lacks necessary permissions for Outlook operations"
resultLabel="permissions_missing"
) )
except Exception as e: except Exception as e:

View file

@ -44,7 +44,7 @@ class MethodSharepoint(MethodBase):
return None return None
# Get the token for this specific connection # Get the token for this specific connection
token = self.service.interfaceApp.getTokenForConnection(userConnection.id) token = self.service.interfaceApp.getConnectionToken(userConnection.id)
if not token: if not token:
logger.warning(f"No token found for connection {userConnection.id}") logger.warning(f"No token found for connection {userConnection.id}")
return None return None
@ -283,8 +283,7 @@ class MethodSharepoint(MethodBase):
"documentData": result_data, "documentData": result_data,
"mimeType": output_mime_type "mimeType": output_mime_type
} }
], ]
resultLabel="sharepoint_find_path"
) )
except Exception as e: except Exception as e:
@ -474,8 +473,7 @@ class MethodSharepoint(MethodBase):
"documentData": result_data, "documentData": result_data,
"mimeType": output_mime_type "mimeType": output_mime_type
} }
], ]
resultLabel="sharepoint_documents"
) )
except Exception as e: except Exception as e:
logger.error(f"Error reading SharePoint documents: {str(e)}") logger.error(f"Error reading SharePoint documents: {str(e)}")
@ -652,8 +650,7 @@ class MethodSharepoint(MethodBase):
"documentData": result_data, "documentData": result_data,
"mimeType": output_mime_type "mimeType": output_mime_type
} }
], ]
resultLabel="sharepoint_upload"
) )
except Exception as e: except Exception as e:
@ -861,8 +858,7 @@ class MethodSharepoint(MethodBase):
"documentData": result_data, "documentData": result_data,
"mimeType": output_mime_type "mimeType": output_mime_type
} }
], ]
resultLabel="sharepoint_document_list"
) )
except Exception as e: except Exception as e:

View file

@ -562,8 +562,7 @@ class MethodWeb(MethodBase):
"documentData": result_data, "documentData": result_data,
"mimeType": output_mime_type "mimeType": output_mime_type
} }
], ]
resultLabel="web_search_results"
) )
except Exception as e: except Exception as e:
@ -698,8 +697,7 @@ class MethodWeb(MethodBase):
"documentData": result_data, "documentData": result_data,
"mimeType": output_mime_type "mimeType": output_mime_type
} }
], ]
resultLabel="web_crawl_results"
) )
except Exception as e: except Exception as e:
@ -803,8 +801,7 @@ class MethodWeb(MethodBase):
"documentData": result_data, "documentData": result_data,
"mimeType": output_mime_type "mimeType": output_mime_type
} }
], ]
resultLabel="web_scrape_results"
) )
except Exception as e: except Exception as e:

View file

@ -223,9 +223,9 @@ async def auth_callback(code: str, state: str, request: Request) -> HTMLResponse
createdAt=get_utc_timestamp() createdAt=get_utc_timestamp()
) )
# Save token # Save access token (no connectionId)
appInterface = getInterface(user) appInterface = getInterface(user)
appInterface.saveToken(token) appInterface.saveAccessToken(token)
# Return success page with token data # Return success page with token data
return HTMLResponse( return HTMLResponse(
@ -347,7 +347,7 @@ async def auth_callback(code: str, state: str, request: Request) -> HTMLResponse
expiresAt=create_expiration_timestamp(token_response.get("expires_in", 0)), expiresAt=create_expiration_timestamp(token_response.get("expires_in", 0)),
createdAt=get_utc_timestamp() createdAt=get_utc_timestamp()
) )
interface.saveToken(token) interface.saveConnectionToken(token)
# Return success page with connection data # Return success page with connection data
return HTMLResponse( return HTMLResponse(
@ -491,7 +491,7 @@ async def refresh_token(
logger.debug(f"Found Google connection: {google_connection.id}, status={google_connection.status}") logger.debug(f"Found Google connection: {google_connection.id}, status={google_connection.status}")
# Get the token for this specific connection using the new method # Get the token for this specific connection using the new method
current_token = appInterface.getTokenForConnection(google_connection.id, auto_refresh=False) current_token = appInterface.getConnectionToken(google_connection.id, auto_refresh=False)
if not current_token: if not current_token:
raise HTTPException( raise HTTPException(
@ -507,9 +507,9 @@ async def refresh_token(
refreshed_token = token_manager.refresh_token(current_token) refreshed_token = token_manager.refresh_token(current_token)
if refreshed_token: if refreshed_token:
# Save the new token and delete the old one # Save the new connection token and delete the old one
appInterface.saveToken(refreshed_token) appInterface.saveConnectionToken(refreshed_token)
appInterface.deleteTokenByConnectionId(google_connection.id) appInterface.deleteConnectionTokenByConnectionId(google_connection.id)
# Update the connection's expiration time # Update the connection's expiration time
google_connection.expiresAt = float(refreshed_token.expiresAt) google_connection.expiresAt = float(refreshed_token.expiresAt)

View file

@ -103,8 +103,8 @@ async def login(
expiresAt=expires_at.timestamp() expiresAt=expires_at.timestamp()
) )
# Save token # Save access token
userInterface.saveToken(token) userInterface.saveAccessToken(token)
# Create response data # Create response data
response_data = { response_data = {

View file

@ -173,9 +173,9 @@ async def auth_callback(code: str, state: str, request: Request) -> HTMLResponse
createdAt=get_utc_timestamp() createdAt=get_utc_timestamp()
) )
# Save token # Save access token (no connectionId)
appInterface = getInterface(user) appInterface = getInterface(user)
appInterface.saveToken(token) appInterface.saveAccessToken(token)
# Create JWT token data # Create JWT token data
jwt_token_data = { jwt_token_data = {
@ -198,8 +198,8 @@ async def auth_callback(code: str, state: str, request: Request) -> HTMLResponse
createdAt=get_utc_timestamp() createdAt=get_utc_timestamp()
) )
# Save JWT token # Save JWT access token
appInterface.saveToken(jwt_token_obj) appInterface.saveAccessToken(jwt_token_obj)
# Convert token to dict and ensure proper timestamp handling # Convert token to dict and ensure proper timestamp handling
token_dict = jwt_token_obj.to_dict() token_dict = jwt_token_obj.to_dict()
@ -328,7 +328,7 @@ async def auth_callback(code: str, state: str, request: Request) -> HTMLResponse
) )
interface.saveToken(token) interface.saveConnectionToken(token)
# Return success page with connection data # Return success page with connection data
@ -499,7 +499,7 @@ async def refresh_token(
# Get the token for this specific connection using the new method # Get the token for this specific connection using the new method
# Enable auto-refresh to handle expired tokens gracefully # Enable auto-refresh to handle expired tokens gracefully
current_token = appInterface.getTokenForConnection(msft_connection.id, auto_refresh=True) current_token = appInterface.getConnectionToken(msft_connection.id, auto_refresh=True)
if not current_token: if not current_token:
raise HTTPException( raise HTTPException(
@ -515,9 +515,9 @@ async def refresh_token(
refreshed_token = token_manager.refresh_token(current_token) refreshed_token = token_manager.refresh_token(current_token)
if refreshed_token: if refreshed_token:
# Save the new token and delete the old one # Save the new connection token and delete the old one
appInterface.saveToken(refreshed_token) appInterface.saveConnectionToken(refreshed_token)
appInterface.deleteTokenByConnectionId(msft_connection.id) appInterface.deleteConnectionTokenByConnectionId(msft_connection.id)
# Update the connection's expiration time # Update the connection's expiration time
msft_connection.expiresAt = float(refreshed_token.expiresAt) msft_connection.expiresAt = float(refreshed_token.expiresAt)

View file

@ -1,6 +1,12 @@
TODO TODO
- ui: Besseres Rendering der Tasks, Actions, Files (hierarchisch eingerückt) und der Log Entries ohne Rahmen
- ui: Beim Laden des Workflows die Logs und Messages synchron laden chronologisch
- documents: Sprechende Filenamen für user, ein Label für die interne Nutzung
- Chat: Pro Action und Task eine Message an den User in der UserLanguage
- check history --> tasks? - check history --> tasks?
- model reference diagram for all models. who uses who? --> to see the basic building blocks - model reference diagram for all models. who uses who? --> to see the basic building blocks