API-referentie

Data opvragen

Alle lijstquery's in de Tidal Control API volgen dezelfde patronen voor paginering, filtering en sortering. Leer ze eenmalig en ze gelden overal.

Verzoekstructuur

Elk GraphQL-verzoek is een POST naar https://portal.tidalcontrol.com/graphql met een JSON-body:

{
  "query": "...",
  "variables": { ... }
}

Gebruik altijd variables in plaats van waarden rechtstreeks in de querystring te plaatsen. Dat houdt query's leesbaar en voorkomt injectieproblemen.

Paginering

Alle *_paged-query's gebruiken cursor-gebaseerde paginering volgens de Relay Connection-specificatie. Vraag een pagina op met first (paginagrootte) en after (cursor van de vorige pagina):

query ExportRisks($first: Int, $after: String) {
  risks_paged(first: $first, after: $after) {
    edges {
      node {
        id
        name
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Variabelen:

{ "first": 50, "after": null }

Als pageInfo.hasNextPage true is, geef je pageInfo.endCursor door als after in het volgende verzoek.

Alle pagina's ophalen (Python)

def fetch_all(graphql_fn, query, page_size=50):
    """Haal alle pagina's op van een gepagineerde query."""
    results = []
    cursor = None

    while True:
        data = graphql_fn(query, {"first": page_size, "after": cursor})
        key = next(iter(data))
        page = data[key]
        results.extend(edge["node"] for edge in page["edges"])

        if not page["pageInfo"]["hasNextPage"]:
            break
        cursor = page["pageInfo"]["endCursor"]

    return results
Tip

Een paginagrootte van 50 is een goede standaard. Je kunt hoger gaan, maar zeer grote pagina's kunnen de responstijd verlengen.

Filtering

Geef een filter-argument mee om resultaten te beperken. Elke entiteit heeft zijn eigen filtertype met relevante velden. Alle filtervelden zijn optioneel. Laat velden die je niet nodig hebt weg.

Dit voorbeeld haalt alleen open issues met hoge prioriteit op:

query FilteredIssues($filter: IssueFilter) {
  issues_paged(first: 50, filter: $filter) {
    edges {
      node {
        id
        name
        priority
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Variabelen:

{
  "filter": {
    "closed": false,
    "priority": ["HIGH"]
  }
}

Veelgebruikte filtervelden

Beschikbaar op de meeste entiteiten:

  • archived: Boolean: gearchiveerde records in- of uitsluiten
  • assignees: [UUID!]: filteren op toegewezen gebruikers-ID's
  • search: String: vrije-tekstzoekactie op naam/omschrijving

Activiteiten (issues, taken):

  • closed: Boolean: open of gesloten activiteiten
  • controls: [UUID!]: activiteiten gekoppeld aan specifieke controls
  • assets: [UUID!]: activiteiten gekoppeld aan specifieke assets
  • status: [ActivityDueStatus!]: NOT_DUE_SOON, DUE_SOON, OVERDUE, NOT_SET

Alleen issues:

  • priority: [Priority!]: LOW, MEDIUM, HIGH
  • type: [IssueType!]: bijv. AUDIT_FINDING, INCIDENT, CONTROL_GAP

Risico's en controls:

  • frameworks: [UUID!]: gekoppeld aan specifieke frameworks
  • references: [UUID!]: gekoppeld aan specifieke frameworkreferenties

Sortering

Geef een sort-array mee om de volgorde van resultaten te bepalen. Elk sorteerelement geeft een field-naam en een optionele direction (ASC of DESC) aan:

query SortedRisks {
  risks_paged(
    first: 50,
    sort: [{ field: "sequenceId", direction: ASC }]
  ) {
    edges {
      node {
        sequenceId
        name
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Je kunt meerdere sorteervelden opgeven. Veelgebruikte velden zijn name, sequenceId, createdDate, notBefore en expires.

Records tellen

Gebruik *_count-query's om een totaaltelling op te halen zonder alle gegevens op te vragen:

query {
  risks_count
  controls_count(filter: { archived: false })
  issues_count(filter: { closed: false, priority: ["HIGH"] })
}

Foutafhandeling

Een geslaagd antwoord heeft altijd HTTP-statuscode 200, ook wanneer de GraphQL-query zelf fouten bevat. Controleer de errors-array in de response-body:

{
  "data": null,
  "errors": [
    {
      "message": "Unauthorized",
      "locations": [{ "line": 1, "column": 1 }]
    }
  ]
}

Veelvoorkomende fouten:

  • Unauthorized: je token ontbreekt of is verlopen; authenticeer opnieuw
  • Field does not exist: controleer de veldnaam in het schema
  • Variable … expected type …: een filtervariabele heeft het verkeerde type