Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions integration-examples/synthetics-examples/API/graphql-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# GraphQL API Check Example
This example API test shows how to query a GraphQL endpoint and validate the returned data using built in matching and JavaScript parsing.
The test and it's configuration are included in this directory:
- [`synthetics_example_graphql_api_check.tf`](./synthetics_example_graphql_api_check.tf)
- Uses the [Splunk Synthetics Terraform provider](https://registry.terraform.io/providers/splunk/synthetics/latest/docs)

## Synthetic API Test
The API test will call [https://countries.trevorblades.com/graphql](https://countries.trevorblades.com/graphql) which provides data on various countries. We use this data for ease of accessibility purposes. But this approach applies just as well to internal GraphQL endpoints where you want to validate the responses being provided or specific values of data within those responses.

Our test will query the info on Canada and check that:
- The endpoint returns the expected national languages in the expected JSON format.
- The endpoint returns an array of states within the country. We will use JavaScript and custom variables to validate that there are still exactly 13 "states" (provences) in Canada.

The expected body we will receive from our request and that we will be validating against looks like so:
```JSON
{
"data":
{
"country":
{
"name": "Canada",
"native": "Canada",
"capital": "Ottawa",
"emoji": "🇨🇦",
"currency": "CAD",
"languages":
[
{
"code": "en",
"name": "English"
},
{
"code": "fr",
"name": "French"
}
],
"states":
[
{
"code": "AB"
},
{
"code": "BC"
},
{
"code": "MB"
},
{
"code": "NB"
},
{
"code": "NL"
},
{
"code": "NS"
},
{
"code": "NU"
},
{
"code": "NT"
},
{
"code": "ON"
},
{
"code": "PE"
},
{
"code": "QC"
},
{
"code": "SK"
},
{
"code": "YT"
}
]
}
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
resource "synthetics_create_api_check_v2" "synthetics_example_graphql_api_check" {
test {
active = true
automatic_retries = 0
device_id = 34
frequency = 1440
location_ids = ["aws-us-east-1", "aws-us-west-1"]
name = "Canada - Languages - Number of States"
scheduling_strategy = "round_robin"
requests {
configuration {
body = jsonencode({
operationName = "Query"
query = "query Query {\n country(code: \"CA\") {\n name\n native\n capital\n emoji\n currency\n languages {\n code\n name\n }\n states {\n code\n }\n }\n}\n"
variables = {}
})
headers = {
Content-Type = "application/json"
}
name = "https://countries.trevorblades.com/graphql"
request_method = "POST"
url = "https://countries.trevorblades.com/graphql"
}
validations {
actual = "{{response.code}}"
code = null
comparator = "is_less_than"
expected = "300"
extractor = null
name = "Assert response code is less than 300"
source = null
type = "assert_numeric"
value = null
variable = null
}
validations {
actual = "{{response.body}}"
code = null
comparator = "contains"
expected = "\"languages\":[{\"code\":\"en\",\"name\":\"English\"},{\"code\":\"fr\",\"name\":\"French\"}]"
extractor = null
name = "Assert response body value"
source = null
type = "assert_string"
value = null
variable = null
}
validations {
actual = null
code = null
comparator = null
expected = null
extractor = "$.data.country.states"
name = "Extract from response body"
source = "{{response.body}}"
type = "extract_json"
value = null
variable = "statearray"
}
validations {
actual = null
code = "const apiResponse = custom[\"statearray\"] ? JSON.parse(custom[\"statearray\"]) || [] : [];\n\nfunction countStates(jsonData) { \n const stateCount = jsonData.length;\n \n custom.statcount = stateCount;\n return stateCount;\n}\n\n\ncountStates(apiResponse);"
comparator = null
expected = null
extractor = null
name = "JavaScript run"
source = null
type = "javascript"
value = null
variable = "statecount"
}
validations {
actual = "{{custom.statecount}}"
code = null
comparator = "equals"
expected = "13"
extractor = null
name = "Assert custom variable statecount equals 13"
source = null
type = "assert_numeric"
value = null
variable = null
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Third-party Status Page API Check to Metric
This example API test shows how to call multiple APIs, collect data, turn that data into a usable JSON payload, and send it off to another API.
This test creates metrics using a Splunk Synthetics API test.
The test and it's configuration are included in this directory:
- [`synthetics_thirdparty_status_api_check.tf`](./synthetics_thirdparty_status_api_check.tf)
- Uses the [Splunk Synthetics Terraform provider](https://registry.terraform.io/providers/splunk/synthetics/latest/docs)

For a detailed description of this test and how it functions check out the [Splunk Lantern Article: Constructing an API test JSON payload](https://lantern.splunk.com/Observability/Product_Tips/Synthetic_Monitoring/Constructing_an_API_test_JSON_payload_for_alerting_on_external_dependencies)

## Synthetic API Test
The synthetic API test will call the CloudFlare and GitHub status pages and report a metric with a value of 1 (status is impacted) or 0 (status is normal) for each:
- `cloudflare.status`
- `github.status`

These metrics include dimensions for description of any impact to status and an indicator (none, minor, major, or critical).
![alt text](image.png)

### Required Splunk Synthetic Global Variables
The following [global variables](https://docs.splunk.com/observability/en/synthetics/test-config/global-variables.html) are **REQUIRED** to run the included API test.
- `org_ingest_token`: A provisioned INGEST token
![required synthetic variables](synthetic-variables.png)

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
resource "synthetics_create_api_check_v2" "synthetics_thirdparty_status_api_check" {
test {
active = true
automatic_retries = 0
device_id = 34
frequency = 60
location_ids = ["aws-us-east-1", "aws-us-west-1"]
name = "Cloudflare Status API"
scheduling_strategy = "round_robin"
requests {
configuration {
body = null
headers = {}
name = "https://www.cloudflarestatus.com/api/v2/status.json"
request_method = "GET"
url = "https://www.cloudflarestatus.com/api/v2/status.json"
}
validations {
actual = null
code = null
comparator = null
expected = null
extractor = "*"
name = "Extract from response body"
source = "{{response.body}}"
type = "extract_json"
value = null
variable = "response"
}
validations {
actual = null
code = "var apiResponse = JSON.parse(custom[\"response\"]);\n\nfunction createGaugeEntry(apiResponse) {\n var value = (apiResponse.status.indicator === \"none\") ? \"0\" : \"1\";\n \n var gaugeEntry = {\n \"gauge\": [\n {\n \"metric\": \"cloudflare.status\",\n \"dimensions\": {\n \"description\": apiResponse.status.description,\n \"indicator\": apiResponse.status.indicator\n },\n \"value\": value\n }\n ]\n };\n\n return gaugeEntry;\n}\n\nvar gaugeEntry = createGaugeEntry(apiResponse);\n\ncustom.payload_cloudflare = gaugeEntry;"
comparator = null
expected = null
extractor = null
name = "JavaScript run"
source = null
type = "javascript"
value = null
variable = "payload_cloudflare"
}
validations {
actual = "{{custom.payload_cloudflare}}"
code = null
comparator = "is_not_empty"
expected = null
extractor = null
name = "Assert custom variable payload_cloudflare is not empty"
source = null
type = "assert_string"
value = null
variable = null
}
}
requests {
configuration {
body = null
headers = {}
name = "curl https://www.githubstatus.com/api/v2/status.json"
request_method = "GET"
url = "https://www.githubstatus.com/api/v2/status.json"
}
validations {
actual = null
code = null
comparator = null
expected = null
extractor = "*"
name = "Extract from response body"
source = "{{response.body}}"
type = "extract_json"
value = null
variable = "response"
}
validations {
actual = null
code = "var apiResponse = JSON.parse(custom[\"response\"]);\n\nfunction createGaugeEntry(apiResponse) {\n var value = (apiResponse.status.indicator === \"none\") ? \"0\" : \"1\";\n\n var gaugeEntry = {\n \"gauge\": [\n {\n \"metric\": \"github.status\",\n \"dimensions\": {\n \"description\": apiResponse.status.description,\n \"indicator\": apiResponse.status.indicator\n },\n \"value\": value\n }\n ]\n };\n\n return gaugeEntry;\n}\n\nvar gaugeEntry = createGaugeEntry(apiResponse);\n\ncustom.payload_github = gaugeEntry;"
comparator = null
expected = null
extractor = null
name = "JavaScript run"
source = null
type = "javascript"
value = null
variable = "payload_github"
}
validations {
actual = "{{custom.payload_github}}"
code = null
comparator = "is_not_empty"
expected = null
extractor = null
name = "Assert custom variable payload_github is not empty"
source = null
type = "assert_string"
value = null
variable = null
}
}
requests {
configuration {
body = "{{custom.payload}}"
headers = {
Content-Type = "application/json"
X-SF-Token = "{{env.org_ingest_token}}"
}
name = "https://ingest.us1.signalfx.com/v2/datapoint"
request_method = "POST"
url = "https://ingest.us1.signalfx.com/v2/datapoint"
}
setup {
code = "var gaugePayloads = [\n JSON.parse(custom[\"payload_github\"]),\n JSON.parse(custom[\"payload_cloudflare\"])\n];\n\nfunction combineMultipleGaugeEntries(payloads) {\n var combinedGaugeArray = [];\n \n for (var i = 0; i < payloads.length; i++) {\n combinedGaugeArray = combinedGaugeArray.concat(payloads[i].gauge);\n }\n\n return { \"gauge\": combinedGaugeArray };\n}\n\nvar combinedGaugeEntry = combineMultipleGaugeEntries(gaugePayloads);\n\ncustom.payload = combinedGaugeEntry;"
extractor = null
name = "JavaScript run"
source = null
type = "javascript"
value = null
variable = "payload"
}
validations {
actual = "{{response.code}}"
code = null
comparator = "is_less_than"
expected = "300"
extractor = null
name = "Assert response code is less than 300"
source = null
type = "assert_numeric"
value = null
variable = null
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Third-party Status Page API Check to Metric
This example API test calls the OpenAI status endpoint and collects data on ongoing incidents and updates.
This test creates and sends a log event containing that incident data to a Splunk HEC endpoint.
The test and it's configuration are included in this directory:
- [`synthetics_status_to_splunk_hec_api_check.tf`](./synthetics_status_to_splunk_hec_api_check.tf)
- Uses the [Splunk Synthetics Terraform provider](https://registry.terraform.io/providers/splunk/synthetics/latest/docs)

## Synthetic API Test
The synthetic API test will call the OpenAI status page and report any current and ongoing incidents to a Splunk HEC endpoint of your choice. This example is mostly to illustrate ingest arbitrary ingest into Splunk. The test serves a double function of providing external monitoring of the HEC endpoint in question in addition to providing ingest of useful incident data.


### Required Splunk Synthetic Global Variables
The following [global variables](https://docs.splunk.com/observability/en/synthetics/test-config/global-variables.html) are **REQUIRED** to run the included API test.
- `splunk_hec_url`: The url to your hec raw ingest (E.G. `https://hec-inputs-for-my-service.mysplunkinstance.com:443/services/collector/raw`)
- **Terraform apply will fail if this global variable does not exist in your environment!**
- `hec_token`: A provisioned hec token for basic auth (E.G. `Splunk 123412-3123-1234-abcd-1234123412abc`)

Loading