API Integration
Dieses Dokument beschreibt, wie Sie sich in die OctoMesh-APIs integrieren, einschließlich Authentifizierung, GraphQL-Operationen und dem Service Client SDK.
API-Überblick
OctoMesh bietet mehrere Methoden für den API-Zugriff:
| API-Typ | Protokoll | Anwendungsfall |
|---|---|---|
| GraphQL | HTTPS | Primärer Datenzugriff, Queries, Mutations |
| REST | HTTPS | Administrative Operationen, Datei-Uploads |
| AMQP | RabbitMQ | Echtzeit-Messaging, Adapter-Kommunikation |
Authentifizierung
OAuth 2.0 / OpenID Connect
OctoMesh verwendet OAuth 2.0 mit OpenID Connect zur Authentifizierung.
Access Tokens erhalten
OctoMesh unterstützt zwei primäre Authentifizierungsmethoden, je nachdem, ob ein Benutzerkontext benötigt wird.
Authorization Code mit PKCE (benutzerbasiert)
Verwenden Sie diesen Flow, wenn Ihre Anwendung im Namen eines Benutzers handelt. Der Benutzer authentifiziert sich interaktiv, und das ausgestellte Token enthält dessen Identität, Rollen und Tenant-Kontext.
Schritt 1 – Leiten Sie den Benutzer zum Authorize-Endpunkt weiter:
GET /connect/authorize?
response_type=code
&client_id=my-application
&redirect_uri=https://my-app.example.com/callback
&scope=openid profile email role octo_api
&acr_values=tenant:{tenantId}
&code_challenge={challenge}
&code_challenge_method=S256
&state={state}
Der Parameter acr_values=tenant:{tenantId} leitet den Identity Service zur Login-Seite des richtigen Tenants. Der Benutzer authentifiziert sich mit den Anmeldedaten und Identity Providern, die für diesen Tenant konfiguriert sind.
Schritt 2 – Tauschen Sie den Authorization Code gegen Tokens:
POST /connect/token HTTP/1.1
Host: identity.octomesh.local
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id=my-application
&code={authorization_code}
&redirect_uri=https://my-app.example.com/callback
&code_verifier={verifier}
Antwort:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "openid profile email role octo_api"
}
Das ausgestellte Access Token enthält die Claims sub, tenant_id, role und allowed_tenants des Benutzers, eingeschränkt auf den ausgewählten Tenant.
Der OAuth-Client muss in jedem Tenant registriert sein, in dem sich Benutzer authentifizieren müssen. Siehe Clients and API Scopes für Details zur Client-Verwaltung.
Client Credentials (Service-to-Service)
Verwenden Sie diesen Flow für Hintergrunddienste und automatisierte Prozesse, an denen kein Benutzer beteiligt ist. Der Client authentifiziert sich mit seinen eigenen Anmeldedaten.
POST /connect/token HTTP/1.1
Host: identity.octomesh.local
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=my-service
&client_secret=my-secret
&scope=octo_api
&acr_values=tenant:my-tenant
acr_values ist erforderlichClients werden pro Tenant registriert, und /connect/token hat kein Tenant-Pfadsegment. Der Formularparameter acr_values=tenant:{tenantId} teilt dem Identity Service mit, in welchem Client-Store des Tenants gesucht werden soll – ohne ihn fällt die Suche auf den System-Tenant zurück, und die Anfrage schlägt für jeden tenant-registrierten Client mit invalid_client fehl.
Antwort:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "octo_api"
}
Client-Credentials-Tokens haben keine Benutzeridentität (sub-Claim) und keine tenant_id / allowed_tenants-Claims; nachgelagerte Dienste überspringen daher für sie die Route-Tenant-Autorisierungsprüfung. Die Tenant-Bindung wird ausschließlich zum Zeitpunkt der Token-Ausstellung hergestellt, und zwar dadurch, welcher Client-Store eines Tenants über acr_values übereinstimmte.
API Scopes
| Scope | Beschreibung |
|---|---|
octo_api | Vollständiger Lese-/Schreibzugriff auf alle OctoMesh-APIs |
octo_api.read_only | Nur-Lese-Zugriff auf alle OctoMesh-APIs |
Das Token verwenden
Fügen Sie das Token in den Authorization-Header ein:
GET /tenants/my-tenant/graphql HTTP/1.1
Host: asset-repo.octomesh.local
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
GraphQL API
Endpunkt
https://{asset-repo-host}/tenants/{tenantId}/graphql
GraphQL Playground (interaktiver Explorer):
https://{asset-repo-host}/tenants/{tenantId}/graphql/playground
Query-Struktur
Das GraphQL-Schema ist in zwei Haupt-Query-Roots organisiert:
type Query {
runtime: RuntimeQuery # Master data (MongoDB)
streamData: StreamQuery # Time-series data (CrateDB)
}
type Mutation {
runtime: RuntimeMutation
}
Runtime Queries
Alle Entitäten eines Typs abfragen:
query GetEnergyMeters {
runtime {
rtIndustryEnergyEnergyMeter(
first: 50
after: "cursor"
filter: {
status: { eq: "Active" }
}
orderBy: { name: ASC }
) {
items {
rtId
ckTypeId
wellKnownName
name
serialNumber
status
location {
items {
rtId
name
address
}
}
}
pageInfo {
hasNextPage
endCursor
totalCount
}
}
}
}
Nach ID abfragen:
query GetMeterById($rtId: OctoObjectId!) {
runtime {
rtIndustryEnergyEnergyMeterById(rtId: $rtId) {
rtId
name
serialNumber
readings(first: 10) {
items {
timestamp
value
}
}
}
}
}
Nach wellKnownName abfragen:
query GetMeterByName {
runtime {
rtIndustryEnergyEnergyMeterByWellKnownName(
wellKnownName: "main-building-meter"
) {
rtId
name
}
}
}
Stream Data Queries
Zeitreihendaten abfragen:
query GetMeterReadings($rtId: OctoObjectId!) {
streamData {
tsIndustryEnergyMeterReading(
first: 1000
filter: {
rtId: { eq: $rtId }
timestamp: {
gte: "2024-01-01T00:00:00Z"
lte: "2024-01-31T23:59:59Z"
}
}
orderBy: { timestamp: DESC }
) {
items {
rtId
timestamp
voltage
current
power
energy
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
Mutations
Entität erstellen:
mutation CreateEnergyMeter {
runtime {
createRtIndustryEnergyEnergyMeter(
entity: {
wellKnownName: "new-meter-001"
attributes: {
name: "New Energy Meter"
serialNumber: "EM-2024-001"
manufacturer: "Siemens"
status: Active
}
}
) {
rtId
wellKnownName
}
}
}
Entität aktualisieren:
mutation UpdateEnergyMeter($rtId: OctoObjectId!) {
runtime {
updateRtIndustryEnergyEnergyMeter(
rtId: $rtId
entity: {
attributes: {
status: Maintenance
}
}
) {
rtId
status
}
}
}
Entität löschen:
mutation DeleteEnergyMeter($rtId: OctoObjectId!) {
runtime {
deleteRtIndustryEnergyEnergyMeter(rtId: $rtId)
}
}
Assoziationen verwalten:
mutation AddMeterToLocation($meterId: OctoObjectId!, $locationId: OctoObjectId!) {
runtime {
addAssociationRtIndustryEnergyEnergyMeter(
rtId: $meterId
association: "location"
targetRtIds: [$locationId]
) {
rtId
location {
items {
rtId
}
}
}
}
}
Filterung
OctoMesh GraphQL unterstützt umfangreiche Filterung:
query FilteredQuery {
runtime {
rtIndustryEnergyEnergyMeter(
filter: {
AND: [
{ status: { eq: "Active" } }
{
OR: [
{ manufacturer: { contains: "Siemens" } }
{ manufacturer: { contains: "ABB" } }
]
}
{ maxCapacity: { gte: 100 } }
]
}
) {
items {
rtId
name
}
}
}
}
Verfügbare Filteroperatoren:
| Operator | Beschreibung |
|---|---|
eq | Gleich |
neq | Ungleich |
gt / gte | Größer als / Größer als oder gleich |
lt / lte | Kleiner als / Kleiner als oder gleich |
contains | Zeichenkette enthält |
startsWith | Zeichenkette beginnt mit |
endsWith | Zeichenkette endet mit |
in | Wert in Liste |
AND / OR | Logische Operatoren |
Service Client SDK
Das .NET SDK stellt einen typisierten Client für OctoMesh-APIs bereit.
Installation
dotnet add package Meshmakers.Octo.Sdk.ServiceClient
Konfiguration
using Meshmakers.Octo.Sdk.ServiceClient;
var services = new ServiceCollection();
services.AddOctoMeshServiceClient(options =>
{
options.IdentityServiceUrl = "https://identity.octomesh.local";
options.AssetRepositoryUrl = "https://asset-repo.octomesh.local";
options.ClientId = "my-application";
options.ClientSecret = "my-secret";
options.TenantId = "my-tenant";
});
Den Service Client verwenden
public class MyService
{
private readonly IAssetRepositoryClient _client;
public MyService(IAssetRepositoryClient client)
{
_client = client;
}
public async Task<IEnumerable<EnergyMeter>> GetActiveMetersAsync(
CancellationToken cancellationToken)
{
var query = new GraphQLRequest
{
Query = @"
query {
runtime {
rtIndustryEnergyEnergyMeter(
filter: { status: { eq: ""Active"" } }
) {
items {
rtId
name
serialNumber
}
}
}
}"
};
var response = await _client.ExecuteGraphQLAsync<EnergyMeterResponse>(
query, cancellationToken);
return response.Runtime.RtIndustryEnergyEnergyMeter.Items;
}
public async Task<string> CreateMeterAsync(
CreateMeterRequest request,
CancellationToken cancellationToken)
{
var mutation = new GraphQLRequest
{
Query = @"
mutation CreateMeter($input: CreateEnergyMeterInput!) {
runtime {
createRtIndustryEnergyEnergyMeter(entity: $input) {
rtId
}
}
}",
Variables = new { input = request }
};
var response = await _client.ExecuteGraphQLAsync<CreateMeterResponse>(
mutation, cancellationToken);
return response.Runtime.CreateRtIndustryEnergyEnergyMeter.RtId;
}
}
Source Generation
Verwenden Sie Source Generation, um typisierte DTOs zu erstellen:
dotnet add package Meshmakers.Octo.Sdk.SourceGeneration
// Define your models with source generation attributes
[GenerateGraphQLTypes("Industry.Energy/EnergyMeter")]
public partial class EnergyMeterDto
{
}
// Generated code provides:
// - Strongly typed properties matching CK attributes
// - Serialization support
// - GraphQL query/mutation helpers
REST API
Administrative Endpunkte
Tenant-Informationen abrufen:
GET /api/tenants/{tenantId}
Authorization: Bearer {token}
Datei hochladen:
POST /api/tenants/{tenantId}/files
Authorization: Bearer {token}
Content-Type: multipart/form-data
file=@document.pdf
Systemzustand abrufen:
GET /health
Construction Kit Management
CK-Bibliotheken auflisten:
GET /api/tenants/{tenantId}/construction-kits
Authorization: Bearer {token}
CK-Bibliothek hochladen:
POST /api/tenants/{tenantId}/construction-kits
Authorization: Bearer {token}
Content-Type: application/json
{
"libraryId": "MyCompany.Domain",
"version": "1.0.0",
"types": [...],
"enums": [...]
}
Fehlerbehandlung
GraphQL-Fehler
GraphQL-Fehler werden im errors-Array zurückgegeben:
{
"data": null,
"errors": [
{
"message": "Entity not found",
"locations": [{ "line": 2, "column": 3 }],
"path": ["runtime", "rtIndustryEnergyEnergyMeterById"],
"extensions": {
"code": "ENTITY_NOT_FOUND",
"details": {
"rtId": "invalid-id"
}
}
}
]
}
Häufige Fehlercodes
| Code | Beschreibung |
|---|---|
UNAUTHORIZED | Fehlendes oder ungültiges Token |
FORBIDDEN | Unzureichende Berechtigungen |
ENTITY_NOT_FOUND | Angeforderte Entität existiert nicht |
VALIDATION_ERROR | Eingabevalidierung fehlgeschlagen |
CK_TYPE_NOT_FOUND | Construction-Kit-Typ nicht gefunden |
ASSOCIATION_ERROR | Ungültige Assoziationsoperation |
Clientseitige Fehlerbehandlung
try
{
var result = await _client.ExecuteGraphQLAsync<Response>(query, ct);
if (result.Errors?.Any() == true)
{
foreach (var error in result.Errors)
{
_logger.LogError("GraphQL error: {Message} (Code: {Code})",
error.Message,
error.Extensions?["code"]);
}
throw new OctoMeshException("GraphQL operation failed", result.Errors);
}
return result.Data;
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "HTTP request failed");
throw;
}
catch (AuthenticationException ex)
{
_logger.LogError(ex, "Authentication failed");
// Refresh token and retry
throw;
}
Rate Limiting
OctoMesh implementiert Rate Limiting, um die Plattform zu schützen:
| Endpunkt-Typ | Limit |
|---|---|
| GraphQL Queries | 100 req/min pro Client |
| Mutations | 50 req/min pro Client |
| Datei-Uploads | 10 req/min pro Client |
Bei Überschreitung des Limits erhalten Sie:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Best Practices
Query-Optimierung
- Nur benötigte Felder anfordern: Reduziert die Antwortgröße
- Paginierung verwenden: Setzen Sie stets den Parameter
first - Serverseitig filtern: Nicht clientseitig abrufen und filtern
- Zusammengehörige Queries bündeln: In einer einzigen Anfrage kombinieren
Verbindungsverwaltung
- HTTP-Clients wiederverwenden: Connection Pooling nutzen
- Token-Refresh handhaben: Automatischen Refresh implementieren
- Retry-Logik implementieren: Transiente Fehler abfangen
- Timeouts verwenden: Angemessene Anfrage-Timeouts setzen
Sicherheit
- Anmeldedaten sichern: Secrets Management verwenden
- Minimale Scopes: Nur benötigte Berechtigungen anfordern
- Antworten validieren: Externen Daten nicht vertrauen
- Sicher loggen: Keine Tokens oder sensiblen Daten loggen
Nächste Schritte
- SDK Overview: Verfügbare SDK-Bibliotheken erkunden
- Adapter Development: Eigene Integrationen erstellen
- API Reference: Vollständige API-Dokumentation