]> Git — Sourcephile - gargantext.git/blob - src/Gargantext/API/Node.hs
[API] Favorites + documents (toTrash) routes.
[gargantext.git] / src / Gargantext / API / Node.hs
1 {-|
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
8 Portability : POSIX
9
10 Node API
11 -}
12
13 {-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-orphans #-}
14
15 {-# LANGUAGE DataKinds #-}
16 {-# LANGUAGE DeriveGeneric #-}
17 {-# LANGUAGE FlexibleContexts #-}
18 {-# LANGUAGE NoImplicitPrelude #-}
19 {-# LANGUAGE OverloadedStrings #-}
20 {-# LANGUAGE TemplateHaskell #-}
21 {-# LANGUAGE TypeOperators #-}
22
23 -------------------------------------------------------------------
24 module Gargantext.API.Node
25 ( module Gargantext.API.Node
26 , HyperdataCorpus(..)
27 , HyperdataResource(..)
28 , HyperdataUser(..)
29 , HyperdataDocument(..)
30 , HyperdataDocumentV3(..)
31 ) where
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)
38
39 import Data.Either(Either(Left))
40 import Data.Aeson (FromJSON, ToJSON, Value())
41 --import Data.Text (Text(), pack)
42 import Data.Text (Text())
43 import Data.Swagger
44 import Data.Time (UTCTime)
45
46 import Database.PostgreSQL.Simple (Connection)
47
48 import GHC.Generics (Generic)
49 import Servant
50
51 import Gargantext.Prelude
52 import Gargantext.Database.Types.Node
53 import Gargantext.Database.Node ( runCmd
54 , getNodesWithParentId
55 , getNode, getNodesWith, CorpusId
56 , deleteNode, deleteNodes, mk, JSONB)
57 import qualified Gargantext.Database.Node.Update as U (update, Update(..))
58 import Gargantext.Database.Facet (FacetDoc , runViewDocuments', OrderBy(..)
59 ,FacetChart)
60 import Gargantext.Database.Tree (treeDB, HasTreeError(..), TreeError(..))
61 import Gargantext.Database.NodeNode (nodesToFavorite, nodesToTrash)
62 -- Graph
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(..))
69
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
78
79 type NodesAPI = Delete '[JSON] Int
80
81
82 ------------------------------------------------------------------------
83 ------------------------------------------------------------------------
84 data RenameNode = RenameNode { r_name :: Text }
85 deriving (Generic)
86
87 instance FromJSON RenameNode
88 instance ToJSON RenameNode
89 instance ToSchema RenameNode
90 instance Arbitrary RenameNode where
91 arbitrary = elements [RenameNode "test"]
92
93 ------------------------------------------------------------------------
94
95 data PostNode = PostNode { pn_name :: Text
96 , pn_typename :: NodeType}
97 deriving (Generic)
98
99 instance FromJSON PostNode
100 instance ToJSON PostNode
101 instance ToSchema PostNode
102 instance Arbitrary PostNode where
103 arbitrary = elements [PostNode "Node test" NodeCorpus]
104
105 ------------------------------------------------------------------------
106 type DocsApi = "documents" :> Summary "Docs api"
107 :> ReqBody '[JSON] Documents
108 :> Delete '[JSON] [Int]
109
110 data Documents = Documents { documents :: [NodeId]}
111 deriving (Generic)
112
113 instance FromJSON Documents
114 instance ToJSON Documents
115 instance ToSchema Documents
116
117 delDocs :: Connection -> CorpusId -> Documents -> Handler [Int]
118 delDocs c cId ds = liftIO $ nodesToTrash c
119 $ map (\n -> (cId, n, True)) $ documents ds
120
121 ------------------------------------------------------------------------
122 type FavApi = "favorites" :> Summary "Modify statut"
123 :> ReqBody '[JSON] Favorites
124 :> Put '[JSON] [Int]
125 :<|> Summary "Delete"
126 :> ReqBody '[JSON] Favorites
127 :> Delete '[JSON] [Int]
128
129 data Favorites = Favorites { favorites :: [NodeId]}
130 deriving (Generic)
131
132 instance FromJSON Favorites
133 instance ToJSON Favorites
134 instance ToSchema Favorites
135
136 putFav :: Connection -> CorpusId -> Favorites -> Handler [Int]
137 putFav c cId fs = liftIO $ nodesToFavorite c
138 $ map (\n -> (cId, n, True)) $ favorites fs
139
140 delFav :: Connection -> CorpusId -> Favorites -> Handler [Int]
141 delFav c cId fs = liftIO $ nodesToFavorite c
142 $ map (\n -> (cId, n, False)) $ favorites fs
143
144 favApi :: Connection -> CorpusId -> (Favorites -> Handler [Int])
145 :<|> (Favorites -> Handler [Int])
146 favApi c cId = putFav c cId :<|> delFav c cId
147
148 ------------------------------------------------------------------------
149 type NodeAPI a = Get '[JSON] (Node a)
150 :<|> "rename" :> Summary " RenameNode Node"
151 :> ReqBody '[JSON] RenameNode
152 :> Put '[JSON] [Int]
153 :<|> Summary " PostNode Node with ParentId as {id}"
154 :> ReqBody '[JSON] PostNode
155 :> Post '[JSON] [Int]
156 :<|> Put '[JSON] Int
157 :<|> Delete '[JSON] Int
158 :<|> "children" :> Summary " Summary children"
159 :> QueryParam "type" NodeType
160 :> QueryParam "offset" Int
161 :> QueryParam "limit" Int
162 :> Get '[JSON] [Node a]
163 :<|> Summary " Tabs" :> FacetDocAPI
164
165
166 -- TODO: make the NodeId type indexed by `a`, then we no longer need the proxy.
167 nodeAPI :: JSONB a => Connection -> proxy a -> NodeId -> Server (NodeAPI a)
168 nodeAPI conn p id
169 = liftIO (getNode conn id p)
170 :<|> rename conn id
171 :<|> postNode conn id
172 :<|> putNode conn id
173 :<|> deleteNode' conn id
174 :<|> getNodesWith' conn id p
175 :<|> getTable conn id
176 :<|> getChart conn id
177 :<|> favApi conn id
178 :<|> delDocs conn id
179 -- :<|> upload
180 -- :<|> query
181
182
183 --data FacetFormat = Table | Chart
184 data FacetType = Docs | Terms | Sources | Authors | Trash
185 deriving (Generic, Enum, Bounded)
186
187 instance FromHttpApiData FacetType
188 where
189 parseUrlPiece "Docs" = pure Docs
190 parseUrlPiece "Terms" = pure Terms
191 parseUrlPiece "Sources" = pure Sources
192 parseUrlPiece "Authors" = pure Authors
193 parseUrlPiece "Trash" = pure Trash
194 parseUrlPiece _ = Left "Unexpected value of FacetType"
195
196 instance ToParamSchema FacetType
197 instance ToJSON FacetType
198 instance FromJSON FacetType
199 instance ToSchema FacetType
200 instance Arbitrary FacetType
201 where
202 arbitrary = elements [minBound .. maxBound]
203
204 ------------------------------------------------------------------------
205 type FacetDocAPI = "table"
206 :> Summary " Table data"
207 :> QueryParam "view" FacetType
208 :> QueryParam "offset" Int
209 :> QueryParam "limit" Int
210 :> QueryParam "order" OrderBy
211 :> Get '[JSON] [FacetDoc]
212
213 :<|> "chart"
214 :> Summary " Chart data"
215 :> QueryParam "from" UTCTime
216 :> QueryParam "to" UTCTime
217 :> Get '[JSON] [FacetChart]
218 :<|> Summary " Favorites" :> FavApi
219 :<|> Summary " Documents" :> DocsApi
220
221 -- Depending on the Type of the Node, we could post
222 -- New documents for a corpus
223 -- New map list terms
224 -- :<|> "process" :> MultipartForm MultipartData :> Post '[JSON] Text
225
226 -- To launch a query and update the corpus
227 -- :<|> "query" :> Capture "string" Text :> Get '[JSON] Text
228
229
230 -- | Node API functions
231 roots :: Connection -> Server Roots
232 roots conn = liftIO (putStrLn ( "/user" :: Text) >> getNodesWithParentId 0 Nothing conn)
233 :<|> pure (panic "not implemented yet") -- TODO
234 :<|> pure (panic "not implemented yet") -- TODO
235 :<|> pure (panic "not implemented yet") -- TODO
236
237
238 ------------------------------------------------------------------------
239 type GraphAPI = Get '[JSON] Graph
240 graphAPI :: Connection -> NodeId -> Server GraphAPI
241 graphAPI _ _ = liftIO $ textFlow (Mono EN) (Contexts contextText)
242 -- TODO what do we get about the node? to replace contextText
243
244 -- TODO(orphan): There should be a proper APIError data type with a case TreeError.
245 instance HasTreeError ServantErr where
246 _TreeError = prism' mk (const Nothing) -- Note a prism
247 where
248 mk NoRoot = err404 { errBody = "Root node not found" }
249 mk EmptyRoot = err500 { errBody = "Root node should not be empty" }
250 mk TooManyRoots = err500 { errBody = "Too many root nodes" }
251
252 type TreeAPI = Get '[JSON] (Tree NodeTree)
253 treeAPI :: Connection -> NodeId -> Server TreeAPI
254 treeAPI = treeDB
255
256 ------------------------------------------------------------------------
257 -- | Check if the name is less than 255 char
258 --rename :: Connection -> NodeId -> Rename -> Server NodeAPI
259 rename :: Connection -> NodeId -> RenameNode -> Handler [Int]
260 rename c nId (RenameNode name) = liftIO $ U.update (U.Rename nId name) c
261
262 nodesAPI :: Connection -> [NodeId] -> Server NodesAPI
263 nodesAPI conn ids = deleteNodes' conn ids
264
265 getTable :: Connection -> NodeId -> Maybe FacetType
266 -> Maybe Offset -> Maybe Limit
267 -> Maybe OrderBy -> Handler [FacetDoc]
268 getTable c cId ft o l order = liftIO $ case ft of
269 (Just Docs) -> runViewDocuments' c cId False o l order
270 (Just Trash) -> runViewDocuments' c cId True o l order
271 _ -> panic "not implemented"
272
273 getChart :: Connection -> NodeId -> Maybe UTCTime -> Maybe UTCTime
274 -> Handler [FacetChart]
275 getChart _ _ _ _ = undefined -- TODO
276
277 postNode :: Connection -> NodeId -> PostNode -> Handler [Int]
278 postNode c pId (PostNode name nt) = liftIO $ mk c nt (Just pId) name
279
280 putNode :: Connection -> NodeId -> Handler Int
281 putNode = undefined -- TODO
282
283 deleteNodes' :: Connection -> [NodeId] -> Handler Int
284 deleteNodes' conn ids = liftIO (runCmd conn $ deleteNodes ids)
285
286 deleteNode' :: Connection -> NodeId -> Handler Int
287 deleteNode' conn id = liftIO (runCmd conn $ deleteNode id)
288
289 getNodesWith' :: JSONB a => Connection -> NodeId -> proxy a -> Maybe NodeType
290 -> Maybe Int -> Maybe Int -> Handler [Node a]
291 getNodesWith' conn id p nodeType offset limit = liftIO (getNodesWith conn id p nodeType offset limit)
292
293
294
295 query :: Text -> Handler Text
296 query s = pure s
297
298
299 -- | Upload files
300 -- TODO Is it possible to adapt the function according to iValue input ?
301 --upload :: MultipartData -> Handler Text
302 --upload multipartData = do
303 -- liftIO $ do
304 -- putStrLn "Inputs:"
305 -- forM_ (inputs multipartData) $ \input ->
306 -- putStrLn $ " " <> show (iName input)
307 -- <> " -> " <> show (iValue input)
308 --
309 -- forM_ (files multipartData) $ \file -> do
310 -- content <- readFile (fdFilePath file)
311 -- putStrLn $ "Content of " <> show (fdFileName file)
312 -- <> " at " <> fdFilePath file
313 -- putStrLn content
314 -- pure (pack "Data loaded")
315