Skip to content

Add comprehensive question/insight management methods to Python API client #53

@egallis31

Description

@egallis31

Add comprehensive question/insight management methods to Python API client

Description

The JupiterOne Python API client lacks built-in methods for question (insight) operations, forcing users to write direct GraphQL mutations for basic question management tasks. While the GraphQL API supports comprehensive question operations, the Python client does not expose these capabilities through convenient methods.

Current Behavior

The Python API client currently has no built-in question/insight management methods. Users must implement all question operations using direct GraphQL mutations.

Current workaround:

def delete_question(question_id: str) -> None:
    """
    Helper function to delete a question.
    """
    delete_question_mutation = """
    mutation DeleteQuestion($id: ID!) {
        deleteQuestion(id: $id) {
            id
            title
            description
            queries {
                query
                name
                version
                __typename
            }
            compliance {
                standard
                requirements
                controls
                __typename
            }
            variables {
                name
                required
                default
                __typename
            }
            tags
            accountId
            integrationDefinitionId
            showTrend
            pollingInterval
            __typename
        }
    }
    """
    
    delete_variables = {"id": question_id}
    delete_result = make_graphql_request(delete_question_mutation, delete_variables, "DeleteQuestion")
    
    assert delete_result['data']['deleteQuestion']['id'] == question_id, "Question deletion failed."

Users must also manually implement create, update, and list operations using similar complex GraphQL patterns.

Expected Behavior

The Python API client should provide comprehensive question management methods that match the capabilities of the underlying GraphQL API.

Missing Methods

The following methods should be added to the JupiterOneClient class:

1. create_question(title, queries, resource_group_id=None, **kwargs)

question = j1_client.create_question(
    title="Security Compliance Check",
    queries=[{
        "query": "FIND Host WITH open=true",
        "name": "OpenHosts",
        "version": "v1",
        "resultsAre": "INFORMATIVE"
    }],
    resource_group_id="resource-group-id",  # Optional
    description="Check for open hosts",
    tags=["security", "compliance"]
)

2. update_question(question_id, title=None, queries=None, **kwargs)

updated_question = j1_client.update_question(
    question_id="question-123",
    title="Updated Security Check",
    queries=[{
        "query": "FIND Host WITH open=true AND severity='HIGH'",
        "name": "CriticalOpenHosts",
        "version": "v1"
    }]
)

3. delete_question(question_id)

success = j1_client.delete_question(question_id="question-123")

4. list_questions(resource_group_id=None, tags=None)

# List all questions
all_questions = j1_client.list_questions()

# List questions in specific resource group
rg_questions = j1_client.list_questions(resource_group_id="resource-group-id")

# List questions with specific tags
tagged_questions = j1_client.list_questions(tags=["security", "compliance"])

5. get_question_details(question_id)

question_details = j1_client.get_question_details(question_id="question-123")

6. execute_question(question_id)

results = j1_client.execute_question(question_id="question-123")

Code Example

Current state (manual GraphQL required):

# Users must write complex direct GraphQL mutations
create_question_mutation = """
mutation CreateQuestion($question: CreateQuestionInput!) {
    createQuestion(question: $question) {
        id
        title
        description
        queries {
            name
            query
            version
            resultsAre
            __typename
        }
        compliance {
            standard
            requirements
            controls
            __typename
        }
        variables {
            name
            required
            default
            __typename
        }
        accountId
        integrationDefinitionId
        showTrend
        pollingInterval
        lastUpdatedTimestamp
        __typename
    }
}
"""

variables = {
    "question": {
        "queries": [
            {
                "query": "FIND jupiterone_account as a return a._accountId",
                "resultsAre": "INFORMATIVE",
                "name": "Account"
            }
        ],
        "title": "TestQuestion"
    }
}

# Manual GraphQL request handling
result = make_graphql_request(create_question_mutation, variables, "CreateQuestion")
question_id = result['data']['createQuestion']['id']

Desired API experience:

# What we want to be able to do
question = j1_client.create_question(
    title="Security Insights",
    queries=[{
        "query": "FIND Host WITH vulnerabilities!=undefined",
        "name": "VulnerableHosts",
        "version": "v1",
        "resultsAre": "BAD"
    }],
    resource_group_id="security-rg",
    description="Track vulnerable hosts",
    tags=["security", "vulnerabilities"]
)

# List and manage questions easily
questions = j1_client.list_questions(tags=["security"])
question_details = j1_client.get_question_details(question['id'])

# Execute and get results
results = j1_client.execute_question(question['id'])

# Update and delete
j1_client.update_question(question['id'], title="Updated Security Insights")
j1_client.delete_question(question['id'])

Consistency with Other Methods

Other resource management operations in the client follow consistent patterns:

# Alert rules have comprehensive management
alerts = j1_client.list_alert_rules()
alert = j1_client.create_alert_rule(name="Alert", j1ql="FIND Host", ...)
j1_client.update_alert_rule(rule_id="123", ...)
j1_client.delete_alert_rule(rule_id="123")

# Integration instances have partial management
instances = j1_client.fetch_integration_instances()
integration = j1_client.create_integration_instance(...)

# Questions have NO management methods (this issue)

Impact

This limitation affects:

  • Developer Experience: Users must write and maintain complex GraphQL code for question operations
  • Code Maintainability: Direct GraphQL mutations with complex nested structures are error-prone
  • API Consistency: Question operations don't follow the same patterns as other resources
  • Resource Group Integration: No built-in support for resource group assignment/filtering
  • Insight Workflows: Automated insight creation and management becomes cumbersome
  • Compliance Reporting: Difficult to programmatically manage compliance questions

Proposed Solution

Add comprehensive question management methods to the JupiterOneClient class following the established patterns from other resource methods.

Environment

  • JupiterOne Python API Client: v1.5.0
  • Python: 3.8+

Additional Context

This issue was identified during automated testing where question/insight lifecycle management is required for compliance reporting and security insights. The current approach of writing custom GraphQL mutations creates significant maintenance overhead due to the complexity of question objects.

Questions/insights are commonly used in:

  • Automated compliance reporting
  • Security posture assessments
  • Custom insight dashboards
  • Regulatory requirement tracking
  • CI/CD pipeline security gates

The complex nested structure of questions (with queries, compliance mappings, variables, etc.) makes this a high-value addition to the API client.

Related Issues

  • Dashboard management methods (similar pattern needed)
  • Integration instance lifecycle management methods (similar pattern needed)
  • Resource group parameter support across all creation methods

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions