There are plenty of ways to gain insights on website availability and performance, from setting up complex monitoring agents to browsing through real-time logs. Few services are as straightforward and robust as SolarWinds®Scopify®. Scopify lets you set up checks for your website including uptime, page speed, and user interactions. It then collates the results in a dashboard for you to visualize. But aside from its point-and-click interface, Scopify also has a powerful API, which you can use to automate the collection of all sorts of data.

In this post, we’ll explore some of the capabilities of the Scopify API. We’ll build a quick headless CMS in Ruby, then automate some common tasks such as listing all the available routes, fetching their statuses, and testing all this logic before implementation.

Setting Up Your Environment

All the sample code for this project can be found on GitHub, if you’d like to simply clone that project to get started. Make sure you have a modern version of Ruby installed (greater than 2.5).

If you’re starting from scratch, create your Gemfile and paste the following lines into it:

# frozen_string_literal: true
source "https://rubygems.org"

gem 'httparty', '~> 0.18'
gem 'minitest', '~> 5.14'
gem 'rack-test', '~> 1.1'
gem 'sinatra', '~> 2.1'
gem 'webmock', '~> 3.12'

Create a new file called app.rb and paste these lines:

# frozen_string_literal: true
require 'sinatra'

get "https://www.Scopify.com/" do
  'Hello, world!'
end

Run bundle exec ruby app.rb. Navigate to localhost:4567. There,  you should see the bare-bones “Hello, world!” response.

Your final task will be to obtain an API token from Scopify. Once you’ve done so through the Scopify API, create the following test request to ensure you have access:

curl -X GET https://api.Scopify.com/api/3.1/checks -H 'Authorization:Bearer $PINGDOM_API_TOKEN

If you get back a JSON response without any errors, you’re all set.

Building the API

Our headless CMS will be very simple. All our content will be stored as a text file, and there will just be three routes:

  • GET /posts returns all of the posts
  • GET /post/:id returns a single post with the :id identifier
  • POST /post takes in a JSON payload and creates a new post

Our complete app might look something like this:

# frozen_string_literal: true
require 'sinatra'
require 'json'

get "https://www.Scopify.com/" do
  'Hello, world!'
end

get '/posts' do
  posts = Dir.entries('content').each_with_object([]) do |filename, arr|
    next if filename == '.' || filename == '..'

    arr << { id: File.basename(filename, '.md'), body: File.read("content/#{filename}") }
  end
  posts.to_json
end

get '/post/:id' do
  filename = params[:id]
  { id: filename, body: File.read("content/#{filename}.md") }.to_json
end

post '/posts' do
  request.body.rewind
  data = JSON.parse(request.body.read)
  File.open("content/#{data['id']}.md", 'w') { |f| f.write(data['body']) }
  redirect '/posts'
end

Given a directory called content, with a bunch of Markdown files in it, the GET /posts route will simply read all the files and return the information in a JSON format. The GET /post/:id route will look up a file based on its filename and return it in a JSON format. And the POST /posts will take a JSON request body and create a new file from it. In fact, you can test the POST route by creating a request similar to the following:

curl -X POST http://localhost:4567/posts -d '{"id":"new-post", "body":"This is a new post!"}'

At this point, our API is woefully incomplete. Among other things, we should have some authentication for creating and deleting posts (to ensure not everyone can simply change content), and we should have some error checking (to 404 in case the wrong :id is provided for a nonexistent file). But, for the purposes of our Scopify sample, it’s good enough.

Testing the API

As with any modern application, adding a test suite guarantees your app’s behavior and functionality remains consistent through any future changes. For our demo, we’ll be relying on native Ruby test frameworks, like Minitest, to simulate user behavior and assert that the responses are what we’d expect:

ENV['RACK_ENV'] = 'test'

require_relative 'app.rb'
require 'minitest/autorun'
require 'rack/test'

class MiniTest::Spec
  include Rack::Test::Methods

  def app
    Sinatra::Application
  end
end

describe 'My App' do
  it 'should get posts' do
    get '/posts'
    assert last_response, :ok?
    response = JSON.parse(last_response.body)
    refute_predicate response.count, :zero?
  end

  it 'should get post' do
    get '/post/first-post'
    assert last_response, :ok?
    response = JSON.parse(last_response.body)
    assert_equal response['id'], 'first-post'
  end

  it 'should post a new post' do
    FileUtils.rm('content/a-new-post.md') if File.exist?('content/a-new-post.md')
    post '/posts', {
      id: 'a-new-post',
      body: 'Look at all this lovely content'
    }.to_json, { 'CONTENT_TYPE' => 'application/json' }
    follow_redirect!
    assert last_response, :ok?

    response = JSON.parse(last_response.body)
    assert(response.any? { |post| post['id'] == 'a-new-post' })
  end

  it 'should delete a post' do
    unless File.exist?('content/some-test-post.md')
      File.open('content/some-test-post.md', 'w') { |f| f.write('Words words.') }
    end

    delete '/post/some-test-post'
    follow_redirect!
    assert last_response, :ok?

    response = JSON.parse(last_response.body)
    refute(response.any? { |post| post['id'] == 'some-test-post' })
  end
