Files
cariflex/scripts/fix-hasura-relations.py
Eric F a3a066034d fix: Hasura camelCase relations + UI patch + NodeRED
- Fix Evses.evseId type (varchar→integer) for FK compatibility
- Recreate all Hasura relations with camelCase names matching UI expectations
- Patch UI .next JS files: PascalCase→camelCase (Evses, Connectors, etc.)
- Add fix-hasura-relations.py script for future maintenance
- NodeRED accessible at nodered.digitribe.fr
- All 15 stations online with working map and detail pages
2026-06-15 20:27:41 -04:00

242 lines
12 KiB
Python

#!/usr/bin/env python3
"""
Script pour recréer toutes les relations Hasura avec les noms camelCase.
Utilise l'API metadata Hasura pour créer les relations manquantes.
"""
import json
import subprocess
import sys
ADMIN_SECRET = 'Digitribe972'
METADATA_URL = 'http://localhost:8082/v1/metadata'
GRAPHQL_URL = 'http://localhost:8082/v1/graphql'
def graphql_query(query):
"""Execute a GraphQL query and return the result."""
r = subprocess.run(
['curl', '-s', '-X', 'POST', GRAPHQL_URL,
'-H', f'x-hasura-admin-secret: {ADMIN_SECRET}',
'-H', 'Content-Type: application/json',
'-d', json.dumps({"query": query})],
capture_output=True, text=True, timeout=15
)
return json.loads(r.stdout)
def metadata_command(payload):
"""Execute a metadata command and return the result."""
r = subprocess.run(
['curl', '-s', '-X', 'POST', METADATA_URL,
'-H', f'x-hasura-admin-secret: {ADMIN_SECRET}',
'-H', 'Content-Type: application/json',
'-d', json.dumps(payload)],
capture_output=True, text=True, timeout=15
)
return json.loads(r.stdout)
def test_relation(table, relation, fields='{ id }'):
"""Test if a relation works by querying it."""
query = f'{{ {table}(where: {{id: {{_eq: "CP001"}}}}) {{ id {relation} {fields} }} }}'
result = graphql_query(query)
if 'errors' in result:
return False, result['errors'][0]['message'][:100]
return True, "OK"
def create_relation(table, name, rel_type, remote_table, col_map):
"""Create a relationship in Hasura."""
if rel_type == 'object':
payload = {
"type": "pg_create_object_relationship",
"args": {
"source": "default",
"table": {"schema": "public", "name": table},
"name": name,
"using": {
"manual_configuration": {
"remote_table": {"schema": "public", "name": remote_table},
"column_mapping": col_map
}
}
}
}
else:
payload = {
"type": "pg_create_array_relationship",
"args": {
"source": "default",
"table": {"schema": "public", "name": table},
"name": name,
"using": {
"manual_configuration": {
"remote_table": {"schema": "public", "name": remote_table},
"column_mapping": col_map
}
}
}
}
result = metadata_command(payload)
if 'message' in result and result['message'] == 'success':
return True
elif 'error' in result:
if 'already exists' in result['error']:
return True # Already exists, that's OK
print(f" ERROR: {result['error'][:200]}")
return False
return False
def main():
print("=== Diagnostic des relations Hasura ===\n")
# Test critical relations
critical_relations = [
("ChargingStations", "location", "{ id name }"),
("ChargingStations", "evses", "{ id evseId }"),
("ChargingStations", "connectors", "{ id status }"),
("ChargingStations", "transactions", "{ id }"),
("ChargingStations", "latestStatusNotifications", "{ statusNotification { connectorStatus } }"),
("ChargingStations", "tenant", "{ id name }"),
("Evses", "connectors", "{ id status }"),
("Transactions", "chargingStation", "{ id }"),
("Transactions", "evse", "{ id }"),
("Transactions", "connector", "{ id }"),
("Transactions", "location", "{ id name }"),
("LatestStatusNotifications", "statusNotification", "{ connectorStatus }"),
]
print("Test des relations critiques:")
all_ok = True
for table, relation, fields in critical_relations:
ok, msg = test_relation(table, relation, fields)
status = "OK" if ok else "FAIL"
print(f" {status}: {table}.{relation}")
if not ok:
all_ok = False
print(f" -> {msg}")
if all_ok:
print("\n=== Toutes les relations fonctionnent ! ===")
return
print("\n=== Recréation des relations manquantes ===\n")
# Define all relations to create
relations = [
# ChargingStations
("ChargingStations", "location", "object", "Locations", {"locationId": "id"}),
("ChargingStations", "tenant", "object", "Tenants", {"tenantId": "id"}),
("ChargingStations", "evses", "array", "Evses", {"id": "stationId"}),
("ChargingStations", "connectors", "array", "Connectors", {"id": "stationId"}),
("ChargingStations", "transactions", "array", "Transactions", {"id": "stationId"}),
("ChargingStations", "latestStatusNotifications", "array", "LatestStatusNotifications", {"id": "stationId"}),
("ChargingStations", "variableAttributes", "array", "VariableAttributes", {"id": "stationPkId"}),
# Evses
("Evses", "connectors", "array", "Connectors", {"evseId": "evseId"}),
("Evses", "transactions", "array", "Transactions", {"evseId": "evseId"}),
("Evses", "chargingStation", "object", "ChargingStations", {"stationId": "id"}),
# Connectors
("Connectors", "evse", "object", "Evses", {"evseId": "id"}),
("Connectors", "chargingStation", "object", "ChargingStations", {"stationId": "id"}),
# Transactions
("Transactions", "chargingStation", "object", "ChargingStations", {"stationId": "id"}),
("Transactions", "evse", "object", "Evses", {"evseId": "id"}),
("Transactions", "connector", "object", "Connectors", {"connectorId": "id"}),
("Transactions", "authorization", "object", "Authorizations", {"authorizationId": "id"}),
("Transactions", "location", "object", "Locations", {"locationId": "id"}),
# LatestStatusNotifications
("LatestStatusNotifications", "statusNotification", "object", "StatusNotifications", {"statusNotificationId": "id"}),
("LatestStatusNotifications", "chargingStation", "object", "ChargingStations", {"stationId": "id"}),
# Locations
("Locations", "chargingStations", "array", "ChargingStations", {"locationId": "id"}),
# StatusNotifications
("StatusNotifications", "chargingStation", "object", "ChargingStations", {"stationId": "id"}),
("StatusNotifications", "evse", "object", "Evses", {"evseId": "id"}),
("StatusNotifications", "connector", "object", "Connectors", {"connectorId": "id"}),
# Authorizations
("Authorizations", "transactions", "array", "Transactions", {"authorizationId": "id"}),
("Authorizations", "chargingStation", "object", "ChargingStations", {"stationId": "id"}),
# Tenants
("Tenants", "chargingStations", "array", "ChargingStations", {"tenantId": "id"}),
("Tenants", "evses", "array", "Evses", {"tenantId": "id"}),
("Tenants", "connectors", "array", "Connectors", {"tenantId": "id"}),
("Tenants", "transactions", "array", "Transactions", {"tenantId": "id"}),
("Tenants", "locations", "array", "Locations", {"tenantId": "id"}),
("Tenants", "authorizations", "array", "Authorizations", {"tenantId": "id"}),
("Tenants", "tenantPartners", "array", "TenantPartners", {"tenantId": "id"}),
("Tenants", "statusNotifications", "array", "StatusNotifications", {"tenantId": "id"}),
("Tenants", "latestStatusNotifications", "array", "LatestStatusNotifications", {"tenantId": "id"}),
("Tenants", "variableAttributes", "array", "VariableAttributes", {"tenantId": "id"}),
("Tenants", "chargingProfiles", "array", "ChargingProfiles", {"tenantId": "id"}),
("Tenants", "chargingSchedules", "array", "ChargingSchedules", {"tenantId": "id"}),
("Tenants", "salesTariffs", "array", "SalesTariffs", {"tenantId": "id"}),
("Tenants", "messageInfos", "array", "MessageInfos", {"tenantId": "id"}),
("Tenants", "securityEvents", "array", "SecurityEvents", {"tenantId": "id"}),
("Tenants", "certificates", "array", "Certificates", {"tenantId": "id"}),
("Tenants", "boots", "array", "Boots", {"tenantId": "id"}),
("Tenants", "asyncJobStatuses", "array", "AsyncJobStatuses", {"tenantId": "id"}),
("Tenants", "eventData", "array", "EventData", {"tenantId": "id"}),
("Tenants", "subscriptions", "array", "Subscriptions", {"tenantId": "id"}),
("Tenants", "reservations", "array", "Reservations", {"tenantId": "id"}),
("Tenants", "transactionEvents", "array", "TransactionEvents", {"tenantId": "id"}),
("Tenants", "startTransactions", "array", "StartTransactions", {"tenantId": "id"}),
("Tenants", "stopTransactions", "array", "StopTransactions", {"tenantId": "id"}),
("Tenants", "meterValues", "array", "MeterValues", {"tenantId": "id"}),
("Tenants", "chargingStationNetworkProfiles", "array", "ChargingStationNetworkProfiles", {"tenantId": "id"}),
("Tenants", "serverNetworkProfiles", "array", "ServerNetworkProfiles", {"tenantId": "id"}),
("Tenants", "componentVariables", "array", "ComponentVariables", {"tenantId": "id"}),
("Tenants", "components", "array", "Components", {"tenantId": "id"}),
("Tenants", "variables", "array", "Variables", {"tenantId": "id"}),
("Tenants", "evseTypes", "array", "EvseTypes", {"tenantId": "id"}),
("Tenants", "compositeSchedules", "array", "CompositeSchedules", {"tenantId": "id"}),
("Tenants", "changeConfigurations", "array", "ChangeConfigurations", {"tenantId": "id"}),
("Tenants", "chargingStationSequences", "array", "ChargingStationSequences", {"tenantId": "id"}),
("Tenants", "chargingStationSecurityInfos", "array", "ChargingStationSecurityInfos", {"tenantId": "id"}),
("Tenants", "localListAuthorizations", "array", "LocalListAuthorizations", {"tenantId": "id"}),
("Tenants", "localListVersionAuthorizations", "array", "LocalListVersionAuthorizations", {"tenantId": "id"}),
("Tenants", "localListVersions", "array", "LocalListVersions", {"tenantId": "id"}),
("Tenants", "sendLocalListAuthorizations", "array", "SendLocalListAuthorizations", {"tenantId": "id"}),
("Tenants", "sendLocalLists", "array", "SendLocalLists", {"tenantId": "id"}),
("Tenants", "deleteCertificateAttempts", "array", "DeleteCertificateAttempts", {"tenantId": "id"}),
("Tenants", "installCertificateAttempts", "array", "InstallCertificateAttempts", {"tenantId": "id"}),
("Tenants", "installedCertificates", "array", "InstalledCertificates", {"tenantId": "id"}),
]
created = 0
failed = 0
for table, name, rel_type, remote_table, col_map in relations:
print(f"Creating {table}.{name}...", end=" ")
if create_relation(table, name, rel_type, remote_table, col_map):
print("OK")
created += 1
else:
print("FAIL")
failed += 1
print(f"\n=== Résultat: {created} créées, {failed} échouées ===")
# Retest critical relations
print("\n=== Vérification finale ===\n")
all_ok = True
for table, relation, fields in critical_relations:
ok, msg = test_relation(table, relation, fields)
status = "OK" if ok else "FAIL"
print(f" {status}: {table}.{relation}")
if not ok:
all_ok = False
if all_ok:
print("\n🎉 Toutes les relations fonctionnent !")
else:
print("\n❌ Certaines relations ne fonctionnent pas encore.")
if __name__ == '__main__':
main()