fix: Stellio/Orion - use NGSI-LD core context only (remove raw.githubusercontent.com)

- STELLIO_INLINE_CONTEXT: replaced long inline dict with uri.etsi.org core URL
- ORION_CONTEXT: removed raw.githubusercontent.com URLs (not accessible from containers)
- publish_stellio(): added NGSILD-Tenant header (urn:ngsi-ld:tenant:default)
- publish_stellio(): keep @context in payload (Stellio needs it to resolve vocabulary)
- Added STELLIO_URL and STELLIO_TENANT env vars for host-based execution via Traefik

Fixes: Stellio 503 JsonLdError, Orion-LD context resolution failures.
Tested: Stellio 201, Orion-LD 207.
This commit is contained in:
Eric FELIXINE
2026-05-04 10:35:18 -04:00
parent ba13bf1321
commit aa42a213bb

View File

@@ -131,14 +131,10 @@ for stype, locs in SENSOR_LOCATIONS.items():
# =============================================================================
# Contextes NGSI-LD : core + Smart Data Models
# https://smartdatamodels.org pour les @context officiels
STELLIO_CONTEXT_URL = "https://gitea.digitribe.fr/eric/smart-city-digital-twin-martinique/raw/branch/master/contexts"
# Contexte NGSI-LD pur pour Orion-LD (vocabulaires standards uniquement)
# Orion-LD ne peut pas résoudre raw.githubusercontent.com — utiliser uri.etsi.org uniquement
ORION_CONTEXT = [
f"{STELLIO_CONTEXT_URL}/ngsi-ld-core.jsonld",
"https://raw.githubusercontent.com/smart-data-models/dataModel.Environment/master/context.jsonld",
"https://raw.githubusercontent.com/smart-data-models/dataModel.Transportation/master/context.jsonld",
"https://raw.githubusercontent.com/smart-data-models/dataModel.Parking/master/context.jsonld",
"https://raw.githubusercontent.com/smart-data-models/dataModel.Weather/master/context.jsonld",
"https://raw.githubusercontent.com/smart-data-models/dataModel.Device/master/context.jsonld",
"https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld",
]
# Mapping sensor type → Smart Data Model type NGSI-LD
@@ -155,7 +151,16 @@ FROST_HEADERS = {"Accept": "application/json", "Content-Type": "application/json
# Cache FROST : éviter de recréer Thing/Datastream
_frost_cache: dict[str, tuple[str, str]] = {} # (sid, field) -> (thing_id, ds_id)
def _ngsi_payload(sid: str, sensor: dict) -> dict:
# Contexte NGSI-LD pur pour Stellio et Orion-LD (vocabulaires standards uniquement)
# Stellio et Orion-LD embarquent le contexte core NGSI-LD : https://uri.etsi.org/ngsi-ld/
# On n'utilise PAS les vocabulaires smartdatamodels.org distants (inaccessibles depuis les containers)
# Les types d'entité Smart Data Models (AirQualityObserved, etc.) sont reconnus par leur nom
# Les propriétés spécifiques sont stockées telles quelles (vocabulaire libre)
STELLIO_INLINE_CONTEXT = [
"https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld",
]
def _ngsi_payload(sid: str, sensor: dict, context: list | dict = ORION_CONTEXT) -> dict:
"""Construit un payload NGSI-LD avec Smart Data Models officiels."""
stype = sensor["type"]
model_type = SMART_MODEL_MAPPING.get(stype, "Device")
@@ -163,7 +168,7 @@ def _ngsi_payload(sid: str, sensor: dict) -> dict:
# Attributs communs à tous les modèles
payload = {
"@context": ORION_CONTEXT,
"@context": context,
"id": f"urn:ngsi-ld:{model_type}:{sid}",
"type": model_type,
"dateObserved": {"type": "Property", "value": now},
@@ -413,19 +418,24 @@ try:
except:
pass
ORION_URL = f"http://{ORION_IP or ORION_HOST}:1026" if ORION_IP else "http://fiware-gis-quickstart-orion-1:1026"
STELLIO_URL = "http://stellio-api-gateway:8080"
STELLIO_URL = os.environ.get("STELLIO_URL", "http://stellio-api-gateway:8080")
# Configuration OpenRemote (URLs dynamiques)
OR_URL = os.environ.get("OR_URL", "http://openremote-manager-1:8080") # Hostname Docker interne
OR_REALM = os.environ.get("OR_REALM", "smartcity") # Default: smartcity
OR_TOKEN_URL = os.environ.get("OR_TOKEN_URL", f"http://openremote-keycloak-1:8080/auth/realms/{OR_TOKEN_REALM}/protocol/openid-connect/token")
OR_TOKEN_TTL = int(os.environ.get("OR_TOKEN_TTL", "3600")) # Refresh token every hour
STELLIO_TENANT = os.environ.get("STELLIO_TENANT", "urn:ngsi-ld:tenant:default")
def publish_stellio(sid: str, sensor: dict) -> bool:
"""Publie sur Stellio (gère le 409)."""
entity = _ngsi_payload(sid, sensor)
url = f"{STELLIO_URL}/ngsi-ld/v1/entities" # Sans options=upsert
"""Publie sur Stellio via Traefik (gère le 409)."""
entity = _ngsi_payload(sid, sensor, context=STELLIO_INLINE_CONTEXT)
# Stellio a besoin du @context pour résoudre les vocabulaires NGSI-LD
# (uri.etsi.org résolu depuis le JAR embarqué)
url = f"{STELLIO_URL}/ngsi-ld/v1/entities"
headers = {
"Content-Type": "application/ld+json",
"Accept": "application/ld+json",
"Content-Type": "application/ld+json",
"Accept": "application/ld+json",
"NGSILD-Tenant": STELLIO_TENANT,
}
try:
body = json.dumps(entity).encode()