running MVP2
This commit is contained in:
parent
1b59810e0d
commit
2a02bb1c01
12 changed files with 199 additions and 110 deletions
|
|
@ -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)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 = {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue