Zum Inhalt springen

Ihre erste Änderung, von Anfang bis Ende

Diese Seite ist ein einziges lineares Skript, von einem frischen Access Token bis zu einer Änderung, die Sie in der Prevu3D Platform sehen können, und wieder zurück. Sie verwendet durchgehend den Client Credentials-Flow, und jede benötigte ID stammt aus einer API-Antwort weiter oben im selben Skript: nichts muss von Hand eingetragen werden.


  1. Ein Access Token abrufen: Authentifizierung mit Client Credentials
  2. Ihre Organisation ermitteln: /oauth/api-info aufrufen, um Ihre API-URL und Organisations-ID zu erhalten
  3. Zu einer Division navigieren: dem ersten Kind Ihrer Organisation
  4. Zu einer Site navigieren: dem ersten Kind dieser Division
  5. Die Site umbenennen: eine kleine, offensichtliche, umkehrbare Änderung
  6. In der Prevu3D Platform bestätigen: die Ziellinie
  7. Die Umbenennung rückgängig machen: keine Spuren hinterlassen
  • Schließen Sie zuerst den Leitfaden Client-Credentials-Flow ab. Ihre OAuth-Anwendung benötigt die Scopes read:hierarchy und write:hierarchy, und Ihr Service-Benutzer benötigt Inhaltszugriff sowie eine Rolle auf Bearbeitungsebene für mindestens eine Site (siehe Sicherheitsmodell).
  • Ihre Organisation benötigt mindestens eine Division, die mindestens eine Site enthält. Dieser Durchlauf navigiert zur ersten gefundenen.

Endpunkt: POST https://cloud-api.prevu3d.com/oauth/token

POST https://cloud-api.prevu3d.com/oauth/token HTTP/1.1
Host: cloud-api.prevu3d.com
Authorization: Basic eW91ci1jbGllbnQtaWQ6eW91ci1jbGllbnQtc2VjcmV0
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
{
"hasError": false,
"access_token": "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3599,
"token_type": "bearer"
}

Speichern Sie access_token. Schlägt dies fehl, lesen Sie den Leitfaden Client-Credentials-Flow.

Endpunkt: GET https://cloud-api.prevu3d.com/oauth/api-info

Header: Authorization: Bearer <access_token>

{
"organization": {
"id": "217ebd23-ec54-4af0-a6d6-4a441a6d1966",
"name": "Test Organization"
},
"scopes": ["read:hierarchy", "write:hierarchy"],
"apiUrl": "https://api-ue1.prevu3d.com/realityconnect-api"
}

Speichern Sie apiUrl und organization.id. Jede folgende Anfrage verwendet {apiUrl}.

Endpunkt: GET {apiUrl}/v1/nodes/{organization_id}/browse

{
"node": { "id": "217ebd23-ec54-4af0-a6d6-4a441a6d1966", "type": "Organization", "...": "..." },
"items": [
{ "id": "3d3b6f2a-9e0a-4b0a-8b0a-1c2d3e4f5a6b", "name": "North America Operations", "type": "Division", "...": "..." }
],
"total": 1
}

Verwenden Sie items[0].id als division_id.

Endpunkt: GET {apiUrl}/v1/nodes/{division_id}/browse

{
"node": { "id": "3d3b6f2a-9e0a-4b0a-8b0a-1c2d3e4f5a6b", "type": "Division", "...": "..." },
"items": [
{ "id": "8a1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e", "name": "Chicago Plant", "type": "Site", "...": "..." }
],
"total": 1
}

Verwenden Sie items[0].id als site_id und merken Sie sich items[0].name (hier "Chicago Plant"); Sie brauchen ihn in Schritt 7.

Das Umbenennen eines Knotens ist eine kleine, offensichtliche, umkehrbare Änderung: Sie erfordert write:hierarchy, betrifft nichts anderes und lässt sich mit einem einzigen Aufruf rückgängig machen.

Endpunkt: PATCH {apiUrl}/v1/nodes/{site_id}

