Skip to main content

GraphQL Editor

DISCLAIMER: This page is continuously updated with new content.

Pre-requisites​

Please read through the following generic GraphQL guide first.

GraphQL basics​

What it is​

  • Query language for APIs
  • Runtime for fulfilling queries with existing data

Features​

  • Ask exactly the data you need
  • Get many resources in single request
  • Single endpoint + type system: organized in terms of types and fields, not endpoints
  • No-version API evolution
  • Integration with own data + code

Pros​

  • Speed + no over-fetching/under-fetching (ask and get exactly what you need)
  • Suitable for complex microservice-based systems (unified API)
  • Hierarchical structure
  • Data "shaping"
  • Strong typing
  • No "latest" version (Facebook use case)

Cons​

  • Query complexity can be high ⇒ system load (query depth, recursion, etc.)
  • Complex caching (queries can be unpredictable, dynamic)
  • Complex rate-limiting

Thinking in Graphs​

With GraphQL, you can model your business domain as a "graph" by defining a schema.

Within the schema, you define:

  • different types of nodes
  • how they connect/relate to each other

Types may reference other types, e.g., a BGP route may reference a prefix or AS path.

You can use GQL over your current business logic (but not implement it in GraphQL).

The idea is that you treat your API as an expressive shared language, expressing "how" clients consume the data (not "what" data). GraphQL enables working with legacy data. The GQL schema should be expanded/iterated gradually and frequently.

Concepts​

  • Queries on objects fields, using optional (variable) arguments
  • Directives for forming dynamic composite queries
  • Mutations to modify server-side data
  • Type system: queries/mutations, scalars, enums, interfaces, unions
  • Type language: agnostic (use your favorite!)
  • Queries/mutations validated and executed at run-time by GQL resolvers
  • Introspection capabilities by design

Type System​

Example of basic Prefixes type:

type Prefixes {
data_source_count: Int
id: uuid
ip_version: Int
routes(
distinct_on: [routes_select_column!]
limit: Int
offset: Int
order_by: [routes_order_by!]
where: routes_bool_exp
): [routes!]!
}

Example of interface:

interface Identifiable {
id: String!
}

Example of an Autonomous System entity (AutSystem) implementing the example interface:

type AutSystem implements Identifiable {
id: String!
number: Int!
}

Subscriptions​

Subscriptions are a GQL feature that allows a server to send data to its clients when a specific event happens. They are typically implemented with WebSockets. The server maintains a steady connection to its subscribed client. Subscriptions break the "Request-Response-Cycle" as follows:

  • Client initially opens up a long-lived connection to the server
  • Client sends a subscription query that specifies which event it is interested in
  • Every time this particular event happens, the server uses the connection to push the event data to the subscribed client(s).

Best Practices​

  • Serve over HTTP(S) via single endpoint
GET: https://myapi/graphql?query={object{field}}
POST:
{
"query": "...",
"operationName": "...",
"variables": { "myVariable": "someValue", ... }
}
Response:
{
"data": { ... },
"errors": [ ... ]
}
  • JSON syntax is typical in responses (note that specification does not require it!)
  • Versioning: continuous evolution & add/deprecate objects and fields
  • Nullable/non-nullable types should be explicitly defined
  • Authorization: delegate to business logic layer (not the GQL layer!). Frameworks like Hasura offer appropriate support for this.
  • Pagination: up to API designer (typically cursor-based)
  • Batching & Caching: expose globally unique IDs for clients to use/cache on
  • In general: most things besides the query contexts are kept out of the spec on purpose, providing to the developer/operator freedom to implement their own business logic!

BGPQL: A GQL API for BGP data​

Please check the following sections on this topic. For simplicity, we mention here some sample primitives and associations/relationships that are part of this GQL API for BGP data.

Sample primitives:

  • dataSources
  • prefixes
  • autonomousSystems
  • peerings
  • routes

Sample associations/relationships:

  • dataSources → all
  • autonomousSystems → routes.Origin, routes.Neighbor, peerings.Left, peerings.Right
  • prefixes → routes.prefix

Sample query:

query MyV6Prefixes {
prefixes(
distinct_on: network
where: {
routes: {originAutonomousSystem: {number: {_eq: "50414"}}, data_source_count: {_gte: 10}},
ip_version: {_eq: 6}
} order_by: {network: asc}
) {
network
}
}

Sample response:

{
"data": {
"prefixes": [
{"network": "2a12:bc0::/48"},
{"network": "2a12:bc0:1::/48"},
{"network": "2a12:bc0:2::/48"}
]
}
}

We provide a BGPQL editor based on graphiql.

The Editor contains:

  • an Explorer (leftmost tab), to navigate the API and form queries/mutations/subscriptions with simple point and click.
  • the History (left-inner tab) of your past queries/mutations/subscriptions.
  • a Prettify functionality for syntax beauitification.
  • a Docs link redirecting to our API reference.

You can write the queries/mutations/subscriptions directly (center tab up), input query variables and optionally tune the POST request headers used (center tab down). On the rightmost side you see the resulting JSON data after you run the query/subsctiption/mutation.

BGPQL Raw API Primitives​

These can be found here. Note that the accessible primitives differ depending on you role as Viewer (read-only access) or Editor (read+write access). In the current documentation we provide a more user-friendly API description with appropriate examples.

BGPQL Entities​

Looking Glass​

Ingested state of the system. They correspond to the Looking Glass page contents of the UI.

dataSources​

Sources of data; a data service like RIS Live may employ 100s of dataSources. 1-N relationships with: autonomousSystemDataSourceAssociations, prefixDataSourceAssociations, peeringDataSourceAssociations, routeDataSourceAssociations, rpkiROADataSourceAssociations, dataSourceMetadatas. Fields: id, locality (not used), selector, data_service.

Example query/response:

query dataSources {
dataSources {
id
selector
data_service
}
}

{
"data": {
"dataSources": [
{
"id": "e7f752ec-7847-4951-8b34-433a72e30dd6",
"selector": {
"host": "rrc15",
"peer_ip": "187.16.222.156",
"peer_asn": 264479
},
"data_service": "RIS_LIVE"
}
]
}
}

autonomousSystems​

The autonomous systems collected by the BGP updates (AS paths in announcements) seen by the system. 1-N relationships with: autonomousSystemDataSourceAssociations, routeOriginAutonomousSystems, routeNeighborAutonomousSystems, peeringLeftAutonomousSystems, peeringRightAutonomousSystems. Fields: id, number, data_source_count, orig_prefixes_diff (args: base_selector_field_name, base_selector_field_value, base_service, comp_selector_field_name, comp_selector_field_value, comp_service).

Example query/response:

query autonomousSystems {
autonomousSystems {
number
data_source_count
id
}
}

{
"data": {
"autonomousSystems": [
{
"number": 20080,
"data_source_count": 3,
"id": "d54e0509-9f64-4123-a879-54e9852a8a48"
}
]
}
}

autonomousSystemDataSourceAssociations​

Data source - AS associations. 1-1 relationships with: autonomousSystem, dataSource. Fields: id, data_time.

Example query/response:

query autonomousSystemDataSourceAssociations {
autonomousSystemDataSourceAssociations {
data_time
id
dataSource {
id
}
autonomousSystem {
id
}
}
}

{
"data": {
"autonomousSystemDataSourceAssociations": [
{
"data_time": "2023-02-16T22:20:39.49+00:00",
"id": "2a2c044b-e946-4abe-8b9a-0752b5a583e1",
"dataSource": {
"id": "d1d0af52-3e83-4897-b9a4-4a6d3c79a2bc"
},
"autonomousSystem": {
"id": "596cc059-a99d-4779-9910-0c841645ffb2"
}
}
]
}
}

prefixes​

The prefixes collected by the BGP updates seen by the system. 1-N relationships with prefixDataSourceAssociations, routes. Fields: id, configured_prefix_best_match, data_source_count, ip_version (auto-generated), mask_length (auto-generated), network, is_sub_prefix (args: network), is_sub_prefix_multiple (args: networks), as_paths_diff (args: base_service, comp_service, end_asn).

Example query/response:

query Prefixes {
prefixes {
configured_prefix_best_match
data_source_count
id
ip_version
mask_length
network
}
}

{
"data": {
"prefixes": [
{
"configured_prefix_best_match": "",
"data_source_count": 126,
"id": "f8d2a4f8-04ba-4b5f-8312-63b31cfd9a7d",
"ip_version": 6,
"mask_length": 48,
"network": "2a12:bc0::/48"
}
]
}
}

prefixDataSourceAssociations​

Data source - prefix associations. 1-1 relationships with: prefix, dataSource. Fields: id, withdrawn, data_time.

Example query/response:

query PrefixDataSourceAssociations {
prefixDataSourceAssociations {
data_time
withdrawn
prefix {
id
}
dataSource {
id
}
}
}

{
"data": {
"prefixDataSourceAssociations": [
{
"data_time": "2023-03-02T06:51:59+00:00",
"withdrawn": false,
"prefix": {
"id": "cf931693-8a38-4d11-8bd5-5c9fc00e067a"
},
"dataSource": {
"id": "e401023b-3328-4804-8ccd-6f892bb8ec44"
}
}
]
}
}

peerings​

The autonomous system peerings (AS-level) collected by the BGP updates (AS paths in announcements) seen by the system. Note that for a path of the form AS1 AS2 AS3, with AS3 as the origin AS, we derive two peerings with the following directions: AS3->AS2, AS2->AS1. 1-1 relationships with: autonomousSystem. 1-N relationships with: peeringDataSourceAssociations. Fields: id, data_source_count.

Example query/response:

query Peerings {
peerings {
data_source_count
id
leftAutonomousSystem {
number
}
rightAutonomousSystem {
number
}
}
}

{
"data": {
"peerings": [
{
"data_source_count": 92,
"id": "2d91eb81-4a84-4d57-9868-152549a7f90a",
"leftAutonomousSystem": {
"number": 50414
},
"rightAutonomousSystem": {
"number": 20473
}
}
]
}
}

peeringDataSourceAssociations​

Data source - peering associations. 1-1 relationships with: peering, dataSource. Fields: id, data_time.

Example query/response:

query PeeringDataSourceAssociations {
peeringDataSourceAssociations {
id
data_time
peering {
id
}
dataSource {
id
}
}
}

{
"data": {
"peeringDataSourceAssociations": [
{
"id": "5a65fbde-06ae-42f3-b107-753f62adb398",
"data_time": "2023-03-02T03:58:36.84+00:00",
"peering": {
"id": "727733c1-f07d-45e9-a8a3-65f235d8c458"
},
"dataSource": {
"id": "9d7703e5-e5fc-4797-a6cd-b45eee0b7bce"
}
}
]
}
}

routes​

The routes correlating prefixes, autonomous systems, paths and other update info collected by the BGP updates seen by the system. 1-1 relationships with: autonomousSystem, prefix. 1-N relationships with: routeDataSourceAssociations. Fields: id, data_source_count, as_path, rpki_status.

Example query/response:

query Routes {
routes {
id
data_source_count
as_path
prefix {
network
}
originAutonomousSystem {
number
}
neighborAutonomousSystem {
number
}
rpki_status
}
}

{
"data": {
"routes": [
{
"id": "394fcad8-720d-4162-8a19-ba169efe4fe4",
"data_source_count": 2,
"as_path": "57695 60068 2914 3356 3908 721 27064 5927",
"prefix": {
"network": "2608:140:c::/48"
},
"originAutonomousSystem": {
"number": 5927
},
"neighborAutonomousSystem": {
"number": 27064
},
"rpki_status": "not-found"
}
]
}
}

routeDataSourceAssociations​

Data source - route associations. 1-1 relationships with: route, dataSource. Fields: id, communities, data_time.

Example query/response:

query RouteDataSourceAssociations {
routeDataSourceAssociations {
communities
data_time
id
route {
id
}
dataSource {
id
}
}
}

{
"data": {
"routeDataSourceAssociations": [
{
"communities": [
"25091:41252",
"65300:20144"
],
"data_time": "2023-03-02T09:00:20+00:00",
"id": "96d822e0-17d2-4ea3-878f-aeff84be0050",
"route": {
"id": "ca0b7a85-bce6-4dd0-8c48-75426bbd6638"
},
"dataSource": {
"id": "7d129b0e-a0c3-4829-91cf-57ae283e1d7f"
}
}
]
}
}

rpkiROAs​

RPKI ROAs downloaded from RPKI repositories. 1-N relationships with: rpkiROADataSourceAssociations. Fields: id, autonomous_system_number, data_source_count, expiry_time, ip_version (auto-generated), mask_length (auto-generated), max_length, network.

Example query/response:

query RPKIROAs {
rpkiROAs {
autonomous_system_number
data_source_count
expiry_time
id
ip_version
mask_length
max_length
network
}
}

{
"data": {
"rpkiROAs": [
{
"autonomous_system_number": 16509,
"data_source_count": 1,
"expiry_time": "0001-01-01T00:00:00+00:00",
"id": "645aafc8-423b-4ee4-98b5-5ba65fad4221",
"ip_version": 4,
"mask_length": 24,
"max_length": 24,
"network": "2.255.190.0/24"
}
]
}
}

rpkiROADataSourceAssociations​

Data source - RPKI ROA associations. 1-1 relationships with: rpki_roa, dataSource. Fields: id, data_time.

Example query/response:

query RPKIROADataSourceAssociations {
rpkiROADataSourceAssociations {
data_time
id
dataSource {
id
}
rpkiROA {
id
}
}
}

{
"data": {
"rpkiROADataSourceAssociations": [
{
"data_time": "2023-02-09T13:31:51+00:00",
"id": "475ffd6f-93cd-4271-a1be-bb96104d1b8f",
"dataSource": {
"id": "433de726-02b4-4431-9366-bf5d4653d0c9"
},
"rpkiROA": {
"id": "645aafc8-423b-4ee4-98b5-5ba65fad4221"
}
}
]
}
}

dataSourceMetadatas​

TBD

Configuration/Setup​

Configuration and setup entities for configuring the system. They correspond to the Setup page contents of the UI.

alertReceiverTypes​

Read-only available types of alert receivers, with their internal IDs and external labels. Currently available: Email, Slack (via Email). 1-N relationship with alertSubscriptions. Fields: id, label.

Example query/response:

query alertReceiverTypes {
alertReceiverTypes {
id
label
}
}

{
"data": {
"alertReceiverTypes": [
{
"id": "email",
"label": "Email"
},
{
"id": "slack",
"label": "Slack"
}
]
}
}

alertSeverities​

Read-only available severities of alerts, with their internal IDs and external labels. Currently available: warning, critical. 1-N relationship with alertSubscriptions. Fields: id, label.

Example query/response:

query alertSeverities {
alertSeverities {
id
label
}
}

{
"data": {
"alertSeverities": [
{
"id": "warning",
"label": "warning"
},
{
"id": "critical",
"label": "critical"
}
]
}
}

alertTypes​

Read-only available types of alerts, with their internal IDs and external labels. Currently available: Exact Prefix Hijack, Sub-Prefix Hijack, Route Leak, New Neighbor, Neighbor Leak/Hijack, Squatting, RPKI-Invalid Detection, RPKI-Invalid Announcement, RPKI-Invalid Propagation, RPKI-NotFound Propagation, Bogon (Exact-)Prefix,Bogon (Sub-)Prefix, Bogon AS, AS Path Comparison, Prefix Comparison, Custom. Fields: id, label.

Example query/response:

query alertTypes {
alertTypes {
id
label
}
}

{
"data": {
"alertTypes": [
{
"id": "unexpected_originated_prefixes",
"label": "Route Leak"
},
{
"id": "unexpected_neighbor_prefix",
"label": "Neighbor Leak/Hijack"
},
{
"id": "as_origin_violation_exact",
"label": "Exact Prefix Hijack"
},
{
"id": "as_origin_violation_sub",
"label": "Sub-Prefix Hijack"
},
{
"id": "sub_prefix",
"label": "Possible Sub-prefix Hijack"
},
{
"id": "squatting",
"label": "Squatting"
},
{
"id": "rpki_invalid_detect",
"label": "RPKI-Invalid Detection"
},
{
"id": "rpki_invalid_announce",
"label": "RPKI-Invalid Announcement"
},
{
"id": "rpki_invalid_propagate",
"label": "RPKI-Invalid Propagation"
},
{
"id": "rpki_not_found_propagate",
"label": "RPKI-NotFound Propagation"
},
{
"id": "unexpected_peering",
"label": "New Neighbor"
},
{
"id": "bogon_as",
"label": "Bogon AS"
},
{
"id": "as_path_diffs",
"label": "AS Path Comparison"
},
{
"id": "prefix_diffs",
"label": "Prefix Comparison"
},
{
"id": "custom",
"label": "Custom"
},
{
"id": "bogon_prefix_exact",
"label": "Bogon (Exact-)Prefix"
},
{
"id": "bogon_prefix_sub",
"label": "Bogon (Sub-)Prefix"
}
]
}
}

