# Schema-introspectie

> Ontdek met GraphQL-introspectie welke queries, mutations, types en velden de Tidal Control API biedt, en inspecteer een mutation voordat je hem aanroept.

Source: https://tidalcontrol.com/nl/docs/api/schema-introspection

GraphQL heeft ingebouwde introspectie: je kunt het schema zelf bevragen om te ontdekken welke queries, types en velden beschikbaar zijn. Dit is handig bij het bouwen van scripts of het verkennen van beschikbare data.

Alle introspectiequery's gebruiken hetzelfde endpoint en dezelfde authenticatie als gewone queries.

## Alle beschikbare queries weergeven

```graphql
{
  __schema {
    queryType {
      fields {
        name
        description
      }
    }
  }
}
```

Dit geeft de volledige lijst van root query-velden terug: `controls_paged`, `activities_paged`, `risks_paged` enzovoort.

## Alle beschikbare mutations weergeven

Mutations werken op dezelfde manier. Bevraag `mutationType` in plaats van `queryType`:

```graphql
{
  __schema {
    mutationType {
      fields {
        name
        description
      }
    }
  }
}
```

Dit geeft elke mutation terug die je kunt aanroepen: `addExecution`, `addEvidenceToExecution` enzovoort.

> **info:**

Een read-only [machine-to-machine-credential](/docs/api/authentication) kan mutations wel *introspecteren* (de namen opvragen), maar niet *uitvoeren*. Authenticeer via de Device Authorization Grant zodat je token schrijfrechten heeft om een mutation uit te voeren.

### De argumenten van een mutation inspecteren

Voeg `args` toe om te zien welke invoer elke mutation verwacht:

```graphql
{
  __schema {
    mutationType {
      fields {
        name
        args {
          name
          description
          type {
            name
            kind
            ofType {
              name
              kind
            }
          }
        }
      }
    }
  }
}
```

Als een argument een input-object is (bijvoorbeeld `ExecutionInput`), inspecteer dan de velden met `__type` en `inputFields`:

```graphql
{
  __type(name: "ExecutionInput") {
    inputFields {
      name
      description
      type {
        name
        kind
        ofType {
          name
        }
      }
    }
  }
}
```

Zie [Bewijs toevoegen](/docs/api/adding-evidence) voor een uitgewerkt voorbeeld waarin meerdere mutations aan elkaar worden geknoopt.

## Een specifiek type inspecteren

Gebruik `__type` om de velden van een type op te vragen:

```graphql
{
  __type(name: "Activity") {
    fields {
      name
      description
      type {
        name
        kind
        ofType {
          name
          kind
        }
      }
    }
  }
}
```

Vervang `"Activity"` door een willekeurige typenaam (`"Control"`, `"Risk"`, `"Issue"` of `"Assessment"`).

## Enum-waarden weergeven

Gebruik dit om de geldige waarden van een enum-type op te vragen:

```graphql
{
  __type(name: "ActivityDueStatus") {
    enumValues {
      name
    }
  }
}
```

Veelgebruikte enums om te inspecteren: `ActivityDueStatus`, `Effectiveness`, `ControlCategory`, `Priority`, `IssueType`.

## Volledig Python-voorbeeld

Dit script authenticeert en print een samenvatting van het schema: alle query-velden en de velden van een opgegeven type.

```python

TENANT = "{jouw-tenant}"
BASE_URL = f"https://auth.tidalcontrol.com/realms/{TENANT}/protocol/openid-connect"
CLIENT_ID = "portal"
GRAPHQL_URL = "https://portal.tidalcontrol.com/graphql"

def get_token():
    r = requests.post(
        f"{BASE_URL}/auth/device",
        data={"client_id": CLIENT_ID, "scope": "openid"},
    )
    r.raise_for_status()
    device = r.json()

    print(f"\nOpen deze URL in je browser om in te loggen:")
    print(f"  {device['verification_uri_complete']}\n")

    interval = device.get("interval", 5)
    deadline = time.time() + device["expires_in"]

    while time.time() < deadline:
        time.sleep(interval)
        r = requests.post(
            f"{BASE_URL}/token",
            data={
                "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
                "client_id": CLIENT_ID,
                "device_code": device["device_code"],
            },
        )
        if r.status_code == 200:
            print("Authenticatie geslaagd.")
            return r.json()
        body = r.json()
        if body.get("error") == "authorization_pending":
            continue
        if body.get("error") == "slow_down":
            interval += 5
            continue
        r.raise_for_status()

    raise RuntimeError("Authenticatie verlopen.")

def graphql(query, access_token):
    r = requests.post(
        GRAPHQL_URL,
        json={"query": query, "variables": {}},
        headers={"Authorization": f"Bearer {access_token}"},
    )
    r.raise_for_status()
    result = r.json()
    if "errors" in result:
        raise RuntimeError(result["errors"])
    return result["data"]

def list_queries(access_token):
    data = graphql(
        "{ __schema { queryType { fields { name description } } } }",
        access_token,
    )
    fields = data["__schema"]["queryType"]["fields"]
    print(f"\n{len(fields)} query-velden beschikbaar:\n")
    for f in sorted(fields, key=lambda x: x["name"]):
        desc = f.get("description") or ""
        print(f"  {f['name']:<40} {desc}")

def describe_type(type_name, access_token):
    data = graphql(
        f'{{ __type(name: "{type_name}") {{ fields {{ name description '
        f'type {{ name kind ofType {{ name }} }} }} }} }}',
        access_token,
    )
    t = data.get("__type")
    if not t:
        print(f"Type '{type_name}' niet gevonden.")
        return
    print(f"\nVelden van {type_name}:\n")
    for f in t["fields"]:
        type_info = f["type"]
        type_name_str = type_info.get("name") or (type_info.get("ofType") or {}).get("name", "")
        print(f"  {f['name']:<30} {type_name_str}")

if __name__ == "__main__":
    token = get_token()
    access_token = token["access_token"]

    list_queries(access_token)
    describe_type("Activity", access_token)
    describe_type("Control", access_token)
```