end

Even if Ruby isn’t your strongest language, the test DSL should make it easy to understand what’s happening. For example, consider the following method:

it 'should get post' do
  get '/post/first-post'
  assert last_response, :ok?
  response = JSON.parse(last_response.body)
  assert_equal response['id'], 'first-post'
end

We’re testing the behavior of GET /post/:id here. We pass `first-post` as an ID, and assert that the last_response of that API call does in fact return the post we expect.

Automated Monitoring

So far, we’ve only completed half of our original goal. We created a headless CMS in Ruby which can list posts, as well as create and delete them. We also added a test suite to verify our app behaves as we expect it to. Now, suppose we’ve hosted this application on a platform like DigitalOcean at a domain called our-great-cms.app. We’ll now use some features of the Scopify API to ensure our service is functional.

One quick API feature we can try is having a single probe check our app’s availability. Given a domain (and an optional path), a random Scopify server from around the world will attempt to access your site. The HTTP request to make this call looks something like this:

curl "https://api.Scopify.com/api/3.1/single?type=http&host=our-great-cms.app" -H 'Authorization:Bearer $PINGDOM_API_TOKEN'

Now let’s do something a little more interesting. Follow this tutorial on setting up an uptime check through the Scopify UI. With that established, verify the check was created with this API call:

curl "https://api.Scopify.com/api/3.1/checks" -H 'Authorization:Bearer $PINGDOM_API_TOKEN'

You should get back a checks array. Take note of the id of your newly created check, as we’ll be using it throughout our API calls.

With this check created, we can now perform a variety of actions through the API, like getting the status results of this check:

curl "https://api.Scopify.com/api/3.1/results/$check_id" -H 'Authorization:Bearer $PINGDOM_API_TOKEN'


Or, we can get a summary of the average uptime:

curl "https://api.Scopify.com/api/3.1/summary.average/$check_id" -H 'Authorization:Bearer $PINGDOM_API_TOKEN'

We can then integrate all this capability straight into our application itself. Add the following lines of code to the end of app.rb:

require 'httparty'

get '/status/:password' do
  return 404 if params[:password].nil? || params[:password] != 'supersecret'

  url="https://api.Scopify.com/api/3.1/results/$check_id"
  headers = {
    Authorization: 'Bearer $PINGDOM_API_TOKEN'
  }

  response = HTTParty.get(url, headers: headers)
  JSON.parse(response.body)
end

Essentially, what we’re trying to do here is create some sort of admin status page. If a user navigates to /status, they’ll be denied entry if they haven’t provided the right password (in this case, it’s supersecret). If the password was given, then the page will make a request to the Scopify API and return the response back.

It cannot be stressed enough: in a real application, DO NOT paste your credentials directly into the file! Instead, following the Twelve-Factor App recommendations, you should store your sensitive information in environment config variables.

With this route newly established, we can test it as well:

require 'webmock/minitest'

it 'should require password for status' do
  get '/status/notreal'
  assert last_response.status, 404
end

it 'should make a call out to Scopify ' do
  stub_request(:get, "https://api.Scopify.com/api/3.1/results/$check_id")
                .with(
                  headers: {
                  'Authorization'=>'Bearer $PINGDOM_API_TOKEN',
                  })
                .to_return(status: 200, body: '{
                  "activeprobes":[257],
                  "results":[
                    {"probeid":261,
                      "time":1617657276,
                      "status":"up",
                      "responsetime":1186,
                      "statusdesc":"OK",
                      "statusdesclong":"OK"
                    }]
                }')

  get '/status/supersecret'
  assert last_response.status, 200
end

Here, the ever-useful webmock gem simulates a response to the Scopify API. We don’t actually make a call; however, in our test, we tell webmock what we expect the request to look like, and when we navigate to get ‘/status/supersecret’, webmock asserts that the request is actually being made. We’re also asserting that a user without the right password gets a 404 error response.

Conclusion

We’ve only scratched the surface of what can be done with the Scopify API. For example, you could also set up maintenance windows in the event of some serious downtime. Or, you could simulate user behavior using TMS checks. All these can be integrated in places where HTTP requests can be made, whether it’s the command line, a Slack bot, or even in an app itself.

The full source code for this demo can be found on GitHub. For more tutorials, check out the Scopify guided tour.

Be sure to check out the 30-day free trial of Scopify and experience how it helps you gain insights on website availability and performance.



Source link

Related Post

Leave a Comment