Files
inter-hub/Web/Controller/HubRoutingRules.hs
Bernd Worsch ce42607fca fix(WP-0014/A2): close remaining pure-param and structural compilation errors
Convert all remaining `<- paramOrNothing / param / paramOrDefault /
currentUserOrNothing` monadic binds to `let` — these functions are pure
(ImplicitParams-based) in IHP v1.5, so `<-` is a type error in an IO
do-block.

Controllers fixed:
  AgentDelegations, AiGovernancePolicies, Annotations, ApiConsumers,
  CollectiveProposals, DecisionRecords, DeploymentRecords,
  HubCapabilityManifests, HubRoutingRules, InstitutionalKnowledge,
  OutcomeCorrelations, RequirementCandidates, TypeRegistries,
  WebhookSubscriptions, Widgets,
  Api/V2/{Annotations,InteractionEvents,Token}

WebhookSubscriptions: remove orphaned `Right () ->` case arm that was
left inside a bare `unless` block (structural parse error).

Also carries forward all in-progress fixes from the working tree:
  helpers (AgentBridge, ApiRateLimit, BottleneckDetector,
            CrossHubPropagation, FrictionScore),
  views (CanSelect instances, HSX lambda extraction, formFor wrappers),
  env/build (envrc GHCi perms, flake.nix Tailwind + GHC resource limits,
             static/app.css additional Tailwind output).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 01:14:08 +00:00

103 lines
4.9 KiB
Haskell

module Web.Controller.HubRoutingRules where
import Web.Types
import Web.View.HubRoutingRules.Index
import Web.View.HubRoutingRules.Show
import Web.View.HubRoutingRules.New
import Web.View.HubRoutingRules.Edit
import Web.View.HubRoutingRules.RoutedCandidates
import Generated.Types
import IHP.Prelude
import IHP.ControllerPrelude
import Application.Helper.RoutingEngine (applyRoutingRules)
import Application.Helper.TypeRegistry (validateWidgetType, validateAnnotationCategory)
instance Controller HubRoutingRulesController where
beforeAction = ensureIsUser
action HubRoutingRulesAction = autoRefresh do
rules <- query @HubRoutingRule |> orderByDesc #priority |> fetch
hubs <- query @Hub |> fetch
render IndexView { rules, hubs }
action ShowHubRoutingRuleAction { hubRoutingRuleId } = do
rule <- fetch hubRoutingRuleId
sourceHub <- fetch rule.sourceHubId
targetHub <- fetch rule.targetHubId
render ShowView { rule, sourceHub, targetHub }
action NewHubRoutingRuleAction = do
let rule = newRecord @HubRoutingRule
hubs <- query @Hub |> orderByAsc #name |> fetch
render NewView { rule, hubs }
action CreateHubRoutingRuleAction = do
let rule = newRecord @HubRoutingRule
hubs <- query @Hub |> orderByAsc #name |> fetch
let mMatchWidgetType = paramOrNothing @Text "matchWidgetType"
mMatchCategory = paramOrNothing @Text "matchCategory"
wtResult <- case mMatchWidgetType of { Nothing -> pure (Right ()); Just "" -> pure (Right ()); Just t -> liftIO (validateWidgetType t) }
catResult <- case mMatchCategory of { Nothing -> pure (Right ()); Just "" -> pure (Right ()); Just c -> liftIO (validateAnnotationCategory c) }
rule
|> fill @'["sourceHubId","targetHubId","matchCategory","matchWidgetType","priority","notes"]
|> validateField #sourceHubId nonEmpty
|> validateField #targetHubId nonEmpty
|> (case wtResult of { Left msg -> attachFailure #matchWidgetType msg; Right () -> id })
|> (case catResult of { Left msg -> attachFailure #matchCategory msg; Right () -> id })
|> ifValid \case
Left r -> render NewView { rule = r, hubs }
Right r -> do
r <- createRecord r
setSuccessMessage "Routing rule created"
redirectTo ShowHubRoutingRuleAction { hubRoutingRuleId = r.id }
action EditHubRoutingRuleAction { hubRoutingRuleId } = do
rule <- fetch hubRoutingRuleId
hubs <- query @Hub |> orderByAsc #name |> fetch
render EditView { rule, hubs }
action UpdateHubRoutingRuleAction { hubRoutingRuleId } = do
rule <- fetch hubRoutingRuleId
hubs <- query @Hub |> orderByAsc #name |> fetch
let mMatchWidgetType = paramOrNothing @Text "matchWidgetType"
mMatchCategory = paramOrNothing @Text "matchCategory"
wtResult <- case mMatchWidgetType of { Nothing -> pure (Right ()); Just "" -> pure (Right ()); Just t -> liftIO (validateWidgetType t) }
catResult <- case mMatchCategory of { Nothing -> pure (Right ()); Just "" -> pure (Right ()); Just c -> liftIO (validateAnnotationCategory c) }
rule
|> fill @'["matchCategory","matchWidgetType","priority","notes"]
|> (case wtResult of { Left msg -> attachFailure #matchWidgetType msg; Right () -> id })
|> (case catResult of { Left msg -> attachFailure #matchCategory msg; Right () -> id })
|> ifValid \case
Left r -> render EditView { rule = r, hubs }
Right r -> do
updateRecord r
setSuccessMessage "Routing rule updated"
redirectTo ShowHubRoutingRuleAction { hubRoutingRuleId = r.id }
action ActivateRoutingRuleAction { hubRoutingRuleId } = do
rule <- fetch hubRoutingRuleId
rule |> set #status "active" |> updateRecord
setSuccessMessage "Rule activated"
redirectTo HubRoutingRulesAction
action DeactivateRoutingRuleAction { hubRoutingRuleId } = do
rule <- fetch hubRoutingRuleId
rule |> set #status "inactive" |> updateRecord
setSuccessMessage "Rule deactivated"
redirectTo HubRoutingRulesAction
action RoutedCandidatesAction { hubId } = autoRefresh do
hub <- fetch hubId
candidates <- query @RequirementCandidate
|> filterWhere (#routedToHubId, Just hubId)
|> orderByDesc #createdAt
|> fetch
render RoutedCandidatesView { hub, candidates }
action RouteNowAction { requirementCandidateId } = do
candidate <- fetch requirementCandidateId
widgets <- query @Widget |> fetch
_ <- applyRoutingRules candidate widgets
setSuccessMessage "Routing re-evaluated"
redirectTo ShowRequirementCandidateAction { requirementCandidateId }