Skip to content

Commit 84fe389

Browse files
committed
add get_question_details() method
1 parent 750e6e9 commit 84fe389

File tree

6 files changed

+448
-0
lines changed

6 files changed

+448
-0
lines changed

examples/08_questions_management.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,91 @@ def list_questions_example(j1):
352352
except Exception as e:
353353
print(f"Error listing questions: {e}")
354354

355+
def get_question_details_example(j1):
356+
"""Demonstrate getting specific question details."""
357+
358+
print("=== Get Question Details Example ===\n")
359+
360+
print("First, let's get a list of questions to find one to examine:")
361+
try:
362+
questions = j1.list_questions()
363+
364+
if questions:
365+
# Get details of the first question
366+
first_question = questions[0]
367+
question_id = first_question['id']
368+
question_title = first_question['title']
369+
370+
print(f"Getting detailed information for question: {question_title}")
371+
print(f"Question ID: {question_id}")
372+
373+
# Get full question details
374+
question_details = j1.get_question_details(question_id=question_id)
375+
376+
print(f"\nDetailed Question Information:")
377+
print(f" Title: {question_details['title']}")
378+
print(f" ID: {question_details['id']}")
379+
print(f" Source ID: {question_details.get('sourceId', 'Not specified')}")
380+
print(f" Description: {question_details.get('description', 'No description')}")
381+
print(f" Tags: {', '.join(question_details.get('tags', []))}")
382+
print(f" Last Updated: {question_details.get('lastUpdatedTimestamp', 'Not specified')}")
383+
print(f" Account ID: {question_details.get('accountId', 'Not specified')}")
384+
print(f" Show Trend: {question_details.get('showTrend', False)}")
385+
print(f" Polling Interval: {question_details.get('pollingInterval', 'Not set')}")
386+
387+
# Display queries
388+
queries = question_details.get('queries', [])
389+
print(f"\n Queries ({len(queries)}):")
390+
for i, query in enumerate(queries):
391+
print(f" Query {i+1}: {query.get('name', 'Unnamed')}")
392+
print(f" - Query: {query.get('query', 'No query')}")
393+
print(f" - Version: {query.get('version', 'Not specified')}")
394+
print(f" - Results Are: {query.get('resultsAre', 'Not specified')}")
395+
396+
# Display compliance information
397+
compliance = question_details.get('compliance')
398+
if compliance:
399+
print(f"\n Compliance Information:")
400+
if isinstance(compliance, dict):
401+
print(f" Standard: {compliance.get('standard', 'Not specified')}")
402+
requirements = compliance.get('requirements', [])
403+
if requirements:
404+
print(f" Requirements: {', '.join(map(str, requirements))}")
405+
controls = compliance.get('controls', [])
406+
if controls:
407+
print(f" Controls: {', '.join(map(str, controls))}")
408+
else:
409+
print(f" Compliance data type: {type(compliance)}")
410+
print(f" Compliance content: {compliance}")
411+
else:
412+
print(f"\n Compliance Information: None")
413+
414+
# Display variables
415+
variables = question_details.get('variables', [])
416+
if variables:
417+
print(f"\n Variables ({len(variables)}):")
418+
for var in variables:
419+
print(f" - Name: {var.get('name', 'Unnamed')}")
420+
print(f" Required: {var.get('required', False)}")
421+
print(f" Default: {var.get('default', 'None')}")
422+
else:
423+
print(f"\n Variables: None")
424+
425+
# Display integration information
426+
integration_def_id = question_details.get('integrationDefinitionId')
427+
if integration_def_id:
428+
print(f"\n Integration Definition ID: {integration_def_id}")
429+
else:
430+
print(f"\n Integration Definition ID: None")
431+
432+
else:
433+
print("No questions found in the account to examine")
434+
435+
except Exception as e:
436+
print(f"Error getting question details: {e}")
437+
print(f"Error type: {type(e).__name__}")
438+
print(f"Error details: {str(e)}")
439+
355440
def question_use_cases(j1):
356441
"""Demonstrate real-world use cases for questions."""
357442

@@ -430,6 +515,9 @@ def main():
430515
time.sleep(1)
431516

432517
list_questions_example(j1)
518+
time.sleep(1)
519+
520+
get_question_details_example(j1)
433521

434522
question_use_cases(j1)
435523

examples/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ This directory contains comprehensive examples demonstrating how to use the Jupi
128128
**Key Methods Demonstrated**:
129129
- `create_question()` - Create questions with J1QL queries
130130
- `list_questions()` - List all questions in the account
131+
- `get_question_details()` - Get detailed information for a specific question by ID
131132

132133
### 9. **examples.py**
133134
**Purpose**: Comprehensive examples of all major SDK methods
@@ -141,6 +142,7 @@ This directory contains comprehensive examples demonstrating how to use the Jupi
141142
**Key Methods Demonstrated**:
142143
- All major SDK methods including:
143144
- `list_questions()` - List and analyze all questions in the account
145+
- `get_question_details()` - Get detailed information for specific questions
144146
- `create_question()` - Create questions with various configurations
145147
- Entity lifecycle management methods
146148
- Relationship management methods
@@ -301,6 +303,14 @@ security_compliance = j1.list_questions(
301303
tags=["security", "compliance"]
302304
)
303305

