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

Code before:
                 _ ->
                   Failure
                     (pure $
                      InvalidType
                        (G.toGT $ G.NamedType name)
                        "not an input object type"))

isTypeCoercible :: G.GType -> G.GType -> Validation (NonEmpty ValidationError) ()
isTypeCoercible actualType expectedType =
  -- The GraphQL spec says that, a singleton type can be coerced into  an array
  -- type. Which means that if the 'actualType' is a singleton type, like
  -- 'Int' we should be able to join this with a remote node, which expects an
  -- input argument of type '[Int]'
  -- http://spec.graphql.org/June2018/#sec-Type-System.List
  let (actualBaseType, actualNestingLevel) = getBaseTyWithNestedLevelsCount actualType
      (expectedBaseType, expectedNestingLevel) = getBaseTyWithNestedLevelsCount expectedType
  in
  if actualBaseType == expectedBaseType
  then if (actualNestingLevel == expectedNestingLevel || actualNestingLevel == 0)
  -- The check of 'actualNestedCount == 0' is the case of coercing a singleton type
  -- into an array type
       then pure ()
       else Failure (pure $ ExpectedTypeButGot expectedType actualType)
       --  we cannot coerce two types with different nesting levels,
       -- for example, we cannot coerce [Int] to [[Int]]
  else Failure (pure $ ExpectedTypeButGot expectedType actualType)

assertListType :: G.GType -> Validation (NonEmpty ValidationError) ()
assertListType actualType =
  (when (not $ isListType' actualType)
    (Failure (pure $ InvalidType actualType "is not a list type")))

-- | Convert a field info to a named type, if possible.
columnInfoToNamedType :: PGColumnInfo -> Validation (NonEmpty ValidationError) G.NamedType
columnInfoToNamedType pci = case pgiType pci of
  PGColumnScalar scalarType -> pure $ mkScalarTy scalarType
  _                         -> Failure $ pure UnsupportedEnum


Review comment (bug):
Can we simplify this code by using MultiwayIf
```haskell
if | actualBaseType /= expectedBaseType -> raiseValidationError
   | actualNestingLevel /= expectedNestingLevel && actualNestingLevel /= 0 -> raiseValidationError
   | otherwise -> pure ()
   where
     raiseValidationError = Failure $ pure $ ExpectedTypeButGot expectedType actualType
```
Of course with appropriate comments. 

Code after:
  -- type. Which means that if the 'actualType' is a singleton type, like
  -- 'Int' we should be able to join this with a remote node, which expects an
  -- input argument of type '[Int]'
  -- http://spec.graphql.org/June2018/#sec-Type-System.List
  let (actualBaseType, actualNestingLevel) = getBaseTyWithNestedLevelsCount actualType
      (expectedBaseType, expectedNestingLevel) = getBaseTyWithNestedLevelsCount expectedType
  in
  if | actualBaseType /= expectedBaseType -> raiseValidationError
       -- we cannot coerce two types with different nesting levels,
       -- for example, we cannot coerce [Int] to [[Int]]
     | (actualNestingLevel == expectedNestingLevel || actualNestingLevel == 0) -> pure ()
     | otherwise -> raiseValidationError
     where
       raiseValidationError = throwError $ ExpectedTypeButGot expectedType actualType

assertListType :: (MonadError ValidationError m) => G.GType -> m ()
assertListType actualType =
  (when (not $ G.isListType actualType)
    (throwError $ InvalidType actualType "is not a list type"))

-- | Convert a field info to a named type, if possible.
columnInfoToNamedType
  :: (MonadError ValidationError m)
  => PGColumnInfo
  -> m G.Name
columnInfoToNamedType pci =
  case pgiType pci of
    PGColumnScalar scalarType -> do
      eitherScalar <- runExceptT $ mkScalarTypeName scalarType
      case eitherScalar of
        Left _ -> throwError $ InvalidGraphQLName $ toSQLTxt scalarType
        Right s -> pure s
    _                         -> throwError UnsupportedEnum