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.
Was Sie tun werden
Abschnitt betitelt „Was Sie tun werden“- Ein Access Token abrufen: Authentifizierung mit Client Credentials
- Ihre Organisation ermitteln:
/oauth/api-infoaufrufen, um Ihre API-URL und Organisations-ID zu erhalten - Zu einer Division navigieren: dem ersten Kind Ihrer Organisation
- Zu einer Site navigieren: dem ersten Kind dieser Division
- Die Site umbenennen: eine kleine, offensichtliche, umkehrbare Änderung
- In der Prevu3D Platform bestätigen: die Ziellinie
- Die Umbenennung rückgängig machen: keine Spuren hinterlassen
Voraussetzungen
Abschnitt betitelt „Voraussetzungen“- Schließen Sie zuerst den Leitfaden Client-Credentials-Flow ab. Ihre OAuth-Anwendung benötigt die Scopes
read:hierarchyundwrite: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.
Schritt 1: Ein Access Token abrufen
Abschnitt betitelt „Schritt 1: Ein Access Token abrufen“Endpunkt: POST https://cloud-api.prevu3d.com/oauth/token
POST https://cloud-api.prevu3d.com/oauth/token HTTP/1.1Host: cloud-api.prevu3d.comAuthorization: Basic eW91ci1jbGllbnQtaWQ6eW91ci1jbGllbnQtc2VjcmV0Content-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.
Schritt 2: Ihre Organisation ermitteln
Abschnitt betitelt „Schritt 2: Ihre Organisation ermitteln“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}.
Schritt 3: Zu einer Division navigieren
Abschnitt betitelt „Schritt 3: Zu einer Division navigieren“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.
Schritt 4: Zu einer Site navigieren
Abschnitt betitelt „Schritt 4: Zu einer Site navigieren“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.
Schritt 5: Die Site umbenennen
Abschnitt betitelt „Schritt 5: Die Site umbenennen“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.1Authorization: Bearer <access_token>Content-Type: application/json
{ "name": "Chicago Plant (test)" }{ "id": "8a1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e", "name": "Chicago Plant (test)", "type": "Site", "...": "..." }Schritt 6: In der Prevu3D Platform bestätigen
Abschnitt betitelt „Schritt 6: In der Prevu3D Platform bestätigen“Ö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.
Schritt 7: Die Umbenennung rückgängig machen
Abschnitt betitelt „Schritt 7: Die Umbenennung rückgängig machen“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.
Mit Python ausprobieren
Abschnitt betitelt „Mit Python ausprobieren“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 requestsimport base64
client_id = "your-client-id"client_secret = "your-client-secret"base_url = "https://cloud-api.prevu3d.com"
# Schritt 1: Token abrufentoken_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 ermittelnapi_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 navigierendivisions = 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 navigierensites = 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 umbenennennew_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 machenrequests.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}'.")Produktionsreife Version
Abschnitt betitelt „Produktionsreife Version“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 timeimport base64import 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.
Wie geht es weiter?
Abschnitt betitelt „Wie geht es weiter?“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.