PATCH https://api-ue1.prevu3d.com/realityconnect-api/v1/nodes/8a1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json
{ "name": "Chicago Plant (test)" }
{ "id": "8a1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e", "name": "Chicago Plant (test)", "type": "Site", "...": "..." }

Öffnen Sie die Prevu3D Platform, navigieren Sie zur Division aus Schritt 3 und öffnen Sie die Site aus Schritt 4.

Sie sollten jetzt den neuen Namen sehen, in diesem Beispiel Chicago Plant (test), oben auf der Seite der Site und in der Site-Liste der Division. Das ist die Ziellinie: eine Änderung, die vollständig über die API vorgenommen wurde und im Produkt sichtbar ist.

Endpunkt: PATCH {apiUrl}/v1/nodes/{site_id}

{ "name": "Chicago Plant" }

Senden Sie den ursprünglichen Namen der Site aus Schritt 4. Die Antwort, und die Prevu3D Platform, zeigen die Site nun wieder so, wie sie vor Schritt 5 war.

Dieses Skript führt die Schritte 1–7 der Reihe nach aus. Es pausiert nach der Umbenennung, damit Sie die Prevu3D Platform prüfen können, bevor die Änderung rückgängig gemacht wird.

import requests
import base64
client_id = "your-client-id"
client_secret = "your-client-secret"
base_url = "https://cloud-api.prevu3d.com"
# Schritt 1: Token abrufen
token_response = requests.post(
f"{base_url}/oauth/token",
data={"grant_type": "client_credentials"},
headers={
"Authorization": f'Basic {base64.b64encode(f"{client_id}:{client_secret}".encode()).decode()}',
"Content-Type": "application/x-www-form-urlencoded",
},
)
token_response.raise_for_status()
access_token = token_response.json()["access_token"]
headers = {"Authorization": f"Bearer {access_token}"}
# Schritt 2: API-URL und Organisations-ID ermitteln
api_info = requests.get(f"{base_url}/oauth/api-info", headers=headers).json()
api_url = api_info["apiUrl"]
organization_id = api_info["organization"]["id"]
# Schritt 3: zur ersten Division navigieren
divisions = requests.get(f"{api_url}/v1/nodes/{organization_id}/browse", headers=headers).json()
division_id = divisions["items"][0]["id"]
# Schritt 4: zur ersten Site in dieser Division navigieren
sites = requests.get(f"{api_url}/v1/nodes/{division_id}/browse", headers=headers).json()
site = sites["items"][0]
site_id = site["id"]
original_name = site["name"]
# Schritt 5: die Site umbenennen
new_name = f"{original_name} (test)"
requests.patch(f"{api_url}/v1/nodes/{site_id}", json={"name": new_name}, headers=headers).raise_for_status()
print(f"Site {site_id} umbenannt in '{new_name}'.")
print("Öffnen Sie die Site jetzt in der Prevu3D Platform. Sie sollten den neuen Namen sehen.")
input("Drücken Sie die Eingabetaste, sobald Sie es bestätigt haben, um die Änderung rückgängig zu machen...")
# Schritt 7: die Umbenennung rückgängig machen
requests.patch(f"{api_url}/v1/nodes/{site_id}", json={"name": original_name}, headers=headers).raise_for_status()
print(f"Site {site_id} wiederhergestellt als '{original_name}'.")

Der obige Durchlauf ist ein Happy Path: ein frisches Token, ein ruhiges Netzwerk, nichts zu wiederholen. Ein Skript, das länger läuft — ein Massenimport, eine Abfrageschleife, ein großer Download — trifft irgendwann auf einen 429, ein Access Token, das mitten im Lauf abläuft, oder einen einzelnen Aufruf, der wiederholt werden muss, während der Rest des Batches weiterläuft. Anstatt jedes Beispiel auf dieser Seite mit demselben defensiven Code aufzublähen, führt dieser Abschnitt einmalig einen kleinen wiederverwendbaren Client ein; die Beispiele, die ihn benötigen, verweisen hierher, statt ihn zu wiederholen.

