Salta ai contenuti

La tua prima modifica, dall'inizio alla fine

Questa pagina è un unico script lineare, da un access token appena ottenuto a una modifica che puoi vedere nella Prevu3D Platform, e ritorno. Usa il flusso Client Credentials dall’inizio alla fine, e ogni ID necessario proviene da una risposta API precedente nello stesso script: nulla da compilare a mano.


  1. Ottieni un access token: autenticati con Client Credentials
  2. Scopri la tua organizzazione: chiama /oauth/api-info per ottenere il tuo URL API e l’ID dell’organizzazione
  3. Naviga fino a una divisione: il primo figlio della tua organizzazione
  4. Naviga fino a un sito: il primo figlio di quella divisione
  5. Rinomina il sito: una modifica piccola, evidente e reversibile
  6. Conferma nella Prevu3D Platform: il traguardo
  7. Annulla la ridenominazione: senza lasciare tracce
  • Completa prima la guida Flusso Client Credentials. La tua applicazione OAuth ha bisogno degli scope read:hierarchy e write:hierarchy, e il tuo utente di servizio ha bisogno dell’accesso ai contenuti e di un ruolo di livello modifica su almeno un sito (vedi il modello di sicurezza).
  • La tua organizzazione ha bisogno di almeno una divisione contenente almeno un sito. Questo percorso naviga fino al primo che trova.

Endpoint: 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"
}

Salva access_token. Se questo fallisce, consulta la guida Flusso Client Credentials.

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

Intestazioni: 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"
}

Salva apiUrl e organization.id. Ogni richiesta successiva usa {apiUrl}.

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
}

Prendi items[0].id come division_id.

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
}

Prendi items[0].id come site_id, e conserva items[0].name ("Chicago Plant" in questo caso); ti servirà al passaggio 7.

Rinominare un nodo è una modifica piccola, evidente e reversibile: richiede write:hierarchy, non tocca nient’altro e si annulla con un’unica chiamata.

Endpoint: 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", "...": "..." }

Apri la Prevu3D Platform, vai alla divisione del passaggio 3 e apri il sito del passaggio 4.

Ora dovresti vedere il nuovo nome, Chicago Plant (test) in questo esempio, in cima alla pagina del sito e nell’elenco dei siti della divisione. Questo è il traguardo: una modifica effettuata interamente tramite l’API, visibile nel prodotto.

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

{ "name": "Chicago Plant" }

Invia il nome originale del sito dal passaggio 4. La risposta, e la Prevu3D Platform, mostrano ora il sito come era prima del passaggio 5.

Questo script esegue i passaggi da 1 a 7 in ordine. Si mette in pausa dopo la ridenominazione, così puoi verificare nella Prevu3D Platform prima di annullare la modifica.

import requests
import base64
client_id = "your-client-id"
client_secret = "your-client-secret"
base_url = "https://cloud-api.prevu3d.com"
# Passaggio 1: ottenere un token
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}"}
# Passaggio 2: scoprire l'URL API e l'ID dell'organizzazione
api_info = requests.get(f"{base_url}/oauth/api-info", headers=headers).json()
api_url = api_info["apiUrl"]
organization_id = api_info["organization"]["id"]
# Passaggio 3: navigare fino alla prima divisione
divisions = requests.get(f"{api_url}/v1/nodes/{organization_id}/browse", headers=headers).json()
division_id = divisions["items"][0]["id"]
# Passaggio 4: navigare fino al primo sito di quella divisione
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"]
# Passaggio 5: rinominare il sito
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"Sito {site_id} rinominato in '{new_name}'.")
print("Apri il sito nella Prevu3D Platform ora. Dovresti vedere il nuovo nome.")
input("Premi Invio una volta confermato, per annullare la modifica...")
# Passaggio 7: annullare la ridenominazione
requests.patch(f"{api_url}/v1/nodes/{site_id}", json={"name": original_name}, headers=headers).raise_for_status()
print(f"Sito {site_id} ripristinato a '{original_name}'.")

Il percorso qui sopra è un caso ideale: un token appena ottenuto, una rete tranquilla, nulla da riprovare. Uno script che continua a girare — un’importazione in blocco, un ciclo di polling, un download di grandi dimensioni — prima o poi incontra un 429, un access token che scade a metà esecuzione, o una singola chiamata che deve essere riprovata mentre il resto del lotto prosegue. Invece di appesantire ogni esempio di questo sito con lo stesso codice difensivo, questa sezione introduce una volta sola un piccolo client riutilizzabile; gli esempi che ne hanno bisogno rimandano qui invece di ripeterlo.

RCAPIClient avvolge le chiamate requests grezze viste sopra con tre funzionalità: mette in cache l’access token e lo rinnova automaticamente — in modo proattivo prima della scadenza, e in modo reattivo su un 401 — riprova un 429 usando l’header Retry-After (vedi Limiti di frequenza) invece di indovinare un’attesa, e si arrende dopo un numero massimo di tentativi o un tempo massimo trascorso invece di riprovare all’infinito.

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)

Usalo al posto di requests direttamente, ad esempio per il token e la ricerca dell’organizzazione dei Passaggi 1-2 sopra:

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() rallenta solo quando incontra un 429. Se un carico di lavoro si avvicina alla capacità di un bucket senza mai farne scattare uno (traffico sostenuto ad alto volume, ad esempio), leggi X-RateLimit-Remaining e X-RateLimit-Reset da qualsiasi risposta e rallenta prima che il bucket si svuoti — vedi Limiti di frequenza. poll() costruisce sopra request() un ciclo di polling con backoff e resa; Elaborazione e monitoraggio lo utilizza.

Sei passato da un access token appena ottenuto a una modifica che hai potuto vedere nella Prevu3D Platform, e ritorno.

  • Trovare i tuoi ID: altri modi per risolvere gli ID di organizzazione, divisione, sito e altri nodi oltre alla navigazione.
  • Tipi di nodo e gerarchia: l’intero albero dei nodi che hai appena percorso, e cosa può contenere ciascun tipo di nodo.
  • Introduzione: il punto di partenza organizzato per attività per tutto il resto che l’API RealityConnect può fare.