alertSubscriptions​

Configured alert rule GQL subscriptions. 1-1 relationship with alertReceiverType, alertSeverity, alertType. Fields: id, name, type, severity, description, query, vars, fire_alert_regex, receiver_type,receiver_endpoint, activation_time.

Example query/response:

query alertSubscriptions {
alertSubscriptions {
id
fire_alert_regex
description
activation_time
name
query
receiver_endpoint
receiver_type
severity
type
vars
}
}

{
"data": {
"alertSubscriptions": [
{
"id": "ecb0b70a-466f-49bd-b41a-5dd9542d7317",
"fire_alert_regex": "^.*prefixes.*\\[.*network.*$",
"description": "Unexpected prefixes in the list of prefixes that are announced by configured ASes",
"activation_time": "2023-02-17T15:10:00.351226+00:00",
"name": "B-Root USC ISI Route Leak",
"query": "subscription RouteLeak($asn: bigint!, $prefixes: [cidr!] = [], $ds_thres: Int!) {prefixes(where: {routes: {originAutonomousSystem: {number: {_eq: $asn}}}, network: {_nin: $prefixes}, data_source_count: {_gte: $ds_thres} } order_by: {network: asc}) {network }}",
"receiver_endpoint": "notifications@codebgp.com",
"receiver_type": "email",
"severity": "critical",
"type": "unexpected_originated_prefixes",
"vars": {
"asn": "394353",
"ds_thres": "1",
"prefixes": [
"192.228.79.0/24",
"199.9.14.0/23",
"199.9.14.0/24",
"199.9.15.0/24",
"2001:500:200::/44",
"2001:500:200::/48",
"2001:500:201::/48",
"2001:500:204::/48",
"2001:500:205::/48",
"2001:500:206::/48",
"2001:500:207::/48",
"2001:500:208::/48",
"2001:500:209::/48",
"2001:500:84::/48"
]
}
}
]
}
}

configuredAutonomousSystems​

Read-only configured AS filters, populated by writable manuallyConfiguredAutonomousSystems. Fields: number, data_service (not used).

Example query/response:

query configuredAutonomousSystems {
configuredAutonomousSystems {
number
}
}

{
"data": {
"configuredAutonomousSystems": [
{
"number": 50414
}
]
}
}

configuredPrefixes​

Read-only configured prefix filters, populated by writable manuallyConfiguredPrefixes. Fields: network, ip_version (auto-generated), mask_length (auto-generated), data_service (not used).

Example query/response:

query configuredPrefixes {
configuredPrefixes {
ip_version
mask_length
network
}
}

{
"data": {
"configuredPrefixes": [
{
"ip_version": 4,
"mask_length": 24,
"network": "192.203.230.0/24"
}
]
}
}

configuredDataServices​

Configured data services. Fields: configuration (not used), id, data_service, data_source_selector_fields (not used), enabled, filter_on_autonomous_system, filter_on_data_source (not used), filter_on_prefix.

Example query/response:

query configuredDataServices {
configuredDataServices {
id
filter_on_prefix
filter_on_autonomous_system
enabled
data_service
}
}

{
"data": {
"configuredDataServices": [
{
"id": "5615dc61-a5c3-4447-8909-f48b51abd97a",
"filter_on_prefix": "any",
"filter_on_autonomous_system": "origin",
"enabled": true,
"data_service": "RPKI"
},
{
"id": "b9612cdd-80d5-40e8-be91-3ed93ce788e9",
"filter_on_prefix": "any",
"filter_on_autonomous_system": "origin",
"enabled": true,
"data_service": "BGP_ROUTER"
},
{
"id": "d4d6deb5-a705-4368-a4e7-b0b03fe075a1",
"filter_on_prefix": "any",
"filter_on_autonomous_system": "origin",
"enabled": true,
"data_service": "CODEBGP_MONITOR"
},
{
"id": "20991c99-ba10-4395-bb11-87f1a5a377fc",
"filter_on_prefix": "any",
"filter_on_autonomous_system": "origin",
"enabled": true,
"data_service": "RIS_LIVE"
}
]
}
}

configuredDataSources​

