Uw eerste wijziging, van begin tot eind
Deze pagina is één lineair script, van een vers access token tot een wijziging die u kunt zien in de Prevu3D Platform, en weer terug. Het gebruikt van begin tot eind de Client Credentials-flow, en elke ID die nodig is komt uit een eerdere API-respons in hetzelfde script: niets om handmatig in te vullen.
Wat u gaat doen
Section titled “Wat u gaat doen”- Een access token ophalen: authenticeren met Client Credentials
- Uw organisatie ontdekken:
/oauth/api-infoaanroepen voor uw API-URL en organisatie-ID - Navigeren naar een divisie: het eerste kind van uw organisatie
- Navigeren naar een site: het eerste kind van die divisie
- De site hernoemen: een kleine, duidelijke, omkeerbare wijziging
- Bevestigen in de Prevu3D Platform: de finish
- De naamswijziging ongedaan maken: geen sporen achterlaten
Vereisten
Section titled “Vereisten”- Doorloop eerst de gids Client Credentials-flow. Uw OAuth-applicatie heeft de scopes
read:hierarchyenwrite:hierarchynodig, en uw servicegebruiker heeft inhoudstoegang en een rol op bewerkingsniveau nodig voor ten minste één site (zie het beveiligingsmodel). - Uw organisatie heeft ten minste één divisie nodig met ten minste één site. Deze doorloop navigeert naar de eerste die wordt gevonden.
Stap 1: een access token ophalen
Section titled “Stap 1: een access token ophalen”Endpoint: 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"}Bewaar access_token. Zie de gids Client Credentials-flow als dit mislukt.
Stap 2: uw organisatie ontdekken
Section titled “Stap 2: uw organisatie ontdekken”Endpoint: GET https://cloud-api.prevu3d.com/oauth/api-info
Headers: 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"}Bewaar apiUrl en organization.id. Elk verzoek hieronder gebruikt {apiUrl}.
Stap 3: navigeren naar een divisie
Section titled “Stap 3: navigeren naar een divisie”Endpoint: 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}Gebruik items[0].id als division_id.
Stap 4: navigeren naar een site
Section titled “Stap 4: navigeren naar een site”Endpoint: 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}Gebruik items[0].id als site_id, en bewaar items[0].name (hier "Chicago Plant"); u heeft deze nodig bij stap 7.
Stap 5: de site hernoemen
Section titled “Stap 5: de site hernoemen”Het hernoemen van een node is een kleine, duidelijke, omkeerbare wijziging: het vereist write:hierarchy, raakt verder niets aan en is met één aanroep terug te draaien.
Endpoint: 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", "...": "..." }Stap 6: bevestigen in de Prevu3D Platform
Section titled “Stap 6: bevestigen in de Prevu3D Platform”Open de Prevu3D Platform, navigeer naar de divisie uit stap 3 en open de site uit stap 4.
U zou nu de nieuwe naam moeten zien, in dit voorbeeld Chicago Plant (test), bovenaan de pagina van de site en in de sitelijst van de divisie. Dat is de finish: een wijziging die volledig via de API is gemaakt, zichtbaar in het product.
Stap 7: de naamswijziging ongedaan maken
Section titled “Stap 7: de naamswijziging ongedaan maken”Endpoint: PATCH {apiUrl}/v1/nodes/{site_id}
{ "name": "Chicago Plant" }Stuur de oorspronkelijke naam van de site uit stap 4. De respons, en de Prevu3D Platform, tonen de site nu weer zoals deze was vóór stap 5.
Probeer het met Python
Section titled “Probeer het met Python”Dit script doorloopt stap 1 t/m 7 in volgorde. Het pauzeert na het hernoemen, zodat u de Prevu3D Platform kunt controleren voordat de wijziging ongedaan wordt gemaakt.
import requestsimport base64
client_id = "your-client-id"client_secret = "your-client-secret"base_url = "https://cloud-api.prevu3d.com"
# Stap 1: een token ophalentoken_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}"}
# Stap 2: de API-URL en organisatie-ID ontdekkenapi_info = requests.get(f"{base_url}/oauth/api-info", headers=headers).json()api_url = api_info["apiUrl"]organization_id = api_info["organization"]["id"]
# Stap 3: navigeren naar de eerste divisiedivisions = requests.get(f"{api_url}/v1/nodes/{organization_id}/browse", headers=headers).json()division_id = divisions["items"][0]["id"]
# Stap 4: navigeren naar de eerste site in die divisiesites = 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"]
# Stap 5: de site hernoemennew_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} hernoemd naar '{new_name}'.")print("Open de site nu in de Prevu3D Platform. U zou de nieuwe naam moeten zien.")input("Druk op Enter zodra u dit heeft bevestigd, om de wijziging ongedaan te maken...")
# Stap 7: de naamswijziging ongedaan makenrequests.patch(f"{api_url}/v1/nodes/{site_id}", json={"name": original_name}, headers=headers).raise_for_status()print(f"Site {site_id} hersteld naar '{original_name}'.")Productieklare versie
Section titled “Productieklare versie”De walkthrough hierboven is een happy path: een vers token, een rustig netwerk, niets om opnieuw te proberen. Een script dat langer blijft draaien — een bulkimport, een pollinglus, een grote download — krijgt uiteindelijk te maken met een 429, een access token dat halverwege verloopt, of één aanroep die opnieuw geprobeerd moet worden terwijl de rest van de batch doorgaat. In plaats van elk voorbeeld op deze site op te blazen met dezelfde defensieve code, introduceert deze sectie eenmalig een kleine herbruikbare client; de voorbeelden die deze nodig hebben verwijzen hiernaar terug in plaats van hem te herhalen.
RCAPIClient verpakt de ruwe requests-aanroepen hierboven met drie dingen: het cachet het access token en vernieuwt het automatisch — proactief vóór het verloopt, en reactief bij een 401 — het probeert een 429 opnieuw met behulp van de Retry-After-header (zie Rate Limits) in plaats van een vertraging te raden, en het geeft het op na een maximumaantal pogingen of een maximale verstreken tijd in plaats van eindeloos door te gaan.
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)Gebruik deze in plaats van rechtstreeks requests, bijvoorbeeld voor het token en de organisatie-opzoeking uit Stap 1-2 hierboven:
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() remt pas af zodra het een 429 ziet. Als een workload dicht bij de capaciteit van een bucket komt zonder er een te veroorzaken (bijvoorbeeld aanhoudend hoogvolumeverkeer), lees dan X-RateLimit-Remaining en X-RateLimit-Reset uit elke response en vertraag voordat de bucket leeg raakt — zie Rate Limits. poll() bouwt bovenop request() een pollinglus met backoff en opgave-conditie; Verwerking en bewaking gebruikt deze.
Wat is de volgende stap?
Section titled “Wat is de volgende stap?”U bent van een vers access token naar een wijziging gegaan die u kon zien in de Prevu3D Platform, en weer terug.
- Uw ID’s vinden: meer manieren om ID’s voor organisatie, divisie, site en andere nodes te achterhalen, naast navigeren.
- Nodetypen en hiërarchie: de volledige nodeboom die u zojuist heeft doorlopen, en wat elk nodetype kan bevatten.
- Introductie: het taakgerichte startpunt voor al het andere dat de RealityConnect API kan doen.