API-referentie

Controls exporteren

Controls worden geëxporteerd met de controls_paged-query. Elke control bevat zijn categorie, effectiviteitsstatus, gekoppelde risico's, assets en frameworkreferenties.

Basisquery

query ExportControls($first: Int, $after: String, $filter: ControlFilter) {
  controls_paged(first: $first, after: $after, filter: $filter) {
    edges {
      node {
        id
        sequenceId
        customId
        name
        description
        category
        notBefore
        expires
        archived
        risks {
          id
          sequenceId
          name
          treatment
        }
        assets {
          id
          name
        }
        assignments {
          assignmentType
          user {
            name
            email
          }
        }
        controlStatus {
          progress {
            effectiveness
            closedTasks
            totalTasks
          }
          issues {
            open
            closed
          }
        }
        attributes {
          key
          value
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Variabelen voor de eerste pagina:

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

Belangrijke velden

  • sequenceId — het leesbare nummer zoals getoond in de UI (bijv. C-15)
  • customId — optionele aangepaste identifier (bijv. ISO 27001-controlreferentie)
  • category — de controlcategorie, bijv. ACCESS_CONTROL, DATA_SECURITY_AND_PRIVACY
  • controlStatus.progress.effectivenessUNDETERMINED, INEFFECTIVE of EFFECTIVE
  • controlStatus.progress.closedTasks / totalTasks — taakvoltooiingsverhouding

Volledig exportscript (Python)

import requests
import csv

GRAPHQL_URL = "https://portal.tidalcontrol.com/graphql"

QUERY = """
query ExportControls($first: Int, $after: String, $filter: ControlFilter) {
  controls_paged(first: $first, after: $after, filter: $filter) {
    edges {
      node {
        id
        sequenceId
        customId
        name
        description
        category
        notBefore
        expires
        archived
        risks { id sequenceId name treatment }
        assets { id name }
        assignments { assignmentType user { name email } }
        controlStatus {
          progress { effectiveness closedTasks totalTasks }
          issues { open closed }
        }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
"""


def graphql(query, variables, access_token):
    r = requests.post(
        GRAPHQL_URL,
        json={"query": query, "variables": 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 export_controls(access_token, include_archived=False):
    controls = []
    cursor = None
    control_filter = {"archived": include_archived} if include_archived else {}

    while True:
        data = graphql(
            QUERY,
            {"first": 50, "after": cursor, "filter": control_filter},
            access_token,
        )
        page = data["controls_paged"]
        controls.extend(edge["node"] for edge in page["edges"])

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

    return controls


def to_csv(controls, output_path):
    if not controls:
        print("Geen controls gevonden.")
        return

    with open(output_path, "w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        writer.writerow([
            "ID", "Volgnummer", "Aangepast ID", "Naam", "Omschrijving",
            "Categorie", "Startdatum", "Vervaldatum", "Gearchiveerd",
            "Effectiviteit", "Gesloten taken", "Totaal taken",
            "Open issues", "Gesloten issues",
            "Gekoppelde risico's", "Gekoppelde assets", "Eigenaren",
        ])
        for c in controls:
            progress = c["controlStatus"]["progress"]
            issues = c["controlStatus"]["issues"]
            owners = [
                a["user"]["email"]
                for a in c["assignments"]
                if a["assignmentType"] == "OWNER"
            ]
            writer.writerow([
                c["id"],
                c["sequenceId"],
                c.get("customId", ""),
                c["name"],
                c.get("description", ""),
                c.get("category", ""),
                c.get("notBefore", ""),
                c.get("expires", ""),
                c["archived"],
                progress["effectiveness"],
                progress["closedTasks"],
                progress["totalTasks"],
                issues["open"],
                issues["closed"],
                ", ".join(r["name"] for r in c["risks"]),
                ", ".join(a["name"] for a in c["assets"]),
                ", ".join(owners),
            ])

    print(f"{len(controls)} controls geëxporteerd naar {output_path}")


if __name__ == "__main__":
    ACCESS_TOKEN = "eyJhbGci..."

    controls = export_controls(ACCESS_TOKEN)
    to_csv(controls, "controls_export.csv")

Filtervoorbeelden

Alleen effectieve controls:

# Filter in Python na het ophalen (effectiviteit zit in controlStatus, niet als top-level filterveld)
effective = [c for c in controls if c["controlStatus"]["progress"]["effectiveness"] == "EFFECTIVE"]

Controls in een specifieke categorie:

{ "filter": { "categories": ["ACCESS_CONTROL"] } }

Controls gekoppeld aan een specifiek framework:

{ "filter": { "frameworks": ["framework-uuid-hier"] } }

Controls met open issues:

met_issues = [c for c in controls if c["controlStatus"]["issues"]["open"] > 0]

Alleen tellen

query {
  controls_count(filter: { archived: false })
}