packages feed

grafdhall-0.1.0.0: app/Main.hs

{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}

module Main (main) where

import Data.Aeson (Value (Array, Bool, Number, Object, String), encode)
import qualified Data.Aeson.KeyMap as HM (lookup)
import qualified Data.ByteString.Char8 as BS
import Data.Maybe (fromMaybe)
import qualified Data.Text as T
import Dhall (inputExpr)
import Dhall.JSON (Conversion (..), convertToHomogeneousMaps, dhallToJSON, omitNull)
import Network.HTTP.Client
  ( RequestBody (RequestBodyLBS),
    Response (responseStatus),
    applyBasicAuth,
    httpNoBody,
    method,
    parseRequest,
    requestBody,
    requestHeaders,
  )
import Network.HTTP.Client.TLS (newTlsManager)
import Network.HTTP.Types (Status (statusCode))
import System.Environment (getArgs, getEnv, lookupEnv)

defaultConversion :: Conversion
defaultConversion =
  Conversion
    { mapKey = "mapKey",
      mapValue = "mapValue"
    }

postDashboard :: Value -> IO ()
postDashboard dashboard = do
  grafanaUrl <- getEnv "GRAFANA_URL"
  grafanaPass <- BS.pack <$> getEnv "GRAFANA_PASS"
  grafanaUser <- BS.pack . fromMaybe "admin" <$> lookupEnv "GRAFANA_USER"
  initReq <- parseRequest $ grafanaUrl <> "/api/dashboards/db"
  let req =
        applyBasicAuth grafanaUser grafanaPass $
          initReq
            { method = "POST",
              requestHeaders = [("Content-Type", "application/json")],
              requestBody = RequestBodyLBS (encode body)
            }
  manager <- newTlsManager
  resp <- httpNoBody req manager
  case statusCode $ responseStatus resp of
    200 -> putStrLn $ "Dashboard updated: " <> dashboardName
    _ -> error $ "Got " <> show resp
  where
    getString :: Value -> String
    getString = \case
      String txt -> T.unpack txt
      _ -> error "Not a string"
    body =
      Object
        [ ("dashboard", dashboard),
          ("folderId", Number 0),
          ("overwrite", Bool True)
        ]
    dashboardName = getString $ case dashboard of
      Object attrs -> fromMaybe (error "title missing") $ HM.lookup "title" attrs
      _ -> error "Dashboard is not an object"

postDashboards :: Value -> IO ()
postDashboards = \case
  Array xs -> mapM_ postDashboard xs
  x -> postDashboard x

load :: FilePath -> IO ()
load fp = do
  confExpr <- inputExpr (T.pack fp)
  case dhallToJSON (convertToHomogeneousMaps defaultConversion confExpr) of
    Right v -> postDashboards (omitNull v)
    Left err -> error ("Could not convert configuration to JSON: " <> show err)

main :: IO ()
main = do
  args <- getArgs
  case args of
    ["--help"] -> putStrLn "usage: grafdhall ./conf.dhall"
    [fp] -> load fp
    _ -> putStrLn "usage: grafdhall ./conf.dhall"