[Python] script not being recognized as function as a function app

Discussão em 'Python' iniciado por Stack, Janeiro 15, 2025.

  1. Stack

    Stack Membro Participativo

    i had a function app that had a python script running in it i was deploying it from vs code and it was working fine , now i created a new resource and a new function and i redid the same configuration , now i tried to deploy the same folder to that function app and everything is in the app files but the function is not recognized as a function is in the function app i have all the necessary files in the directory so what can the issue be ?

    import os
    import logging
    import requests
    import azure.functions as func
    from azure.identity import DefaultAzureCredential
    from azure.keyvault.secrets import SecretClient
    from opencensus.ext.azure.log_exporter import AzureLogHandler

    # Global configuration
    EMAIL = os.getenv("EMAIL")
    KEY_VAULT_NAME = os.getenv("KEY_VAULT_NAME")
    SECRET_NAME = os.getenv("SECRET_NAME")
    ACTIVE_INSTANCE = os.getenv("ACTIVE_INSTANCE")
    LOGIC_APP_URL = os.getenv("LOGIC_APP_URL")

    # Instance-specific configuration
    BASE_URL = os.getenv("BASE_URL")
    WORKSPACE_ID = os.getenv("WORKSPACE_ID")
    OBJECT_TYPE_ID = os.getenv("OBJECT_TYPE_ID")
    PROJECT_ID_ATTR = os.getenv("PROJECT_ID_ATTR")
    PROJECT_KEY_ATTR = os.getenv("PROJECT_KEY_ATTR")
    PROJECT_NAME_ATTR = os.getenv("PROJECT_NAME_ATTR")
    PROJECT_DESCRIPTION_ATTR = os.getenv("PROJECT_DESCRIPTION_ATTR")
    PROJECT_CATEGORY_ATTR = os.getenv("PROJECT_CATEGORY_ATTR")
    PROJECT_TYPEKEY_ATTR = os.getenv("PROJECT_TYPEKEY_ATTR")
    PROJECT_LEAD_ATTR = os.getenv("PROJECT_LEAD_ATTR")
    PROJECT_LEADID_ATTR = os.getenv("PROJECT_LEADID_ATTR")
    PROJECT_SIMPLIFIED_ATTR = os.getenv("PROJECT_SIMPLIFIED_ATTR")
    PROJECT_STYLED_ATTR = os.getenv("PROJECT_STYLED_ATTR")
    PROJECT_ISPRIVATE_ATTR = os.getenv("PROJECT_ISPRIVATE_ATTR")
    PROJECT_TOTALISSUECOUNT_ATTR = os.getenv("PROJECT_TOTALISSUECOUNT_ATTR")
    PROJECT_LASTISSUEUPDATE_ATTR = os.getenv("PROJECT_LASTISSUEUPDATE_ATTR")
    PROJECT_ARCHIVEDDATE_ATTR = os.getenv("PROJECT_ARCHIVEDDATE_ATTR")

    # Setup logging
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.INFO)
    logger.addHandler(AzureLogHandler(connection_string='InstrumentationKey=42d4e759-407c-4774-8869-ef7343513438'))

    def get_secret_from_key_vault(key_vault_name, secret_name):
    """Retrieve a secret from Azure Key Vault."""
    kv_uri = f"https://{key_vault_name}.vault.azure.net"
    credential = DefaultAzureCredential()
    client = SecretClient(vault_url=kv_uri, credential=credential)
    secret = client.get_secret(secret_name)
    return secret.value



    def send_error_report(error_message):
    """Send an error report via Logic App."""
    payload = {
    "To": "example@email.com",
    "CC": "",
    "Response": "No",
    "Subject": "Jira Authentication Error",
    "Body": f"An error occurred during Jira authentication: {error_message}"
    }
    try:
    response = requests.post(LOGIC_APP_URL, json=payload)
    response.raise_for_status()
    logger.info("Error report sent successfully.")
    except requests.exceptions.RequestException as e:
    logger.error(f"Failed to send error report: {e}")


    def authenticate_jira(base_url, email, api_token):
    """Authenticate with the Jira Cloud instance."""
    session = requests.Session()
    session.auth = (email, api_token)
    session.headers.update({'Content-Type': 'application/json'})
    try:
    response = session.get(f"{base_url}/rest/api/3/myself")
    response.raise_for_status()
    logger.info("Authentication successful.")
    except requests.exceptions.RequestException as e:
    logger.error(f"Failed to authenticate: {e}")
    #send_error_report(str(e)) # Call the Logic App to send an error report
    raise e
    return session



    def get_jira_projects(session):
    """Fetch all Jira projects using the paginated API."""
    logger.info("Fetching Jira projects")
    url = f"{BASE_URL}/rest/api/3/project/search"
    params = {"expand": "description,lead,issueTypes,url,projectKeys,permissions,insight"}
    projects = []

    while url:
    try:
    response = session.get(url, params=params)
    if response.status_code != 200:
    logger.error(f"Error fetching Jira projects: {response.status_code} {response.text}")
    response.raise_for_status()
    data = response.json()
    projects.extend(data.get("values", []))
    url = data.get("nextPage") # Continue pagination
    except requests.exceptions.RequestException as e:
    logger.error(f"Error fetching Jira projects: {e}")
    break

    logger.info(f"Fetched {len(projects)} projects")
    for project in projects:
    logger.info(f"Fetched project: {project.get('id')} - {project.get('key')} - {project.get('name')}")

    return projects

    def search_asset_object(session, project_id):
    """Search for an asset object using AQL."""
    url = f"https://api.atlassian.com/jsm/assets/workspace/{WORKSPACE_ID}/v1/object/aql"
    query = {
    "qlQuery": f'objectType = "Jira Project" AND "Project ID" = "{project_id}"'
    }
    try:
    response = session.post(url, json=query)
    response.raise_for_status()
    return response.json().get("values", [])
    except requests.exceptions.RequestException as e:
    logger.error(f"Error searching asset object for project {project_id}: {e}")
    return []

    def create_asset_object(session, project_data):
    """Create a new asset object."""
    url = f"https://api.atlassian.com/jsm/assets/workspace/{WORKSPACE_ID}/v1/object/create"
    payload = format_payload(project_data)

    try:
    response = session.post(url, json=payload)
    response.raise_for_status()
    logger.info(f"Asset created for project {project_data['id']} ({project_data['key']})")
    except requests.exceptions.RequestException as e:
    logger.error(f"Error creating asset object for project {project_data['id']}: {e}")

    def update_asset_object(session, asset_id, project_data):
    """Update an existing asset object."""
    url = f"https://api.atlassian.com/jsm/assets/workspace/{WORKSPACE_ID}/v1/object/{asset_id}"
    payload = format_payload(project_data)

    try:
    response = session.put(url, json=payload)
    response.raise_for_status()
    logger.info(f"Asset updated for project {project_data['id']} ({project_data['key']})")
    except requests.exceptions.RequestException as e:
    logger.error(f"Error updating asset object {asset_id} for project {project_data['id']}: {e}")

    def format_payload(project):
    """Format payload for asset creation or update based on configuration."""
    last_issue_update_time = project.get("insight", {}).get("lastIssueUpdateTime")
    formatted_last_issue_update_time = None

    if last_issue_update_time:
    formatted_last_issue_update_time = last_issue_update_time.split("T")[0]

    description_long = project.get("description")
    shortened_description = None

    if description_long:
    shortened_description = description_long[:255]

    return {
    "objectTypeId": OBJECT_TYPE_ID,
    "attributes": [
    {"objectTypeAttributeId": PROJECT_ID_ATTR,
    "objectAttributeValues": [{"value": project.get("id")}]},
    {"objectTypeAttributeId": PROJECT_KEY_ATTR,
    "objectAttributeValues": [{"value": project.get("key")}]},
    {"objectTypeAttributeId": PROJECT_NAME_ATTR,
    "objectAttributeValues": [{"value": project.get("name")}]},
    {"objectTypeAttributeId": PROJECT_DESCRIPTION_ATTR,
    "objectAttributeValues": [{"value": shortened_description}]},
    {"objectTypeAttributeId": PROJECT_CATEGORY_ATTR,
    "objectAttributeValues": [{"value": project.get("projectCategory", {}).get("name")}]},
    {"objectTypeAttributeId": PROJECT_TYPEKEY_ATTR,
    "objectAttributeValues": [{"value": project.get("projectTypeKey")}]},
    {"objectTypeAttributeId": PROJECT_LEAD_ATTR,
    "objectAttributeValues": [{"value": project.get("lead", {}).get("accountId")}]},
    {"objectTypeAttributeId": PROJECT_LEADID_ATTR,
    "objectAttributeValues": [{"value": project.get("lead", {}).get("accountId")}]},
    {"objectTypeAttributeId": PROJECT_SIMPLIFIED_ATTR,
    "objectAttributeValues": [{"value": project.get("simplified")}]},
    {"objectTypeAttributeId": PROJECT_STYLED_ATTR,
    "objectAttributeValues": [{"value": project.get("style")}]},
    {"objectTypeAttributeId": PROJECT_ISPRIVATE_ATTR,
    "objectAttributeValues": [{"value": project.get("isPrivate")}]},
    {"objectTypeAttributeId": PROJECT_TOTALISSUECOUNT_ATTR,
    "objectAttributeValues": [{"value": project.get("insight", {}).get("totalIssueCount")}]},
    {"objectTypeAttributeId": PROJECT_LASTISSUEUPDATE_ATTR,
    "objectAttributeValues": [{"value": formatted_last_issue_update_time}]},
    {"objectTypeAttributeId": PROJECT_ARCHIVEDDATE_ATTR,
    "objectAttributeValues": [{"value": project.get("archivedDate")}]}
    ]
    }

    def sync_jira_main():
    """Main logic to process Jira projects and sync with assets."""
    logger.info("Starting Jira Projects to Asset synchronization")
    try:
    # Retrieve the API token from Key Vault
    API_TOKEN = get_secret_from_key_vault(KEY_VAULT_NAME, SECRET_NAME)
    except Exception as e:
    message = f"Error fetching API token: {e}"
    logger.error(message)
    raise Exception(message)
    try:
    # Authenticate and create a session
    session = authenticate_jira(BASE_URL, EMAIL, API_TOKEN)

    except Exception as e:
    send_error_report(str(e))
    message = f"Error fetching Jira projects: {e}"
    logger.error(message)
    raise Exception(message)


    projects = get_jira_projects(session)
    for project in projects:
    project_id = project.get("id")
    if not project_id:
    logger.warning("Project without ID encountered, skipping.")
    continue

    existing_assets = search_asset_object(session, project_id)

    if len(existing_assets) == 0:
    create_asset_object(session, project)
    elif len(existing_assets) > 1:
    logger.error(f"Multiple asset objects found for project {project_id} ({project.get('key')}).")
    else:
    update_asset_object(session, existing_assets[0].get("id"), project)

    logger.info("Jira Projects to Asset synchronization completed.")

    app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)

    @app.route(route="leanjira_script", methods=["GET", "POST"])
    def leansyncjira_script(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    # Call the sync_jira main function
    sync_jira_main()

    return func.HttpResponse("Synchronization completed.", status_code=200)

    Continue reading...

Compartilhe esta Página