packages feed

keiro-dsl-0.8.0.0: src/Keiro/Dsl/Parser/Integration.hs

-- | Contract, intake, emit, and publisher syntax.
module Keiro.Dsl.Parser.Integration
  ( pContract,
    pIntake,
    pEmit,
    pPublisher,
  )
where

import Keiro.Dsl.Grammar
import Keiro.Dsl.Parser.Core
import Text.Megaparsec

-- Integration contract (EP-4)
--------------------------------------------------------------------------------

pContract :: P ContractNode
pContract = do
  loc <- getLoc
  keyword "contract"
  nm <- ident
  _ <- symbol "{"
  keyword "schemaVersion"
  sv <- boundedDecimal
  keyword "discriminator"
  disc <- ident
  topics <- many pTopic
  events <- many pContractEvent
  _ <- symbol "}"
  pure
    ContractNode
      { ctrName = nm,
        ctrSchemaVersion = sv,
        ctrDiscriminator = disc,
        ctrTopics = topics,
        ctrEvents = events,
        ctrLoc = loc
      }
  where
    pTopic = do
      keyword "topic"
      alias <- ident
      t <- stringLit
      pure (alias, t)
    pContractEvent = do
      keyword "event"
      nm <- ident
      keyword "on"
      topicAlias <- ident
      fs <- braces (many pContractField)
      pure ContractEvent {ceName = nm, ceTopic = topicAlias, ceFields = fs}
    pContractField = do
      n <- ident
      _ <- symbol ":"
      ty <- pContractType
      _ <- optional (symbol ";")
      pure ContractField {cfName = n, cfType = ty}
    pContractType =
      choice
        [ CTypeId <$> (keyword "typeid" *> stringLit),
          CText <$ keyword "text",
          CInt <$ keyword "int"
        ]

pIntake :: P IntakeNode
pIntake = do
  loc <- getLoc
  keyword "intake"
  nm <- ident
  _ <- symbol "{"
  keyword "contract"
  ctr <- ident
  keyword "topic"
  tp <- ident
  keyword "accept"
  acc <- some ident
  binds <- many pBindRow
  keyword "dedupe"
  keyword "key"
  dk <- ident
  keyword "policy"
  dp <- ident
  persistence <-
    option InkPersistFull $
      keyword "persist"
        *> symbol "="
        *> choice
          [ InkPersistFull <$ keyword "full-envelope",
            InkPersistDedupeOnly <$ keyword "dedupe-only"
          ]
  dec <- pDecode
  disp <- pDisposition
  _ <- symbol "}"
  pure
    IntakeNode
      { inkName = nm,
        inkContract = ctr,
        inkTopic = tp,
        inkAccept = acc,
        inkBinds = binds,
        inkDedupeKey = dk,
        inkDedupePolicy = dp,
        inkPersist = persistence,
        inkDecode = dec,
        inkDisposition = disp,
        inkLoc = loc
      }
  where
    pBindRow = do
      keyword "bind"
      f <- ident
      keyword "from"
      src <- pWireSource
      req <- option False (True <$ keyword "required")
      xc <- option False (True <$ (keyword "cross-check" *> keyword "body"))
      pure BindRow {brField = f, brSource = src, brRequired = req, brCrossCheck = xc}
    pWireSource =
      choice
        [ SrcHeader <$> (keyword "header" *> stringLit),
          SrcKafkaKey <$ keyword "kafka-key",
          SrcKafkaCursor <$ keyword "kafka-cursor",
          SrcBody <$ keyword "body"
        ]
    pDecode = do
      keyword "decode"
      _ <- symbol "{"
      keyword "envelope"
      env <- pEnvelopePolicy
      keyword "body"
      strict <- (True <$ keyword "strict") <|> (False <$ keyword "lenient")
      keyword "schemaVersion"
      _ <- symbol "=="
      v <- boundedDecimal
      _ <- symbol "}"
      pure DecodeSpec {decEnvelope = env, decBodyStrict = strict, decBodySchemaVersion = v}
    pEnvelopePolicy = do
      a <- wireWord
      b <- wireWord
      pure (a <> " " <> b)
    pDisposition = do
      keyword "disposition"
      rows <- braces (many pDispositionRow)
      pure rows
    pDispositionRow = do
      loc <- getLoc
      o <- ident
      _ <- symbol "=>"
      act <- pInboxAction
      pure DispositionRow {drOutcome = o, drAction = act, drLoc = loc}
    pInboxAction =
      choice
        [ IAckOk <$ keyword "ackOk",
          IRetry <$> (keyword "retry" *> pWindow),
          IDeadLetter <$> (keyword "deadLetter" *> optional stringLit)
        ]

pEmit :: P EmitNode
pEmit = do
  loc <- getLoc
  keyword "emit"
  nm <- ident
  _ <- symbol "{"
  keyword "contract"
  ctr <- ident
  keyword "topic"
  tp <- ident
  keyword "source"
  src <- stringLit
  keyword "key"
  k <- ident
  keyword "map"
  disc <- ident
  (rows, skip) <- braces pMapRows
  keyword "messageId"
  mid <- pDerive
  keyword "idempotencyKey"
  idk <- pDerive
  _ <- symbol "}"
  pure
    EmitNode
      { emName = nm,
        emContract = ctr,
        emTopic = tp,
        emSource = src,
        emKey = k,
        emDiscriminant = disc,
        emMap = rows,
        emSkip = skip,
        emMessageId = mid,
        emIdempotencyKey = idk,
        emLoc = loc
      }
  where
    pMapRows = do
      rows <- many pMapRow
      skip <- option False (True <$ try (symbol "_" *> symbol "=>" *> keyword "skip"))
      pure (rows, skip)
    pMapRow = try $ do
      loc <- getLoc
      v <- stringLit
      _ <- symbol "=>"
      ev <- ident
      pure EmitMapRow {emrValue = v, emrEvent = ev, emrLoc = loc}
    pDerive = do
      keyword "derive"
      pfx <- optional stringLit
      keyword "hole"
      pure DeriveSpec {dsPrefix = pfx}

pPublisher :: P PublisherNode
pPublisher = do
  loc <- getLoc
  keyword "publisher"
  nm <- ident
  _ <- symbol "{"
  keyword "emit"
  em <- ident
  keyword "ordering"
  ord <- ident
  keyword "maxAttempts"
  ma <- boundedDecimal
  keyword "backoff"
  bk <- ident
  bw <- pWindow
  bm <- optional (keyword "max" *> symbol "=" *> pWindow)
  multiplier <- optional (keyword "multiplier" *> symbol "=" *> decimalText)
  keyword "outboxId"
  keyword "stable"
  keyword "from"
  obf <- ident
  _ <- symbol "}"
  pure
    PublisherNode
      { pubName = nm,
        pubEmit = em,
        pubOrdering = ord,
        pubMaxAttempts = ma,
        pubBackoff = BackoffSpec {boKind = bk, boWindow = bw, boMax = bm, boMultiplier = multiplier},
        pubOutboxField = obf,
        pubLoc = loc
      }