306+
# Get detailed information for a specific question
307+
question_details = j1.get_question_details(
308+
question_id="f90f9aa1-f9ff-47f7-ab34-ce8fa11c7add"
309+
)
310+
print(f"Question title: {question_details['title']}")
311+
print(f"Compliance standard: {question_details.get('compliance', {}).get('standard')}")
312+
print(f"Number of queries: {len(question_details.get('queries', []))}")
313+
304314
# Analyze by compliance standards
305315
compliance_standards = {}
306316
for question in questions:

examples/examples.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,4 +649,41 @@
649649
polling_questions = [q for q in list_questions_r if 'pollingInterval' in q and q['pollingInterval'] and q['pollingInterval'] != 'DISABLED']
650650
print(f" Questions with polling enabled: {len(polling_questions)}")
651651

652+
# get_question_details - get specific question details
653+
print("\nget_question_details() - Get specific question:")
654+
if list_questions_r:
655+
# Get details of the first question
656+
first_question_id = list_questions_r[0]['id']
657+
try:
658+
question_details = j1.get_question_details(question_id=first_question_id)
659+
print(f" Retrieved details for question: {question_details['title']}")
660+
print(f" Question ID: {question_details['id']}")
661+
print(f" Description: {question_details.get('description', 'No description')}")
662+
print(f" Tags: {question_details.get('tags', [])}")
663+
print(f" Number of queries: {len(question_details.get('queries', []))}")
664+
print(f" Show trend: {question_details.get('showTrend', False)}")
665+
print(f" Polling interval: {question_details.get('pollingInterval', 'Not set')}")
666+
667+
# Show compliance details if available
668+
if question_details.get('compliance'):
669+
compliance = question_details['compliance']
670+
if isinstance(compliance, dict):
671+
print(f" Compliance standard: {compliance.get('standard', 'Not specified')}")
672+
if 'requirements' in compliance:
673+
reqs = compliance['requirements']
674+
if isinstance(reqs, list):
675+
print(f" Compliance requirements: {', '.join(map(str, reqs))}")
676+
677+
# Show variables if available
678+
if question_details.get('variables'):
679+
variables = question_details['variables']
680+
print(f" Variables: {len(variables)}")
681+
for var in variables:
682+
print(f" - {var.get('name', 'Unnamed')}: required={var.get('required', False)}, default={var.get('default', 'None')}")
683+
684+
except Exception as e:
685+
print(f" Error getting question details: {e}")
686+
else:
687+
print(" No questions available to get details for")
688+
652689
print()

jupiterone/client.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
UPDATE_RULE_INSTANCE,
4444
EVALUATE_RULE_INSTANCE,
4545
QUESTIONS,
46+
GET_QUESTION,
4647
CREATE_QUESTION,
4748
COMPLIANCE_FRAMEWORK_ITEM,
4849
LIST_COLLECTION_RESULTS,
@@ -1318,6 +1319,33 @@ def list_questions(self, search_query: str = None, tags: List[str] = None):
13181319

13191320
return results
13201321

1322+
def get_question_details(self, question_id: str = None):
1323+
"""Get details of a specific question by ID
1324+
1325+
Args:
1326+
question_id (str): The unique ID of the question to retrieve
1327+
1328+
Returns:
1329+
Dict: The question object with all its details
1330+
1331+
Example:
1332+
question_details = j1_client.get_question_details(
1333+
question_id="f90f9aa1-f9ff-47f7-ab34-ce8fa11c7add"
1334+
)
1335+
1336+
Raises:
1337+
ValueError: If question_id is not provided
1338+
JupiterOneApiError: If the question is not found or other API errors occur
1339+
"""
1340+
if not question_id:
1341+
raise ValueError("question_id is required")
1342+
1343+
variables = {"id": question_id}
1344+
1345+
response = self._execute_query(GET_QUESTION, variables=variables)
1346+
1347+
return response["data"]["question"]
1348+
13211349
def create_question(
13221350
self,
13231351
title: str,

jupiterone/constants.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,48 @@
920920
__typename
921921
}
922922
"""
923+
924+
GET_QUESTION = """
925+
query question($id: ID!) {
926+
question(id: $id) {
927+
...QuestionFields
928+
__typename
929+
}
930+
}
931+
932+
fragment QuestionFields on Question {
933+
id
934+
sourceId
935+
title
936+
description
937+
tags
938+
lastUpdatedTimestamp
939+
queries {
940+
name
941+
query
942+
version
943+
resultsAre
944+
__typename
945+
}
946+
compliance {
947+
standard
948+
requirements
949+
controls
950+
__typename
951+
}
952+
variables {
953+
name
954+
required
955+
default
956+
__typename
957+
}
958+
accountId
959+
integrationDefinitionId
960+
showTrend
961+
pollingInterval
962+
__typename
963+
}
964+
"""
923965
CREATE_QUESTION = """
924966
mutation CreateQuestion($question: CreateQuestionInput!) {
925967
createQuestion(question: $question) {

0 commit comments

Comments
 (0)