Skip to content

Commit 09814e3

Browse files
authored
Merge pull request #60 from JupiterOne/1.8.0
1.8.0 release
2 parents 2f6563d + 06156f8 commit 09814e3

File tree

6 files changed

+231
-8
lines changed

6 files changed

+231
-8
lines changed

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -357,11 +357,17 @@ instance = j1.create_integration_instance(
357357

358358
```python
359359
# Start sync job for an integration instance
360-
sync_job = j1.start_sync_job(instance_id='<id-of-integration-instance>')
361-
print(f"Started sync job: {sync_job['job']['_id']}")
360+
sync_job = j1.start_sync_job(
361+
instance_id=instance_id,
362+
sync_mode="PATCH",
363+
source="integration-external"
364+
)
365+
366+
sync_job_id = sync_job['job'].get('id')
367+
print(f"Started sync job: {sync_job_id}")
362368

363369
# The returned job ID is used for subsequent operations
364-
job_id = sync_job['job']['_id']
370+
job_id = sync_job_id
365371
```
366372

367373
##### Upload Batch of Entities
@@ -514,7 +520,7 @@ print(f"Uploaded {len(combined_payload['entities'])} entities and {len(combined_
514520
```python
515521
# Finalize the sync job
516522
result = j1.finalize_sync_job(instance_job_id='<id-of-integration-sync-job>')
517-
print(f"Finalized sync job: {result['job']['_id']}")
523+
print(f"Finalized sync job: {result['job'].get('id')}")
518524

519525
# Check job status
520526
if result['job']['status'] == 'COMPLETED':

examples/04_integration_management.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def sync_job_examples(j1, instance_id):
149149
try:
150150
sync_job = j1.start_sync_job(
151151
instance_id=instance_id,
152-
sync_mode="CREATE_OR_UPDATE",
152+
sync_mode="PATCH",
153153
source="api"
154154
)
155155
job_id = sync_job['job']['_id']

examples/bulk_upload.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from jupiterone.client import JupiterOneClient
2+
import os
3+
4+
account = os.environ.get("JUPITERONE_ACCOUNT")
5+
token = os.environ.get("JUPITERONE_TOKEN")
6+
url = "https://graphql.us.jupiterone.io"
7+
8+
j1 = JupiterOneClient(account=account, token=token, url=url)
9+
10+
instance_id = "e7113c37-1ea8-4d00-9b82-c24952e70916"
11+
12+
sync_job = j1.start_sync_job(
13+
instance_id=instance_id,
14+
sync_mode="PATCH",
15+
source="integration-external"
16+
)
17+
18+
print(sync_job)
19+
sync_job_id = sync_job['job'].get('id')
20+
21+
# Prepare entities payload
22+
entities_payload = [
23+
{
24+
"_key": "server-001",
25+
"_type": "aws_ec2_instance",
26+
"_class": "Host",
27+
"displayName": "web-server-001",
28+
"instanceId": "i-1234567890abcdef0",
29+
"instanceType": "t3.micro",
30+
"state": "running",
31+
"tag.Environment": "production",
32+
"tag.Team": "engineering"
33+
},
34+
{
35+
"_key": "server-002",
36+
"_type": "aws_ec2_instance",
37+
"_class": "Host",
38+
"displayName": "web-server-002",
39+
"instanceId": "i-0987654321fedcba0",
40+
"instanceType": "t3.small",
41+
"state": "running",
42+
"tag.Environment": "staging",
43+
"tag.Team": "engineering"
44+
},
45+
{
46+
"_key": "database-001",
47+
"_type": "aws_rds_instance",
48+
"_class": "Database",
49+
"displayName": "prod-database",
50+
"dbInstanceIdentifier": "prod-db",
51+
"engine": "postgres",
52+
"dbInstanceClass": "db.t3.micro",
53+
"tag.Environment": "production",
54+
"tag.Team": "data"
55+
}
56+
]
57+
58+
# Upload entities batch
59+
result = j1.upload_entities_batch_json(
60+
instance_job_id=sync_job_id,
61+
entities_list=entities_payload
62+
)
63+
print(f"Uploaded {len(entities_payload)} entities")
64+
print(result)
65+
66+
# Finalize the sync job
67+
result = j1.finalize_sync_job(instance_job_id=sync_job_id)
68+
print(f"Finalized sync job: {result['job']['id']}")
69+

examples/examples.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@
124124
integration_instance_id = "<GUID>"
125125

126126
# start_sync_job
127-
# sync_mode can be "DIFF", "CREATE_OR_UPDATE", or "PATCH"
127+
# sync_mode can be "DIFF" or "PATCH"
128128
start_sync_job_r = j1.start_sync_job(instance_id=integration_instance_id,
129-
sync_mode='CREATE_OR_UPDATE',
129+
sync_mode='PATCH',
130130
source='integration-external')
131131
print("start_sync_job()")
132132
print(start_sync_job_r)

examples/sync_job_workflow.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#!/usr/bin/env python3
2+
"""
3+
JupiterOne Sync Job Workflow Example
4+
5+
This example demonstrates the core synchronization job workflow:
6+
1. Start a sync job
7+
2. Upload entities batch
8+
3. Finalize the sync job
9+
10+
This is a standalone script that can be run independently to test
11+
the sync job functionality.
12+
13+
Note: This example uses PATCH sync mode, which is suitable for entity-only
14+
uploads. If you need to upload relationships, use DIFF sync mode instead.
15+
See: https://docs.jupiterone.io/reference/pipeline-upgrade#patch-sync-jobs-cannot-target-relationships
16+
"""
17+
18+
import os
19+
import sys
20+
from jupiterone.client import JupiterOneClient
21+
22+
def main():
23+
"""Main function to demonstrate sync job workflow."""
24+
25+
# Initialize JupiterOne client
26+
# You can set these as environment variables or replace with your values
27+
api_token = os.getenv('JUPITERONE_API_TOKEN')
28+
account_id = os.getenv('JUPITERONE_ACCOUNT_ID')
29+
30+
if not api_token or not account_id:
31+
print("Error: Please set JUPITERONE_API_TOKEN and JUPITERONE_ACCOUNT_ID environment variables")
32+
print("Example:")
33+
print(" export JUPITERONE_API_TOKEN='your-api-token'")
34+
print(" export JUPITERONE_ACCOUNT_ID='your-account-id'")
35+
sys.exit(1)
36+
37+
# Create JupiterOne client
38+
j1 = JupiterOneClient(token=api_token, account=account_id)
39+
40+
print("=== JupiterOne Sync Job Workflow Example ===\n")
41+
42+
# You'll need to replace this with an actual integration instance ID
43+
instance_id = os.getenv('JUPITERONE_INSTANCE_ID')
44+
if not instance_id:
45+
print("Error: Please set JUPITERONE_INSTANCE_ID environment variable")
46+
print("Example:")
47+
print(" export JUPITERONE_INSTANCE_ID='your-integration-instance-id'")
48+
sys.exit(1)
49+
50+
try:
51+
# Step 1: Start sync job
52+
print("1. Starting synchronization job...")
53+
print(" Note: Using PATCH mode (entities only). Use DIFF mode if uploading relationships.")
54+
sync_job = j1.start_sync_job(
55+
instance_id=instance_id,
56+
sync_mode="PATCH",
57+
source="integration-external"
58+
)
59+
60+
sync_job_id = sync_job['job'].get('id')
61+
print(f"✓ Started sync job: {sync_job_id}")
62+
print(f" Status: {sync_job['job']['status']}")
63+
print()
64+
65+
# Step 2: Upload entities batch
66+
print("2. Uploading entities batch...")
67+
68+
# Sample entities payload
69+
entities_payload = [
70+
{
71+
"_key": "example-server-001",
72+
"_type": "example_server",
73+
"_class": "Host",
74+
"displayName": "Example Server 001",
75+
"hostname": "server-001.example.com",
76+
"ipAddress": "192.168.1.100",
77+
"operatingSystem": "Linux",
78+
"tag.Environment": "development",
79+
"tag.Team": "engineering",
80+
"tag.Purpose": "web_server"
81+
},
82+
{
83+
"_key": "example-server-002",
84+
"_type": "example_server",
85+
"_class": "Host",
86+
"displayName": "Example Server 002",
87+
"hostname": "server-002.example.com",
88+
"ipAddress": "192.168.1.101",
89+
"operatingSystem": "Linux",
90+
"tag.Environment": "staging",
91+
"tag.Team": "engineering",
92+
"tag.Purpose": "database_server"
93+
},
94+
{
95+
"_key": "example-database-001",
96+
"_type": "example_database",
97+
"_class": "Database",
98+
"displayName": "Example Database 001",
99+
"databaseName": "app_db",
100+
"engine": "postgresql",
101+
"version": "13.4",
102+
"tag.Environment": "development",
103+
"tag.Team": "data"
104+
}
105+
]
106+
107+
# Upload entities
108+
upload_result = j1.upload_entities_batch_json(
109+
instance_job_id=sync_job_id,
110+
entities_list=entities_payload
111+
)
112+
print(f"✓ Uploaded {len(entities_payload)} entities successfully")
113+
print(f" Upload result: {upload_result}")
114+
print()
115+
116+
# Step 3: Finalize sync job
117+
print("3. Finalizing synchronization job...")
118+
finalize_result = j1.finalize_sync_job(instance_job_id=sync_job_id)
119+
120+
finalize_job_id = finalize_result['job'].get('id')
121+
print(f"✓ Finalized sync job: {finalize_job_id}")
122+
print(f" Status: {finalize_result['job']['status']}")
123+
124+
# Check final status
125+
if finalize_result['job']['status'] == 'COMPLETED':
126+
print("✓ Sync job completed successfully!")
127+
elif finalize_result['job']['status'] == 'FAILED':
128+
error_msg = finalize_result['job'].get('error', 'Unknown error')
129+
print(f"✗ Sync job failed: {error_msg}")
130+
else:
131+
print(f"ℹ Sync job status: {finalize_result['job']['status']}")
132+
133+
print("\n=== Sync Job Workflow Complete ===")
134+
135+
except Exception as e:
136+
print(f"✗ Error during sync job workflow: {e}")
137+
sys.exit(1)
138+
139+
if __name__ == "__main__":
140+
main()
141+

jupiterone/client.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,8 +662,15 @@ def start_sync_job(
662662
663663
args:
664664
instance_id (str): The "integrationInstanceId" request param for synchronization job
665-
sync_mode (str): The "syncMode" request body property for synchronization job. "DIFF", "CREATE_OR_UPDATE", or "PATCH"
665+
sync_mode (str): The "syncMode" request body property for synchronization job. "DIFF" or "PATCH"
666666
source (str): The "source" request body property for synchronization job. "api" or "integration-external"
667+
668+
Note:
669+
IMPORTANT: PATCH sync jobs cannot target relationships. If your sync job involves creating
670+
or updating relationships, you must use "DIFF" sync_mode instead of "PATCH". This is due
671+
to the JupiterOne data pipeline upgrade.
672+
673+
For more information, see: https://docs.jupiterone.io/reference/pipeline-upgrade#patch-sync-jobs-cannot-target-relationships
667674
"""
668675
endpoint = "/persister/synchronization/jobs"
669676

0 commit comments

Comments
 (0)