module Components.ObjectHandlers.ServerObjectInspector (checkObjectsAttributes,replaceObjectsVariables,flagOneWay) where
import Control.Exception (throw)
import Data.Foldable (foldl')
import Model.ServerObjectTypes (
ServerObject,
RootObject,
ScalarType(..),
Field,
InlinefragmentObject(..),
NestedObject(..),
FlagNode(..)
)
import Model.ServerExceptions (
ReferenceException(
UnrecognisedObjectException,
UnrecognisedScalarException
),
QueryException(MismatchedVariableTypeException)
)
import Components.ObjectHandlers.ObjectsHandler (
isValidServerObjectChild,
isValidServerObjectScalarField,
isValidScalarTransformation,
isInterface,
translateInterfaceToServerObjects
)
checkObjectsAttributes :: [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],[String])] -> [RootObject] -> Bool
checkObjectsAttributes sss soa objs = all (\x->hasValidAttributes x sss soa) objs
hasValidAttributes :: NestedObject -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],[String])] -> Bool
hasValidAttributes (NestedObject alias name sobject Nothing sfs) sss soa = isValidSubFields sobject sfs sss soa
hasValidAttributes (NestedObject alias name sobject (Just ss) sfs) sss soa = (isValidSubSelection sobject ss sss soa)&&isValidSubFields sobject sfs sss soa
isValidSubSelection :: ServerObject -> ScalarType -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],[String])] -> Bool
isValidSubSelection obj (ScalarType alias name trans arg) sss soa = (isValidServerObjectScalarField obj name sss soa)&&isValidScalarTransformation obj name trans arg sss soa
isValidSubFields :: ServerObject -> [Field] -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],[String])] -> Bool
isValidSubFields _ [] _ _ = True
isValidSubFields obj sfs sss soa = all (\x->isValidSubField obj x sss soa) sfs
isValidSubField :: ServerObject -> Field -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],[String])] -> Bool
isValidSubField obj (Left (ScalarType alias "__typename" trans arg)) sss soa = True
isValidSubField obj (Left (ScalarType alias name trans arg)) sss soa = (isValidServerObjectScalarField obj name sss soa)&&isValidScalarTransformation obj name trans arg sss soa
isValidSubField obj (Right (Left (NestedObject alias name sobject ss sfs))) sss soa = hasValidAttributes (NestedObject alias name sobject ss sfs) sss soa
isValidSubField obj (Right (Right (InlinefragmentObject ifo sfs))) sss soa = (isValidServerObjectChild obj ifo soa)&&isValidSubFields ifo sfs sss soa
replaceObjectsVariables :: [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],[String])] -> [RootObject] -> [(String,String,String)] -> [RootObject]
replaceObjectsVariables sss soa objs vars = [replaceObjectVariables sss soa obj vars | obj<-objs]
replaceObjectVariables :: [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],[String])] -> RootObject -> [(String,String,String)] -> RootObject
replaceObjectVariables sss soa (NestedObject alias name sobject Nothing sfs) vars = NestedObject alias name sobject Nothing [replaceSubfieldVariables sss soa sobject vars sf | sf<-sfs]
replaceObjectVariables sss soa (NestedObject alias name sobject (Just (ScalarType sAlias sName trans Nothing)) sfs) vars = NestedObject alias name sobject (Just $ ScalarType sAlias sName trans Nothing) [replaceSubfieldVariables sss soa sobject vars sf | sf<-sfs]
replaceObjectVariables sss soa (NestedObject alias name sobject (Just (ScalarType sAlias sName trans (Just arg))) sfs) vars = NestedObject alias name sobject newScalar [replaceSubfieldVariables sss soa sobject vars sf | sf<-sfs]
where
newScalar = Just $ ScalarType sAlias sName trans $ Just newValue
newValue = if isVariable arg then replaceScalarVariable (getScalarTypeForVariableReplacement sobject sName sss soa) arg vars else arg
getScalarTypeForVariableReplacement :: ServerObject -> String -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],[String])] -> String
getScalarTypeForVariableReplacement obj st ((h,sts):rst) [] = if h==obj then findScalarType st sts else getScalarTypeForVariableReplacement obj st rst []
getScalarTypeForVariableReplacement obj st sss ((pnt,_,(fst:_)):rst) = if pnt==obj then getScalarTypeForVariableReplacement fst st sss [] else getScalarTypeForVariableReplacement obj st sss rst
getScalarTypeForVariableReplacement obj st sss ((pnt,_,[]):rst) = if pnt==obj then throw UnrecognisedObjectException else getScalarTypeForVariableReplacement obj st sss rst
getScalarTypeForVariableReplacement _ _ [] _ = throw UnrecognisedObjectException
findScalarType :: String -> [(String,String,[(String,[(String,String,String,String)])])] -> String
findScalarType st ((name,typ,_):rst) = if st==name then typ else findScalarType st rst
findScalarType _ [] = throw UnrecognisedScalarException
replaceScalarVariable :: String -> String -> [(String,String,String)] -> String
replaceScalarVariable typ arg ((vn,vt,vval):rst)
| arg==vn&&typ==vt = vval
| arg==vn = throw MismatchedVariableTypeException
| otherwise = replaceScalarVariable typ arg rst
replaceScalarVariable _ _ [] = throw UnrecognisedScalarException
replaceSubfieldVariables :: [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],[String])] -> String -> [(String,String,String)] -> Field -> Field
replaceSubfieldVariables _ _ _ _ (Left (ScalarType alias name trans Nothing)) = Left $ ScalarType alias name trans Nothing
replaceSubfieldVariables sss soa sobj vars (Left (ScalarType alias name trans (Just arg))) = if not $ isVariable arg then Left $ ScalarType alias name trans $ Just arg else Left $ ScalarType alias name trans $ Just $ replaceScalarVariable (getScalarTypeForVariableReplacement sobj name sss soa) arg vars
replaceSubfieldVariables sss soa sobj vars (Right (Left (NestedObject alias name nsobj Nothing sfs))) = Right $ Left $ NestedObject alias name nsobj Nothing [replaceSubfieldVariables sss soa nsobj vars sf | sf<-sfs]
replaceSubfieldVariables sss soa sobj vars (Right (Left (NestedObject alias name nsobj (Just (ScalarType sAlias sName trans Nothing)) sfs))) = Right $ Left $ NestedObject alias name nsobj (Just $ ScalarType sAlias sName trans Nothing) [replaceSubfieldVariables sss soa nsobj vars sf | sf<-sfs]
replaceSubfieldVariables sss soa sobj vars (Right (Left (NestedObject alias name nsobj (Just (ScalarType sAlias sName trans (Just arg))) sfs))) = if (not $ isVariable arg) then Right $ Left $ NestedObject alias name nsobj (Just $ ScalarType sAlias sName trans $ Just arg) [replaceSubfieldVariables sss soa nsobj vars sf | sf<-sfs] else Right $ Left $ NestedObject alias name nsobj (Just $ ScalarType sAlias sName trans $ Just $ replaceScalarVariable (getScalarTypeForVariableReplacement nsobj name sss soa) arg vars) [replaceSubfieldVariables sss soa nsobj vars sf | sf<-sfs]
replaceSubfieldVariables sss soa sobj vars (Right (Right (InlinefragmentObject ifsobj sfs))) = Right $ Right $ InlinefragmentObject ifsobj [replaceSubfieldVariables sss soa ifsobj vars sf | sf<-sfs]
isVariable :: String -> Bool
isVariable = elem '$'
flagOneWay :: [(String,[String],[String])] -> [RootObject] -> [[FlagNode]]
flagOneWay soa robjs = map (\(NestedObject _ _ so _ sfs) -> flagOneWayByRootObj soa so sfs) robjs
flagOneWayByRootObj :: [(String,[String],[String])] -> ServerObject -> [Field] -> [FlagNode]
flagOneWayByRootObj soa so sfs = if isInterface soa so then [flagOneWayRootTable soa (FlagNode 0 []) [] nio sfs [] | nio<-translateInterfaceToServerObjects soa so] else [flagOneWayRootTable soa (FlagNode 0 []) [] so sfs []]
flagOneWayRootTable :: [(String,[String],[String])] -> FlagNode -> [Int] -> ServerObject -> [Field] -> [(ServerObject,[Field])] -> FlagNode
flagOneWayRootTable soa rsf idc so ((Left _):sfs) rem = flagOneWayRootTable soa rsf idc so sfs rem
flagOneWayRootTable soa rsf idc so ((Right (Left (NestedObject _ _ nso _ nsfs))):sfs) rem
| flg && nNios > 1 = flagOneWayRootTable soa fRlt idc so sfs rem
| flg && nNios > 0 = flagOneWayRootTable soa u1Rlt (rIdx:idc) (head nios) nsfs nrem
| flg = flagOneWayRootTable soa u0Rlt idc so sfs rem
| otherwise = flagOneWayRootTable soa u1Rlt (rIdx:idc) nso nsfs nrem
where
nios = translateInterfaceToServerObjects soa nso
nNios = length nios
flg = isInterface soa nso
u0Rlt = addNodeAndInc 0 rsf idc
u1Rlt = addNodeAndInc 1 rsf idc
rIdx = findNextIndex rsf idc
fRlt = foldl' (\nRlt (nIdx,nObj)->let nU1Rlt = addNodeAndInc 1 nRlt idc in flagOneWayRootTable soa nU1Rlt (nIdx:idc) nObj nsfs []) rsf $ zip [rIdx..] nios
nrem = (so,sfs):rem
flagOneWayRootTable soa rsf idc so ((Right (Right (InlinefragmentObject nso nsfs))):sfs) rem = if so==nso then flagOneWayRootTable soa rsf idc so (nsfs++sfs) rem else flagOneWayRootTable soa rsf idc so sfs rem
flagOneWayRootTable soa rsf idc so [] ((nso,nsfs):rem) = flagOneWayRootTable soa rsf (tail idc) nso nsfs rem
flagOneWayRootTable soa rsf idc so [] [] = rsf
addNodeAndInc :: Int -> FlagNode -> [Int] -> FlagNode
addNodeAndInc inc rsf idx = updateNodeValues nVal nTree idx
where
nTree = addNodeWithInc inc rsf idx
nVal = findNewVal nTree idx
addNodeWithInc :: Int -> FlagNode -> [Int] -> FlagNode
addNodeWithInc inc (FlagNode val nds) [] = FlagNode (val+inc) (nds++[FlagNode 0 []])
addNodeWithInc inc (FlagNode val nds) (h:t) = FlagNode val [if nIdx/=h then nNode else addNodeWithInc inc nNode t | (nIdx,nNode)<-zip [0..] nds]
findNewVal :: FlagNode -> [Int] -> Int
findNewVal (FlagNode val _) [] = val
findNewVal (FlagNode _ nds) (h:t) = findNewVal ((!!) nds h) t
updateNodeValues :: Int -> FlagNode -> [Int] -> FlagNode
updateNodeValues nVal (FlagNode val nds) (h:t) = FlagNode (max nVal val) [if nIdx/=h then nNode else updateNodeValues nVal nNode t | (nIdx,nNode)<-zip [0..] nds]
updateNodeValues nVal (FlagNode val nds) [] = FlagNode (max nVal val) nds
findNextIndex :: FlagNode -> [Int] -> Int
findNextIndex (FlagNode _ nds) (h:t) = findNextIndex ((!!) nds h) t
findNextIndex (FlagNode _ nds) [] = length nds