packages feed

powerdns-0.2.1: PowerDNS/API/Zones.hs

-- |
-- Module: PowerDNS.API.Zones
-- Description: Zones endpoints for PowerDNS API
--
-- Implementation of the API endpoints described at [PowerDNS Zones API](https://doc.powerdns.com/authoritative/http-api/zone.html)

{-# LANGUAGE DataKinds          #-}
{-# LANGUAGE DeriveAnyClass     #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric      #-}
{-# LANGUAGE TypeOperators      #-}
module PowerDNS.API.Zones
  (
  -- * API
    ZonesAPI(..)

  -- * Data types
  , Zone(..)
  , Kind(..)
  , RRSets(..)
  , RRSet(..)
  , Record(..)
  , Comment(..)
  , ChangeType(..)
  , RecordType(..)
  )
where

import           Data.Char (toUpper)
import           Data.Data (Data)
import           Data.Word (Word32)

import           Control.DeepSeq (NFData)
import           Data.Aeson (FromJSON(..), ToJSON(..), allNullaryToStringTag,
                             defaultOptions, fieldLabelModifier, constructorTagModifier,
                             genericParseJSON, genericToJSON, omitNothingFields)
import qualified Data.Text as T
import           Data.Time.Clock.POSIX (POSIXTime)
import           Servant.API
import           Servant.API.Generic

import           PowerDNS.Internal.Utils (Empty(..), strip)

----------------------------------------------------------------------------------------

data ZonesAPI f = ZonesAPI
  { apiListZones     :: f :- "servers" :> Capture "server_id" T.Text :> "zones"
                          :> QueryParam "zone" T.Text
                          :> QueryParam "dnssec" Bool
                          :> Get '[JSON] [Zone]

  , apiCreateZone    :: f :- "servers" :> Capture "server_id" T.Text :> "zones"
                          :> QueryParam "rrset" Bool
                          :> ReqBody '[JSON] Zone
                          :> PostCreated '[JSON] Zone

  , apiGetZone       :: f :- "servers" :> Capture "server_id" T.Text :> "zones" :> Capture "zone_id" T.Text
                          :> QueryParam "rrsets" Bool
                          :> Get '[JSON] Zone

  , apiDeleteZone    :: f :- "servers" :> Capture "server_id" T.Text :> "zones" :> Capture "zone_id" T.Text
                          :> DeleteNoContent

  , apiUpdateRecords :: f :- "servers" :> Capture "server_id" T.Text :> "zones" :> Capture "zone_id" T.Text
                          :> ReqBody '[JSON] RRSets
                          :> PatchNoContent

  , apiUpdateZone    :: f :- "servers" :> Capture "server_id" T.Text :> "zones" :> Capture "zone_id" T.Text
                          :> ReqBody '[JSON] Zone
                          :> PutNoContent

  , apiTriggerAxfr   :: f :- "servers" :> Capture "server_id" T.Text :> "zones" :> Capture "zone_id" T.Text :> "axfr-retrieve"
                          :> Put '[JSON] NoContent

  , apiNotifySlaves  :: f :- "servers" :> Capture "server_id" T.Text :> "zones" :> Capture "zone_id" T.Text :> "notify"
                          :> Put '[JSON] NoContent

  , apiGetZoneAxfr   :: f :- "servers" :> Capture "server_id" T.Text :> "zones" :> Capture "zone_id" T.Text :> "export"
                          :> Get '[JSON] T.Text

  , apiRectifyZone   :: f :- "servers" :> Capture "server_id" T.Text :> "zones" :> Capture "zone_id" T.Text :> "rectify"
                          :> Put '[JSON] T.Text

  } deriving Generic


----------------------------------------------------------------------------------------

-- | Zone according to [PowerDNS Documentation](https://doc.powerdns.com/authoritative/http-api/zone.html#zone).
-- All fields are optional because the PowerDNS API differs on which fields are required depending on the endpoint.
data Zone = Zone
  { zone_id :: Maybe T.Text
  , zone_name :: Maybe T.Text
  , zone_type :: Maybe T.Text
  , zone_url :: Maybe T.Text
  , zone_kind :: Maybe Kind
  , zone_rrsets :: Maybe [RRSet]
  , zone_serial :: Maybe Integer
  , zone_notified_serial :: Maybe Integer
  , zone_edited_serial :: Maybe Integer
  , zone_masters :: Maybe [T.Text]
  , zone_dnssec :: Maybe Bool
  , zone_nsec3param :: Maybe T.Text
  , zone_nsec3narrow :: Maybe Bool
  , zone_presigned :: Maybe Bool
  , zone_soa_edit :: Maybe T.Text
  , zone_soa_edit_api :: Maybe T.Text
  , zone_api_rectify :: Maybe Bool
  , zone_zone :: Maybe T.Text
  , zone_account :: Maybe T.Text
  , zone_nameservers :: Maybe [T.Text]
  , zone_master_tsig_key_ids :: Maybe [T.Text]
  , zone_slave_tsig_key_ids :: Maybe [T.Text]
  } deriving (Eq, Ord, Show, Generic, NFData, Data, Empty)

instance ToJSON Zone where
  toJSON = genericToJSON defaultOptions { fieldLabelModifier = strip "zone_"}

instance FromJSON Zone where
  parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = strip "zone_"}

----------------------------------------------------------------------------------------

data Kind = Native | Master | Slave
  deriving (Eq, Ord, Show, Generic, NFData, Data)

instance ToJSON Kind where
  toJSON = genericToJSON defaultOptions { allNullaryToStringTag = True }

instance FromJSON Kind where
  parseJSON = genericParseJSON defaultOptions { allNullaryToStringTag = True }

----------------------------------------------------------------------------------------

-- | A list of RRSets
data RRSets = RRSets
  { rrsets :: [RRSet]
  } deriving (Eq, Ord, Show, Generic, NFData, Data)

instance ToJSON RRSets
instance FromJSON RRSets

----------------------------------------------------------------------------------------

-- | RRSet according to [PowerDNS Documentation](https://doc.powerdns.com/authoritative/http-api/zone.html#rrset).
data RRSet = RRSet
  { rrset_name :: T.Text
  , rrset_type :: RecordType
  , rrset_ttl :: Word32
  , rrset_changetype :: Maybe ChangeType
  , rrset_records :: Maybe [Record]
  , rrset_comments :: Maybe [Comment]
  } deriving (Eq, Ord, Show, Generic, NFData, Data)

instance ToJSON RRSet where
  toJSON = genericToJSON defaultOptions { fieldLabelModifier = strip "rrset_"
                                        , omitNothingFields = True }

instance FromJSON RRSet where
  parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = strip "rrset_"
                                              , omitNothingFields = True }

----------------------------------------------------------------------------------------

data RecordType = A
                | AAAA
                | AFSDB
                | ALIAS
                | APL
                | CAA
                | CERT
                | CDNSKEY
                | CDS
                | CNAME
                | DNSKEY
                | DNAME
                | DS
                | HINFO
                | KEY
                | LOC
                | MX
                | NAPTR
                | NS
                | NSEC
                | NSEC3
                | NSEC3PARAM
                | OPENPGPKEY
                | PTR
                | RP
                | RRSIG
                | SOA
                | SPF
                | SSHFP
                | SRV
                | TKEY
                | TSIG
                | TLSA
                | SMIMEA
                | TXT
                | URI
                | A6
                | DHCID
                | DLV
                | EUI48
                | EUI64
                | IPSECKEY
                | KX
                | MAILA
                | MAILB
                | MINFO
                | MR
                | RKEY
                | SIG
                | WKS
                deriving (Eq, Ord, Show, Generic, NFData, Data)

instance ToJSON RecordType where
  toJSON = genericToJSON defaultOptions { allNullaryToStringTag = True
                                        }

instance FromJSON RecordType where
  parseJSON = genericParseJSON defaultOptions { allNullaryToStringTag = True
                                              }

----------------------------------------------------------------------------------------

-- | Whether or not an 'RRSet' replace or delete an existing entry.
-- If the 'ChangeType' is left at @Nothing@ it will create a new domain entry.
data ChangeType = Replace
                | Delete
                deriving (Eq, Ord, Show, Generic, NFData, Data)

instance ToJSON ChangeType where
  toJSON = genericToJSON defaultOptions { constructorTagModifier = fmap toUpper
                                        , allNullaryToStringTag = True }

instance FromJSON ChangeType where
  parseJSON = genericParseJSON defaultOptions { constructorTagModifier = fmap toUpper
                                              , allNullaryToStringTag = True }

----------------------------------------------------------------------------------------
-- | Record according to [PowerDNS Documentation](https://doc.powerdns.com/authoritative/http-api/zone.html#record)
data Record = Record
  { record_content :: T.Text
  , record_disabled :: Bool
  } deriving (Eq, Ord, Show, Generic, NFData, Data)

instance ToJSON Record where
  toJSON = genericToJSON defaultOptions { fieldLabelModifier = strip "record_"
                                        , omitNothingFields = True }

instance FromJSON Record where
  parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = strip "record_"
                                              , omitNothingFields = True }

----------------------------------------------------------------------------------------

-- | Comment according to [PowerDNS Documentation](https://doc.powerdns.com/authoritative/http-api/zone.html#comment)
data Comment = Comment
  { comment_content :: Maybe T.Text
  , comment_account :: Maybe T.Text
  , commant_modified_at :: Maybe POSIXTime
  } deriving (Eq, Ord, Show, Generic, NFData, Data, Empty)

instance ToJSON Comment where
  toJSON = genericToJSON defaultOptions { fieldLabelModifier = strip "comment_" }

instance FromJSON Comment where
  parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = strip "comment_" }