[NOT USED] Read-only configured data source filters, populated by writable manuallyConfiguredDataSources. Fields: data_service, selector.

manuallyConfiguredAutonomousSystems​

Manually configured AS filters. Fields: id, number, reject (not used).

Example query/response:

query manuallyConfiguredAutonomousSystems {
manuallyConfiguredAutonomousSystems {
id
number
}
}

{
"data": {
"manuallyConfiguredAutonomousSystems": [
{
"id": "d8809422-e1a0-4d41-a752-0473e954ff34",
"number": 50414
}
]
}
}

manuallyConfiguredPrefixes​

Manually configured prefix filters. Fields: id, ip_version (auto-generated), mask_length (auto-generated), network, reject (not used).

Example query/response:

query manuallyConfiguredPrefixes {
manuallyConfiguredPrefixes {
ip_version
mask_length
network
id
}
}

{
"data": {
"manuallyConfiguredPrefixes": [
{
"ip_version": 4,
"mask_length": 24,
"network": "212.46.55.0/24",
"id": "e37f385a-6ada-4b81-872c-b69010277412"
}
]
}
}

manuallyConfiguredDataSources​

[NOT USED] Manually configured data source filters. Fields: id, reject, data_service, selector.

Filtering via where​

Queries/subscriptions/mutations can target specific data by using where filters. Example:

subscription NeighborsToWhichPrefixIsAnnounced($asns: [bigint!] = [], $prefix: cidr!) {
routes(
where: {originAutonomousSystem: {number: {_in: $asns}}, prefix: {network: {_eq: $prefix}}}
) {
originAutonomousSystem {
number
}
neighborAutonomousSystem {
number
}
routeDataSourceAssociations {
communities_string
}
}
}

You can also form more complex BGPQL queries that are parameterized accordingly, as follows.

What prefixes from that AS are visible from my AS?​

subscription PrefixesFromOriginASNVisibleFromASN($originASNRegex: String!, $visibleFromASNRegex: String!) {
prefixes(
where: {routes: {_and: [{as_path: {_regex: $originASNRegex}}, {as_path: {_regex: $visibleFromASNRegex}}]}}
order_by: {network: asc}
) {
network
}
}

Sample variables:

{"originASNRegex":"( |^)2149$","visibleFromASNRegex":"( |^)3557( |$)"}

Which prefixes leak visibly from my AS?​

subscription LeakedPrefixesMyASNOriginates($asn: bigint!, $prefixes: [cidr!] = [], $ds_thres: Int!) {
prefixes(where: {routes: {originAutonomousSystem: {number: {_eq: $asn}}}, network: {_nin: $prefixes}, data_source_count: {_gte: $ds_thres} } order_by: {network: asc}) {
network
}
}

Sample variables:

{asn: 65001, prefixes: ["139.91.0.0/16"], ds_thres: 1}

What are the RPKI-invalids my AS propagates?​

subscription RPKIInvalidPrefixesPropagatedByMyASNs($asn_regex: String, $rpki_status: String!) {
routes(
order_by: {as_path: asc, prefix: {network: asc}}
where: {as_path: {_iregex: $asn_regex}, rpki_status: {_eq: $rpki_status}}
) {
prefix {
network
}
as_path
}
}

Sample variables:

{asn_regex: "( 8522 )|(^8522 )|( 8522$)|(^8522$)", rpki_status: "invalid"}

Which paths towards my prefixes contain ASes that should not be present?​

subscription ASPathPresence($prefixes: [String!] = [], $asn_present_regex: String!) {
routes(
where: {prefix: {configured_prefix_best_match: {_in: $prefixes}}, as_path: {_iregex: $asn_present_regex}}
order_by: {as_path: asc, prefix: {network: asc}}
) {
prefix {
network
}
as_path
}
}

Sample variables:

{prefixes: ["139.91.0.0/16"],asn_present_regex:( 8522 )|(^8522 )|( 8522$)|(^8522$)"}

Aggregates​

Aggregate functions on entities.

Format: <entity>Aggregate.

Fields: nodes (with sub-fields same as <entity> ), aggregate (with functions such as count, min, max, avg, etc.)

Example query/response:

query dataSourcesAggregate {
dataSourcesAggregate {
aggregate {
count
}
}
}

{
"data": {
"dataSourcesAggregate": {
"aggregate": {
"count": 5752
}
}
}
}