Skip to content

Commit 14657ef

Browse files
committed
add delete_question()
1 parent c913715 commit 14657ef

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

examples/08_questions_management.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,44 @@ def update_question_examples(j1):
628628
except Exception as e:
629629
print(f" ❌ Error updating variables: {e}\n")
630630

631+
def delete_question_examples(j1):
632+
"""Demonstrate deleting existing questions."""
633+
634+
print("=== Delete Question Examples ===\n")
635+
636+
# Get question ID from user input
637+
print("1. Enter the question ID to delete:")
638+
print(" (You can find question IDs by running the list_questions_example first)")
639+
print()
640+
641+
# For demonstration purposes, use a placeholder ID
642+
# In a real application, you would use: question_id = input("Enter question ID: ")
643+
question_id = "your-question-id-here" # Replace with actual question ID
644+
645+
if question_id == "your-question-id-here":
646+
print(" ⚠️ Please replace 'your-question-id-here' with an actual question ID")
647+
print(" Example: question_id = 'fcc0507d-0473-43a2-b083-9d5571b92ae7'")
648+
print()
649+
return
650+
651+
print(f" Question ID to delete: {question_id}")
652+
print()
653+
654+
# Delete the question
655+
print("2. Deleting the question:")
656+
try:
657+
deleted_question = j1.delete_question(question_id=question_id)
658+
659+
print(f" ✅ Successfully deleted question!")
660+
print(f" Deleted question title: {deleted_question['title']}")
661+
print(f" Deleted question ID: {deleted_question['id']}")
662+
print(f" Number of queries in deleted question: {len(deleted_question['queries'])}")
663+
print()
664+
665+
except Exception as e:
666+
print(f" ❌ Error deleting question: {e}")
667+
print()
668+
631669
def question_use_cases(j1):
632670
"""Demonstrate real-world use cases for questions."""
633671

@@ -719,6 +757,40 @@ def question_use_cases(j1):
719757
}
720758
)
721759
""")
760+
761+
# Use Case 5: Question Deletion and Cleanup
762+
print("\nUse Case 5: Question Deletion and Cleanup")
763+
print("-" * 50)
764+
print("Delete questions that are no longer needed:")
765+
print("""
766+
# Delete a single question
767+
deleted_question = j1.delete_question(
768+
question_id="question-id-to-delete"
769+
)
770+
print(f"Deleted: {deleted_question['title']}")
771+
772+
# Batch delete deprecated questions
773+
deprecated_questions = j1.list_questions(tags=["deprecated"])
774+
for question in deprecated_questions:
775+
try:
776+
deleted = j1.delete_question(question_id=question['id'])
777+
print(f"Deleted deprecated question: {deleted['title']}")
778+
except Exception as e:
779+
print(f"Failed to delete {question['title']}: {e}")
780+
781+
# Safe deletion with backup
782+
question_to_delete = j1.get_question_details(question_id="question-id")
783+
backup_question = j1.create_question(
784+
title=f"{question_to_delete['title']} - BACKUP",
785+
queries=question_to_delete['queries'],
786+
description=f"Backup before deletion: {question_to_delete.get('description', '')}",
787+
tags=question_to_delete.get('tags', []) + ["backup"]
788+
)
789+
790+
# Now delete the original
791+
j1.delete_question(question_id="question-id")
792+
print("Original question deleted, backup preserved")
793+
""")
722794

723795
def main():
724796
"""Run all question management examples."""
@@ -750,6 +822,9 @@ def main():
750822
update_question_examples(j1)
751823
time.sleep(1)
752824

825+
delete_question_examples(j1)
826+
time.sleep(1)
827+
753828
question_use_cases(j1)
754829

755830
print("\n" + "=" * 50)

jupiterone/client.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
UPDATE_ENTITYV2,
6060
INVOKE_INTEGRATION_INSTANCE,
6161
UPDATE_QUESTION,
62+
DELETE_QUESTION,
6263
)
6364

6465
class JupiterOneClient:
@@ -1542,6 +1543,46 @@ def update_question(
15421543
response = self._execute_query(UPDATE_QUESTION, variables)
15431544
return response["data"]["updateQuestion"]
15441545

1546+
def delete_question(self, question_id: str) -> Dict:
1547+
"""
1548+
Delete an existing question from the J1 account.
1549+
1550+
Args:
1551+
question_id (str): The unique ID of the question to delete (required)
1552+
1553+
Returns:
1554+
Dict: The deleted question object with all its details
1555+
1556+
Raises:
1557+
ValueError: If question_id is not provided
1558+
JupiterOneApiError: If the question deletion fails or other API errors occur
1559+
1560+
Example:
1561+
# Delete a question by ID
1562+
deleted_question = j1_client.delete_question(
1563+
question_id="fcc0507d-0473-43a2-b083-9d5571b92ae7"
1564+
)
1565+
1566+
print(f"Question '{deleted_question['title']}' has been deleted")
1567+
print(f"Deleted question ID: {deleted_question['id']}")
1568+
print(f"Number of queries in deleted question: {len(deleted_question['queries'])}")
1569+
1570+
# Access other deleted question details
1571+
if deleted_question.get('compliance'):
1572+
print(f"Compliance standard: {deleted_question['compliance']['standard']}")
1573+
1574+
if deleted_question.get('tags'):
1575+
print(f"Tags: {', '.join(deleted_question['tags'])}")
1576+
"""
1577+
if not question_id:
1578+
raise ValueError("question_id is required")
1579+
1580+
# Execute the GraphQL mutation
1581+
variables = {"id": question_id}
1582+
1583+
response = self._execute_query(DELETE_QUESTION, variables)
1584+
return response["data"]["deleteQuestion"]
1585+
15451586
def get_compliance_framework_item_details(self, item_id: str = None):
15461587
"""Fetch Details of a Compliance Framework Requirement configured in J1 account"""
15471588
variables = {"input": {"id": item_id}}

jupiterone/constants.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,3 +1370,37 @@
13701370
__typename
13711371
}
13721372
"""
1373+
1374+
DELETE_QUESTION = """
1375+
mutation DeleteQuestion($id: ID!) {
1376+
deleteQuestion(id: $id) {
1377+
id
1378+
title
1379+
description
1380+
queries {
1381+
query
1382+
name
1383+
version
1384+
__typename
1385+
}
1386+
compliance {
1387+
standard
1388+
requirements
1389+
controls
1390+
__typename
1391+
}
1392+
variables {
1393+
name
1394+
required
1395+
default
1396+
__typename
1397+
}
1398+
tags
1399+
accountId
1400+
integrationDefinitionId
1401+
showTrend
1402+
pollingInterval
1403+
__typename
1404+
}
1405+
}
1406+
"""

0 commit comments

Comments
 (0)