2 Module : Gargantext.API.Node
3 Description : Server API
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
13 {-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-orphans #-}
15 {-# LANGUAGE DataKinds #-}
16 {-# LANGUAGE DeriveGeneric #-}
17 {-# LANGUAGE FlexibleContexts #-}
18 {-# LANGUAGE NoImplicitPrelude #-}
19 {-# LANGUAGE OverloadedStrings #-}
20 {-# LANGUAGE TemplateHaskell #-}
21 {-# LANGUAGE TypeOperators #-}
23 -------------------------------------------------------------------
24 module Gargantext.API.Node
25 ( module Gargantext.API.Node
27 , HyperdataResource(..)
29 , HyperdataDocument(..)
30 , HyperdataDocumentV3(..)
32 -------------------------------------------------------------------
33 import Prelude (Enum, Bounded, minBound, maxBound)
34 import Control.Lens (prism')
35 import Control.Monad.IO.Class (liftIO)
36 import Control.Monad ((>>))
37 --import System.IO (putStrLn, readFile)
39 import Data.Either(Either(Left))
40 import Data.Aeson (FromJSON, ToJSON, Value())
41 --import Data.Text (Text(), pack)
42 import Data.Text (Text())
44 import Data.Time (UTCTime)
46 import Database.PostgreSQL.Simple (Connection)
48 import GHC.Generics (Generic)
51 import Gargantext.Prelude
52 import Gargantext.Database.Types.Node
53 import Gargantext.Database.Node ( runCmd
54 , getNodesWithParentId
55 , getNode, getNodesWith
56 , deleteNode, deleteNodes, mk, JSONB)
57 import qualified Gargantext.Database.Node.Update as U (update, Update(..))
58 import Gargantext.Database.Facet (FacetDoc , runViewDocuments', OrderBy(..)
60 import Gargantext.Database.Tree (treeDB, HasTreeError(..), TreeError(..))
63 import Gargantext.TextFlow
64 import Gargantext.Viz.Graph (Graph)
65 import Gargantext.Core (Lang(..))
66 import Gargantext.Core.Types (Offset, Limit)
67 import Gargantext.Core.Types.Main (Tree, NodeTree)
68 import Gargantext.Text.Terms (TermType(..))
70 import Test.QuickCheck (elements)
71 import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
72 -------------------------------------------------------------------
73 -- | Node API Types management
74 type Roots = Get '[JSON] [Node Value]
75 :<|> Post '[JSON] Int -- TODO
76 :<|> Put '[JSON] Int -- TODO
77 :<|> Delete '[JSON] Int -- TODO
79 type NodesAPI = Delete '[JSON] Int
82 ------------------------------------------------------------------------
83 ------------------------------------------------------------------------
84 data RenameNode = RenameNode { r_name :: Text }
87 instance FromJSON RenameNode
88 instance ToJSON RenameNode
89 instance ToSchema RenameNode
90 instance Arbitrary RenameNode where
91 arbitrary = elements [RenameNode "test"]
93 ------------------------------------------------------------------------
95 data PostNode = PostNode { pn_name :: Text
96 , pn_typename :: NodeType}
99 instance FromJSON PostNode
100 instance ToJSON PostNode
101 instance ToSchema PostNode
102 instance Arbitrary PostNode where
103 arbitrary = elements [PostNode "Node test" NodeCorpus]
105 ------------------------------------------------------------------------
106 ------------------------------------------------------------------------
107 type NodeAPI a = Get '[JSON] (Node a)
108 :<|> "rename" :> Summary " RenameNode Node"
109 :> ReqBody '[JSON] RenameNode
111 :<|> Summary " PostNode Node with ParentId as {id}"
112 :> ReqBody '[JSON] PostNode
113 :> Post '[JSON] [Int]
115 :<|> Delete '[JSON] Int
116 :<|> "children" :> Summary " Summary children"
117 :> QueryParam "type" NodeType
118 :> QueryParam "offset" Int
119 :> QueryParam "limit" Int
120 :> Get '[JSON] [Node a]
121 :<|> Summary " Tabs" :> FacetDocAPI
123 --data FacetFormat = Table | Chart
124 data FacetType = Docs | Terms | Sources | Authors | Trash
125 deriving (Generic, Enum, Bounded)
127 instance FromHttpApiData FacetType
129 parseUrlPiece "Docs" = pure Docs
130 parseUrlPiece "Terms" = pure Terms
131 parseUrlPiece "Sources" = pure Sources
132 parseUrlPiece "Authors" = pure Authors
133 parseUrlPiece "Trash" = pure Trash
134 parseUrlPiece _ = Left "Unexpected value of FacetType"
136 instance ToParamSchema FacetType
137 instance ToJSON FacetType
138 instance FromJSON FacetType
139 instance ToSchema FacetType
140 instance Arbitrary FacetType
142 arbitrary = elements [minBound .. maxBound]
144 type FacetDocAPI = "table"
145 :> Summary " Table data"
146 :> QueryParam "view" FacetType
147 :> QueryParam "offset" Int
148 :> QueryParam "limit" Int
149 :> QueryParam "order" OrderBy
150 :> Get '[JSON] [FacetDoc]
153 :> Summary " Chart data"
154 :> QueryParam "from" UTCTime
155 :> QueryParam "to" UTCTime
156 :> Get '[JSON] [FacetChart]
158 -- Depending on the Type of the Node, we could post
159 -- New documents for a corpus
160 -- New map list terms
161 -- :<|> "process" :> MultipartForm MultipartData :> Post '[JSON] Text
163 -- To launch a query and update the corpus
164 -- :<|> "query" :> Capture "string" Text :> Get '[JSON] Text
167 -- | Node API functions
168 roots :: Connection -> Server Roots
169 roots conn = liftIO (putStrLn ( "/user" :: Text) >> getNodesWithParentId 0 Nothing conn)
170 :<|> pure (panic "not implemented yet") -- TODO
171 :<|> pure (panic "not implemented yet") -- TODO
172 :<|> pure (panic "not implemented yet") -- TODO
175 type GraphAPI = Get '[JSON] Graph
176 graphAPI :: Connection -> NodeId -> Server GraphAPI
177 graphAPI _ _ = liftIO $ textFlow (Mono EN) (Contexts contextText)
178 -- TODO what do we get about the node? to replace contextText
180 -- TODO(orphan): There should be a proper APIError data type with a case TreeError.
181 instance HasTreeError ServantErr where
182 _TreeError = prism' mk (const Nothing) -- Note a prism
184 mk NoRoot = err404 { errBody = "Root node not found" }
185 mk EmptyRoot = err500 { errBody = "Root node should not be empty" }
186 mk TooManyRoots = err500 { errBody = "Too many root nodes" }
188 type TreeAPI = Get '[JSON] (Tree NodeTree)
189 treeAPI :: Connection -> NodeId -> Server TreeAPI
192 -- TODO: make the NodeId type indexed by `a`, then we no longer need the proxy.
193 nodeAPI :: JSONB a => Connection -> proxy a -> NodeId -> Server (NodeAPI a)
195 = liftIO (getNode conn id p)
197 :<|> postNode conn id
199 :<|> deleteNode' conn id
200 :<|> getNodesWith' conn id p
201 :<|> getTable conn id
202 :<|> getChart conn id
205 -- | Check if the name is less than 255 char
206 --rename :: Connection -> NodeId -> Rename -> Server NodeAPI
207 rename :: Connection -> NodeId -> RenameNode -> Handler [Int]
208 rename c nId (RenameNode name) = liftIO $ U.update (U.Rename nId name) c
210 nodesAPI :: Connection -> [NodeId] -> Server NodesAPI
211 nodesAPI conn ids = deleteNodes' conn ids
213 getTable :: Connection -> NodeId -> Maybe FacetType -> Maybe Offset -> Maybe Limit -> Maybe OrderBy -> Handler [FacetDoc]
214 getTable c cId ft o l order = liftIO $ case ft of
215 (Just Docs) -> runViewDocuments' c cId False o l order
216 (Just Trash) -> runViewDocuments' c cId True o l order
217 _ -> panic "not implemented"
222 postNode :: Connection -> NodeId -> PostNode -> Handler [Int]
223 postNode c pId (PostNode name nt) = liftIO $ mk c nt (Just pId) name
225 putNode :: Connection -> NodeId -> Handler Int
226 putNode = undefined -- TODO
228 deleteNodes' :: Connection -> [NodeId] -> Handler Int
229 deleteNodes' conn ids = liftIO (runCmd conn $ deleteNodes ids)
231 deleteNode' :: Connection -> NodeId -> Handler Int
232 deleteNode' conn id = liftIO (runCmd conn $ deleteNode id)
234 getNodesWith' :: JSONB a => Connection -> NodeId -> proxy a -> Maybe NodeType
235 -> Maybe Int -> Maybe Int -> Handler [Node a]
236 getNodesWith' conn id p nodeType offset limit = liftIO (getNodesWith conn id p nodeType offset limit)
239 getChart :: Connection -> NodeId -> Maybe UTCTime -> Maybe UTCTime
240 -> Handler [FacetChart]
241 getChart _ _ _ _ = undefined -- TODO
244 query :: Text -> Handler Text
249 -- TODO Is it possible to adapt the function according to iValue input ?
250 --upload :: MultipartData -> Handler Text
251 --upload multipartData = do
253 -- putStrLn "Inputs:"
254 -- forM_ (inputs multipartData) $ \input ->
255 -- putStrLn $ " " <> show (iName input)
256 -- <> " -> " <> show (iValue input)
258 -- forM_ (files multipartData) $ \file -> do
259 -- content <- readFile (fdFilePath file)
260 -- putStrLn $ "Content of " <> show (fdFileName file)
261 -- <> " at " <> fdFilePath file
263 -- pure (pack "Data loaded")