Zum Hauptinhalt springen

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-TypProtokollAnwendungsfall
GraphQLHTTPSPrimärer Datenzugriff, Queries, Mutations
RESTHTTPSAdministrative Operationen, Datei-Uploads
AMQPRabbitMQEchtzeit-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.

Tenant-spezifische Client-Registrierung

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 erforderlich

Clients 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​

ScopeBeschreibung
octo_apiVollständiger Lese-/Schreibzugriff auf alle OctoMesh-APIs
octo_api.read_onlyNur-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:

OperatorBeschreibung
eqGleich
neqUngleich
gt / gteGrößer als / Größer als oder gleich
lt / lteKleiner als / Kleiner als oder gleich
containsZeichenkette enthält
startsWithZeichenkette beginnt mit
endsWithZeichenkette endet mit
inWert in Liste
AND / ORLogische 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​

CodeBeschreibung
UNAUTHORIZEDFehlendes oder ungültiges Token
FORBIDDENUnzureichende Berechtigungen
ENTITY_NOT_FOUNDAngeforderte Entität existiert nicht
VALIDATION_ERROREingabevalidierung fehlgeschlagen
CK_TYPE_NOT_FOUNDConstruction-Kit-Typ nicht gefunden
ASSOCIATION_ERRORUngü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-TypLimit
GraphQL Queries100 req/min pro Client
Mutations50 req/min pro Client
Datei-Uploads10 req/min pro Client

Bei Überschreitung des Limits erhalten Sie:

HTTP/1.1 429 Too Many Requests
Retry-After: 60

Best Practices​

Query-Optimierung​

  1. Nur benötigte Felder anfordern: Reduziert die Antwortgröße
  2. Paginierung verwenden: Setzen Sie stets den Parameter first
  3. Serverseitig filtern: Nicht clientseitig abrufen und filtern
  4. Zusammengehörige Queries bündeln: In einer einzigen Anfrage kombinieren

Verbindungsverwaltung​

  1. HTTP-Clients wiederverwenden: Connection Pooling nutzen
  2. Token-Refresh handhaben: Automatischen Refresh implementieren
  3. Retry-Logik implementieren: Transiente Fehler abfangen
  4. Timeouts verwenden: Angemessene Anfrage-Timeouts setzen

Sicherheit​

  1. Anmeldedaten sichern: Secrets Management verwenden
  2. Minimale Scopes: Nur benötigte Berechtigungen anfordern
  3. Antworten validieren: Externen Daten nicht vertrauen
  4. Sicher loggen: Keine Tokens oder sensiblen Daten loggen

Nächste Schritte​