@@ -41,7 +41,29 @@ the default of "https://api.us.jupiterone.io" is used.
4141
4242## Method Examples:
4343
44- ### * See the examples/examples.py for full usage example documentation
44+ ### * See the examples/ directory for comprehensive usage examples:
45+
46+ #### ** Core API Examples**
47+ - ` examples/01_client_setup_and_queries.py ` - Client setup and basic J1QL queries
48+ - ` examples/02_entity_management.py ` - Entity creation, updates, and deletion
49+ - ` examples/03_relationship_management.py ` - Relationship management and traversal
50+ - ` examples/examples.py ` - General API usage examples and patterns
51+
52+ #### ** Integration & Management**
53+ - ` examples/04_integration_management.py ` - Integration instance management and sync jobs
54+ - ` examples/05_alert_rules_and_smartclasses.py ` - Alert rules and SmartClass operations
55+ - ` examples/06_advanced_operations.py ` - Advanced API operations and workflows
56+
57+ #### ** Specialized Features**
58+ - ` examples/07_account_parameters_list_example.py ` - Account parameter management
59+ - ` examples/08_questions_management.py ` - Complete question management workflows
60+ - ` examples/09_custom_file_transfer_example.py ` - Custom File Transfer (CFT) integration examples
61+ - ` examples/create_integration_instance_example.py ` - Integration instance creation examples
62+
63+ #### ** Utility & Data Examples**
64+ - ` examples/J1QLdeferredResponse.py ` - Deferred response query handling
65+ - ` examples/customFileTransferUploadData.py ` - Custom file transfer data examples
66+ - ` examples/examples2.py ` - Additional example patterns
4567
4668##### Execute a query:
4769
@@ -162,10 +184,17 @@ j1.update_entity(
162184# Delete by entity ID
163185j1.delete_entity(entity_id = ' <id-of-entity-to-delete>' )
164186
165- # Delete with timestamp
187+ # Delete with timestamp and hard delete option
166188j1.delete_entity(
167189 entity_id = ' <id-of-entity-to-delete>' ,
168- timestamp = int (time.time()) * 1000
190+ timestamp = int (time.time()) * 1000 ,
191+ hard_delete = True # Set to False for soft delete
192+ )
193+
194+ # Soft delete (entity marked as deleted but not permanently removed)
195+ j1.delete_entity(
196+ entity_id = ' <id-of-entity-to-delete>' ,
197+ hard_delete = False
169198)
170199```
171200
@@ -494,6 +523,63 @@ elif result['job']['status'] == 'FAILED':
494523 print (f " Sync job failed: { result[' job' ].get(' error' , ' Unknown error' )} " )
495524```
496525
526+ ##### Custom File Transfer (CFT) Integration Methods
527+
528+ ``` python
529+ # Get a pre-signed URL for file upload
530+ upload_info = j1.get_cft_upload_url(
531+ integration_instance_id = ' <id-of-integration-instance>' ,
532+ filename = ' data.csv' ,
533+ dataset_id = ' <id-of-dataset>'
534+ )
535+ print (f " Upload URL: { upload_info[' uploadUrl' ]} " )
536+ print (f " Expires at: { upload_info[' expiresAt' ]} " )
537+
538+ # Upload a CSV file to the CFT integration
539+ upload_result = j1.upload_cft_file(
540+ upload_url = upload_info[' uploadUrl' ],
541+ file_path = ' /path/to/your/data.csv'
542+ )
543+ print (f " Upload status: { upload_result[' status_code' ]} " )
544+ print (f " Upload success: { upload_result[' success' ]} " )
545+
546+ # Invoke the CFT integration to process the uploaded file
547+ invoke_result = j1.invoke_cft_integration(
548+ integration_instance_id = ' <id-of-integration-instance>'
549+ )
550+ if invoke_result is True :
551+ print (" CFT integration invoked successfully" )
552+ elif invoke_result == ' ALREADY_RUNNING' :
553+ print (" CFT integration is already running" )
554+ else :
555+ print (" Failed to invoke CFT integration" )
556+
557+ # Complete workflow example
558+ def upload_and_process_data (j1 , instance_id , dataset_id , file_path ):
559+ """ Complete workflow for CFT data upload and processing"""
560+ try :
561+ # Step 1: Get upload URL
562+ upload_info = j1.get_cft_upload_url(instance_id, ' data.csv' , dataset_id)
563+
564+ # Step 2: Upload file
565+ upload_result = j1.upload_cft_file(upload_info[' uploadUrl' ], file_path)
566+ if not upload_result[' success' ]:
567+ raise Exception (f " Upload failed: { upload_result[' status_code' ]} " )
568+
569+ # Step 3: Invoke processing
570+ invoke_result = j1.invoke_cft_integration(instance_id)
571+ if invoke_result is True :
572+ print (" Data uploaded and processing started successfully" )
573+ else :
574+ print (f " Processing status: { invoke_result} " )
575+
576+ except Exception as e:
577+ print (f " Error in CFT workflow: { e} " )
578+
579+ # Usage
580+ upload_and_process_data(j1, ' instance-123' , ' dataset-456' , ' /path/to/data.csv' )
581+ ```
582+
497583##### Fetch Integration Instance Jobs
498584
499585``` python
@@ -618,6 +704,135 @@ for prompt in complex_prompts:
618704 print (" ---" )
619705```
620706
707+ ##### Question Management Methods
708+
709+ ``` python
710+ # Create a new question
711+ question = j1.create_question(
712+ title = " Security Compliance Check" ,
713+ queries = [
714+ {
715+ " query" : " FIND User WITH mfaEnabled=false" ,
716+ " name" : " UsersWithoutMFA" ,
717+ " resultsAre" : " BAD"
718+ },
719+ {
720+ " query" : " FIND Host WITH encrypted=false" ,
721+ " name" : " UnencryptedHosts" ,
722+ " resultsAre" : " BAD"
723+ }
724+ ],
725+ description = " Check for security compliance violations" ,
726+ tags = [" security" , " compliance" ],
727+ showTrend = True ,
728+ pollingInterval = " ONE_DAY"
729+ )
730+ print (f " Created question: { question[' title' ]} (ID: { question[' id' ]} ) " )
731+
732+ # List existing questions
733+ questions = j1.list_questions()
734+ print (f " Found { len (questions)} questions " )
735+
736+ # Search for specific questions
737+ security_questions = j1.list_questions(search_query = " security" )
738+ compliance_questions = j1.list_questions(tags = [" compliance" ])
739+
740+ # Get question details
741+ question_details = j1.get_question_details(question_id = question[' id' ])
742+ print (f " Question: { question_details[' title' ]} " )
743+ print (f " Description: { question_details[' description' ]} " )
744+ print (f " Queries: { len (question_details[' queries' ])} " )
745+
746+ # Update an existing question
747+ updated_question = j1.update_question(
748+ question_id = question[' id' ],
749+ title = " Updated Security Compliance Check" ,
750+ description = " Enhanced security compliance monitoring" ,
751+ tags = [" security" , " compliance" , " enhanced" ]
752+ )
753+ print (f " Updated question: { updated_question[' title' ]} " )
754+
755+ # Update specific fields only
756+ j1.update_question(
757+ question_id = question[' id' ],
758+ description = " Updated description only"
759+ )
760+
761+ # Update queries with validation
762+ updated_queries = [
763+ {
764+ " query" : " FIND User WITH mfaEnabled=false AND active=true" ,
765+ " name" : " ActiveUsersWithoutMFA" ,
766+ " resultsAre" : " BAD"
767+ }
768+ ]
769+ j1.update_question(
770+ question_id = question[' id' ],
771+ queries = updated_queries
772+ )
773+
774+ # Delete a question
775+ deleted_question = j1.delete_question(question_id = question[' id' ])
776+ print (f " Deleted question: { deleted_question[' title' ]} " )
777+
778+ # Complete workflow example
779+ def manage_security_questions (j1 ):
780+ """ Complete workflow for managing security questions"""
781+ try :
782+ # Create a comprehensive security question
783+ security_question = j1.create_question(
784+ title = " Production Security Audit" ,
785+ queries = [
786+ {
787+ " query" : " FIND Host WITH tag.Environment='production' AND encrypted=false" ,
788+ " name" : " UnencryptedProdHosts" ,
789+ " resultsAre" : " BAD"
790+ },
791+ {
792+ " query" : " FIND User WITH privileged=true AND lastLoginOn < date.now - 90 days" ,
793+ " name" : " InactivePrivilegedUsers" ,
794+ " resultsAre" : " BAD"
795+ }
796+ ],
797+ description = " Comprehensive production security audit" ,
798+ tags = [" security" , " production" , " audit" ],
799+ showTrend = True ,
800+ pollingInterval = " ONE_DAY"
801+ )
802+
803+ print (f " Created security question: { security_question[' title' ]} " )
804+
805+ # Update the question with additional queries
806+ additional_queries = [
807+ {
808+ " query" : " FIND Database WITH backupEnabled=false" ,
809+ " name" : " DatabasesWithoutBackup" ,
810+ " resultsAre" : " BAD"
811+ }
812+ ]
813+
814+ updated_question = j1.update_question(
815+ question_id = security_question[' id' ],
816+ queries = additional_queries
817+ )
818+
819+ print (f " Updated question with additional queries " )
820+
821+ # List all security questions
822+ all_security_questions = j1.list_questions(tags = [" security" ])
823+ print (f " Total security questions: { len (all_security_questions)} " )
824+
825+ # Clean up - delete the test question
826+ j1.delete_question(question_id = security_question[' id' ])
827+ print (" Test question cleaned up" )
828+
829+ except Exception as e:
830+ print (f " Error in security question workflow: { e} " )
831+
832+ # Usage
833+ manage_security_questions(j1)
834+ ```
835+
621836##### List Alert Rules
622837
623838``` python
0 commit comments