RCAPIClient umschließt die rohen requests-Aufrufe von oben mit drei Dingen: Es cacht das Access Token und erneuert es automatisch — proaktiv vor dem Ablauf und reaktiv bei einem 401 — es wiederholt einen 429 anhand des Retry-After-Headers (siehe Rate Limits) statt eine Wartezeit zu raten, und es gibt nach einer maximalen Anzahl an Versuchen oder einer maximalen verstrichenen Zeit auf, statt endlos weiter zu versuchen.

import time
import base64
import requests
class RCAPIClient:
"""Token cache + refresh, retry with backoff, and a give-up condition.
Wrap the raw `requests` calls above with this once your integration runs
long enough to hit a rate limit or outlive an access token -- most bulk
imports, polling loops, and long-running downloads do.
"""
def __init__(self, client_id, client_secret, cloud_api_base="https://cloud-api.prevu3d.com"):
self.client_id = client_id
self.client_secret = client_secret
self.cloud_api_base = cloud_api_base
self._access_token = None
self._expires_at = 0 # epoch seconds
def _fetch_token(self):
credentials = base64.b64encode(f"{self.client_id}:{self.client_secret}".encode()).decode()
response = requests.post(
f"{self.cloud_api_base}/oauth/token",
data={"grant_type": "client_credentials"},
headers={"Authorization": f"Basic {credentials}"},
)
response.raise_for_status()
body = response.json()
self._access_token = body["access_token"]
# Refresh a little before the real expiry, not exactly at it.
self._expires_at = time.time() + body["expires_in"] - 60
def _token(self):
if self._access_token is None or time.time() >= self._expires_at:
self._fetch_token()
return self._access_token
def request(self, method, url, max_attempts=6, max_elapsed=300, **kwargs):
"""`requests.request()` plus token refresh, 429 backoff, and a give-up condition."""
start = time.time()
headers = kwargs.pop("headers", {})
for attempt in range(1, max_attempts + 1):
headers["Authorization"] = f"Bearer {self._token()}"
response = requests.request(method, url, headers=headers, **kwargs)
if response.status_code == 401 and attempt == 1:
self._access_token = None # token was rejected outright; force one refresh
continue
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 1))
if attempt == max_attempts or time.time() - start + retry_after > max_elapsed:
response.raise_for_status()
time.sleep(retry_after)
continue
return response
return response
def poll(self, url, is_done, interval=15, max_elapsed=3600):
"""GET `url` through this client until `is_done(body)` or `max_elapsed` seconds pass."""
start = time.time()
while True:
response = self.request("GET", url)
response.raise_for_status()
body = response.json()
if is_done(body):
return body
if time.time() - start >= max_elapsed:
raise TimeoutError(f"Gave up polling {url} after {max_elapsed}s")
time.sleep(interval)

Verwenden Sie ihn anstelle von requests direkt, zum Beispiel für das Token und die Organisationsermittlung aus Schritt 1-2 oben:

client = RCAPIClient(client_id="your-client-id", client_secret="your-client-secret")
api_info = client.request("GET", "https://cloud-api.prevu3d.com/oauth/api-info").json()

request() bremst erst, sobald es einen 429 sieht. Wenn eine Arbeitslast nahe an die Kapazität eines Buckets herankommt, ohne einen 429 auszulösen (zum Beispiel anhaltend hoher Datenverkehr), lesen Sie X-RateLimit-Remaining und X-RateLimit-Reset aus jeder Antwort und verlangsamen Sie, bevor der Bucket leer wird — siehe Rate Limits. poll() baut auf request() eine Abfrageschleife mit Backoff und Abbruchbedingung auf; Verarbeitung und Überwachung nutzt sie.

Sie sind von einem frischen Access Token zu einer Änderung gelangt, die Sie in der Prevu3D Platform sehen konnten, und wieder zurück.

  • Ihre IDs finden: weitere Wege, um IDs für Organisation, Division, Site und andere Knoten zu ermitteln, über das Navigieren hinaus.
  • Knotentypen und Hierarchie: der vollständige Knotenbaum, den Sie gerade durchlaufen haben, und was jeder Knotentyp enthalten kann.
  • Einführung: der aufgabenorientierte Ausgangspunkt für alles andere, was die RealityConnect API kann.