Repository: hasura/graphql-engine
File: server/src-lib/Hasura/RQL/DDL/Schema/Table.hs (Haskell)

Code before:
$(deriveJSON (aesonDrop 4 snakeCase) ''SetTableIsEnum)

data UntrackTable =
  UntrackTable
  { utTable   :: !QualifiedTable
  , utCascade :: !(Maybe Bool)
  } deriving (Show, Eq, Lift)
$(deriveJSON (aesonDrop 2 snakeCase){omitNothingFields=True} ''UntrackTable)

-- | Track table/view, Phase 1:
-- Validate table tracking operation. Fails if table is already being tracked,
-- or if a function with the same name is being tracked.
trackExistingTableOrViewP1 :: (CacheBuildM m, UserInfoM m) => QualifiedTable -> m ()
trackExistingTableOrViewP1 qt = do
  adminOnly
  rawSchemaCache <- askSchemaCache
  when (M.member qt $ scTables rawSchemaCache) $
    throw400 AlreadyTracked $ "view/table already tracked : " <>> qt
  let qf = fmap (FunctionName . getTableTxt) qt
  when (M.member qf $ scFunctions rawSchemaCache) $
    throw400 NotSupported $ "function with name " <> qt <<> " already exists"

validateCustomRootFields
  :: (QErrM m, CacheRM m)
  => GC.TableCustomRootFields -> m ()
validateCustomRootFields customRootFields = do
  withPathK "custom_root_fields" $ do
    sc <- askSchemaCache
    let defRemoteGCtx = scDefaultRemoteGCtx sc
        GC.TableCustomRootFields sel selByPk selAgg ins upd del = customRootFields
        rootFldNames = catMaybes [sel, selByPk, selAgg, ins, upd, del]
        duplicateRootFldNames = duplicates rootFldNames

    when (not $ null duplicateRootFldNames) $ throw400 NotSupported $
      "the following custom root field names are duplicated: " <> showNames duplicateRootFldNames

    forM_ rootFldNames $ GS.checkConflictingNode defRemoteGCtx

trackExistingTableOrViewP2
  :: (CacheBuildM m) => QualifiedTable -> SystemDefined -> Bool -> TableConfig -> m EncJSON
trackExistingTableOrViewP2 tableName systemDefined isEnum config = do
  sc <- askSchemaCache
  let defGCtx = scDefaultRemoteGCtx sc
  GS.checkConflictingNode defGCtx $ GS.qualObjectToName tableName
  validateCustomRootFields $ _tcCustomRootFields config
  saveTableToCatalog tableName systemDefined isEnum config
  buildSchemaCacheFor (MOTable tableName)
  return successMsg

runTrackTableQ :: (CacheBuildM m, UserInfoM m, HasSystemDefined m) => TrackTable -> m EncJSON
runTrackTableQ (TrackTable qt isEnum) = do

Review comment (suggestion):
In the vein of “parse, don’t validate,” is there some way we could make this return something other than `()`? 🙂 Maybe it could build a map from root field names to something? I don’t know if that makes sense, but just throwing it out there.

Code after:
$(deriveJSON (aesonDrop 4 snakeCase) ''SetTableIsEnum)

data UntrackTable =
  UntrackTable
  { utTable   :: !QualifiedTable
  , utCascade :: !(Maybe Bool)
  } deriving (Show, Eq, Lift)
$(deriveJSON (aesonDrop 2 snakeCase){omitNothingFields=True} ''UntrackTable)

-- | Track table/view, Phase 1:
-- Validate table tracking operation. Fails if table is already being tracked,
-- or if a function with the same name is being tracked.
trackExistingTableOrViewP1 :: (CacheBuildM m, UserInfoM m) => QualifiedTable -> m ()
trackExistingTableOrViewP1 qt = do
  adminOnly
  rawSchemaCache <- askSchemaCache
  when (M.member qt $ scTables rawSchemaCache) $
    throw400 AlreadyTracked $ "view/table already tracked : " <>> qt
  let qf = fmap (FunctionName . getTableTxt) qt
  when (M.member qf $ scFunctions rawSchemaCache) $
    throw400 NotSupported $ "function with name " <> qt <<> " already exists"

trackExistingTableOrViewP2
  :: (CacheBuildM m) => QualifiedTable -> SystemDefined -> Bool -> TableConfig -> m EncJSON
trackExistingTableOrViewP2 tableName systemDefined isEnum config = do
  sc <- askSchemaCache
  let defGCtx = scDefaultRemoteGCtx sc
  GS.checkConflictingNode defGCtx $ GS.qualObjectToName tableName
  saveTableToCatalog tableName systemDefined isEnum config
  buildSchemaCacheFor (MOTable tableName)
  return successMsg

runTrackTableQ :: (CacheBuildM m, UserInfoM m, HasSystemDefined m) => TrackTable -> m EncJSON
runTrackTableQ (TrackTable qt isEnum) = do
  trackExistingTableOrViewP1 qt
  systemDefined <- askSystemDefined
  trackExistingTableOrViewP2 qt systemDefined isEnum emptyTableConfig

data TrackTableV2
  = TrackTableV2
  { ttv2Table         :: !TrackTable
  , ttv2Configuration :: !TableConfig
  } deriving (Show, Eq, Lift)
$(deriveJSON (aesonDrop 4 snakeCase) ''TrackTableV2)

runTrackTableV2Q :: (CacheBuildM m, UserInfoM m, HasSystemDefined m) => TrackTableV2 -> m EncJSON
runTrackTableV2Q (TrackTableV2 (TrackTable qt isEnum) config) = do
  trackExistingTableOrViewP1 qt
  systemDefined <- askSystemDefined
  trackExistingTableOrViewP2 qt systemDefined isEnum config