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
10 -- TODO-SECURITY: Critical
12 -- TODO-ACCESS: CanGetNode
13 -- TODO-EVENTS: No events as this is a read only query.
15 -------------------------------------------------------------------
16 -- TODO-ACCESS: access by admin only.
17 -- At first let's just have an isAdmin check.
18 -- Later: check userId CanDeleteNodes Nothing
19 -- TODO-EVENTS: DeletedNodes [NodeId]
20 -- {"tag": "DeletedNodes", "nodes": [Int*]}
24 {-# OPTIONS_GHC -fno-warn-orphans #-}
26 {-# LANGUAGE DataKinds #-}
27 {-# LANGUAGE DeriveGeneric #-}
28 {-# LANGUAGE FlexibleContexts #-}
29 {-# LANGUAGE FlexibleInstances #-}
30 {-# LANGUAGE NoImplicitPrelude #-}
31 {-# LANGUAGE OverloadedStrings #-}
32 {-# LANGUAGE RankNTypes #-}
33 {-# LANGUAGE ScopedTypeVariables #-}
34 {-# LANGUAGE TemplateHaskell #-}
35 {-# LANGUAGE TypeOperators #-}
37 module Gargantext.API.Node
40 import Data.Aeson (FromJSON, ToJSON)
43 import Data.Text (Text())
44 import Data.Time (UTCTime)
45 import GHC.Generics (Generic)
47 import Test.QuickCheck (elements)
48 import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
50 import Gargantext.API.Admin.Auth (withAccess, PathId(..))
51 import Gargantext.API.Prelude
52 import Gargantext.API.Metrics
53 import Gargantext.API.Ngrams (TabType(..), TableNgramsApi, apiNgramsTableCorpus, QueryParamR)
54 import Gargantext.API.Ngrams.NTree (MyTree)
55 import Gargantext.API.Node.New
56 import Gargantext.API.Search (SearchDocsAPI, searchDocs, SearchPairsAPI, searchPairs)
57 import Gargantext.API.Table
58 import Gargantext.Core.Types (NodeTableResult)
59 import Gargantext.Core.Types.Main (Tree, NodeTree, ListType)
60 import Gargantext.Database.Action.Flow.Pairing (pairing)
61 import Gargantext.Database.Admin.Types.Metrics (ChartMetrics)
62 import Gargantext.Database.Query.Facet (FacetDoc, OrderBy(..))
63 import Gargantext.Database.Query.Table.Node
64 import Gargantext.Database.Query.Table.Node.Children (getChildren)
65 import Gargantext.Database.Query.Table.Node.UpdateOpaleye (updateHyperdata)
66 import Gargantext.Database.Query.Table.Node.User
67 import Gargantext.Database.Query.Tree (treeDB)
68 import Gargantext.Database.Admin.Config (nodeTypeId)
69 import Gargantext.Database.Query.Table.Node.Error (HasNodeError(..))
70 import Gargantext.Database.Admin.Types.Node
71 import Gargantext.Database.Prelude -- (Cmd, CmdM)
72 import Gargantext.Database.Schema.Node (_node_typename)
73 import Gargantext.Database.Query.Table.NodeNode
74 import Gargantext.Prelude
75 import Gargantext.Viz.Phylo.API (PhyloAPI, phyloAPI)
76 import Gargantext.Viz.Types
77 import qualified Gargantext.Database.Query.Table.Node.Update as U (update, Update(..))
80 import qualified Gargantext.Text.List.Learn as Learn
81 import qualified Data.Vector as Vec
84 type NodesAPI = Delete '[JSON] Int
87 -- Be careful: really delete nodes
88 -- Access by admin only
89 nodesAPI :: [NodeId] -> GargServer NodesAPI
90 nodesAPI ids = deleteNodes ids
92 ------------------------------------------------------------------------
93 -- | TODO-ACCESS: access by admin only.
94 -- At first let's just have an isAdmin check.
95 -- Later: CanAccessAnyNode or (CanGetAnyNode, CanPutAnyNode)
96 -- To manage the Users roots
99 -- TODO needs design discussion.
100 type Roots = Get '[JSON] [Node HyperdataUser]
101 :<|> Put '[JSON] Int -- TODO
103 -- | TODO: access by admin only
104 roots :: GargServer Roots
105 roots = getNodesWithParentId Nothing
106 :<|> pure (panic "not implemented yet") -- TODO use patch map to update what we need
108 -------------------------------------------------------------------
109 -- | Node API Types management
110 -- TODO-ACCESS : access by users
111 -- No ownership check is needed if we strictly follow the capability model.
113 -- CanGetNode (Node, Children, TableApi, TableNgramsApiGet, PairingApi, ChartApi,
115 -- CanRenameNode (or part of CanEditNode?)
116 -- CanCreateChildren (PostNodeApi)
117 -- CanEditNode / CanPutNode TODO not implemented yet
119 -- CanPatch (TableNgramsApi)
123 type NodeAPI a = Get '[JSON] (Node a)
124 :<|> "rename" :> RenameApi
125 :<|> PostNodeApi -- TODO move to children POST
127 :<|> ReqBody '[JSON] a :> Put '[JSON] Int
128 :<|> Delete '[JSON] Int
129 :<|> "children" :> ChildrenApi a
132 :<|> "table" :> TableApi
133 :<|> "ngrams" :> TableNgramsApi
135 :<|> "category" :> CatApi
136 :<|> "search" :> SearchDocsAPI
139 :<|> "pairwith" :> PairWith
140 :<|> "pairs" :> Pairs
141 :<|> "pairing" :> PairingApi
142 :<|> "searchPair" :> SearchPairsAPI
145 :<|> "metrics" :> ScatterAPI
146 :<|> "chart" :> ChartApi
148 :<|> "tree" :> TreeApi
149 :<|> "phylo" :> PhyloAPI
150 -- :<|> "add" :> NodeAddAPI
152 -- TODO-ACCESS: check userId CanRenameNode nodeId
153 -- TODO-EVENTS: NodeRenamed RenameNode or re-use some more general NodeEdited...
154 type RenameApi = Summary " Rename Node"
155 :> ReqBody '[JSON] RenameNode
158 type PostNodeApi = Summary " PostNode Node with ParentId as {id}"
159 :> ReqBody '[JSON] PostNode
160 :> Post '[JSON] [NodeId]
162 type ChildrenApi a = Summary " Summary children"
163 :> QueryParam "type" NodeType
164 :> QueryParam "offset" Int
165 :> QueryParam "limit" Int
166 -- :> Get '[JSON] [Node a]
167 :> Get '[JSON] (NodeTableResult a)
169 ------------------------------------------------------------------------
170 type NodeNodeAPI a = Get '[JSON] (Node a)
172 nodeNodeAPI :: forall proxy a. (JSONB a, ToJSON a)
177 -> GargServer (NodeNodeAPI a)
178 nodeNodeAPI p uId cId nId = withAccess (Proxy :: Proxy (NodeNodeAPI a)) Proxy uId (PathNodeNode cId nId) nodeNodeAPI'
180 nodeNodeAPI' :: GargServer (NodeNodeAPI a)
181 nodeNodeAPI' = getNodeWith nId p
183 ------------------------------------------------------------------------
184 -- TODO: make the NodeId type indexed by `a`, then we no longer need the proxy.
185 nodeAPI :: forall proxy a.
192 -> GargServer (NodeAPI a)
193 nodeAPI p uId id' = withAccess (Proxy :: Proxy (NodeAPI a)) Proxy uId (PathNode id') nodeAPI'
195 nodeAPI' :: GargServer (NodeAPI a)
196 nodeAPI' = getNodeWith id' p
198 :<|> postNode uId id'
199 :<|> postNodeAsyncAPI uId id'
201 :<|> deleteNodeApi id'
202 :<|> getChildren id' p
206 :<|> apiNgramsTableCorpus id'
221 :<|> phyloAPI id' uId
222 -- :<|> nodeAddAPI id'
223 -- :<|> postUpload id'
225 deleteNodeApi id'' = do
226 node' <- getNode id''
227 if _node_typename node' == nodeTypeId NodeUser
228 then panic "not allowed" -- TODO add proper Right Management Type
231 scatterApi :: NodeId -> GargServer ScatterAPI
232 scatterApi id' = getScatter id'
233 :<|> updateScatter id'
236 ------------------------------------------------------------------------
237 data RenameNode = RenameNode { r_name :: Text }
240 -- TODO unPrefix "r_" FromJSON, ToJSON, ToSchema, adapt frontend.
241 instance FromJSON RenameNode
242 instance ToJSON RenameNode
243 instance ToSchema RenameNode
244 instance Arbitrary RenameNode where
245 arbitrary = elements [RenameNode "test"]
246 ------------------------------------------------------------------------
247 ------------------------------------------------------------------------
248 type CatApi = Summary " To Categorize NodeNodes: 0 for delete, 1/null neutral, 2 favorite"
249 :> ReqBody '[JSON] NodesToCategory
252 data NodesToCategory = NodesToCategory { ntc_nodesId :: [NodeId]
253 , ntc_category :: Int
257 -- TODO unPrefix "ntc_" FromJSON, ToJSON, ToSchema, adapt frontend.
258 instance FromJSON NodesToCategory
259 instance ToJSON NodesToCategory
260 instance ToSchema NodesToCategory
262 catApi :: CorpusId -> GargServer CatApi
265 putCat :: CorpusId -> NodesToCategory -> Cmd err [Int]
266 putCat cId cs' = nodeNodesCategory $ map (\n -> (cId, n, ntc_category cs')) (ntc_nodesId cs')
268 ------------------------------------------------------------------------
269 -- TODO adapt FacetDoc -> ListDoc (and add type of document as column)
270 -- Pairing utilities to move elsewhere
271 type PairingApi = Summary " Pairing API"
272 :> QueryParam "view" TabType
273 -- TODO change TabType -> DocType (CorpusId for pairing)
274 :> QueryParam "offset" Int
275 :> QueryParam "limit" Int
276 :> QueryParam "order" OrderBy
277 :> Get '[JSON] [FacetDoc]
280 type Pairs = Summary "List of Pairs"
281 :> Get '[JSON] [AnnuaireId]
282 pairs :: CorpusId -> GargServer Pairs
284 ns <- getNodeNode cId
285 pure $ map _nn_node2_id ns
287 type PairWith = Summary "Pair a Corpus with an Annuaire"
288 :> "annuaire" :> Capture "annuaire_id" AnnuaireId
289 :> "list" :> Capture "list_id" ListId
292 pairWith :: CorpusId -> GargServer PairWith
293 pairWith cId aId lId = do
294 r <- pairing cId aId lId
295 _ <- insertNodeNode [ NodeNode cId aId Nothing Nothing]
298 ------------------------------------------------------------------------
299 type ChartApi = Summary " Chart API"
300 :> QueryParam "from" UTCTime
301 :> QueryParam "to" UTCTime
302 :> Get '[JSON] (ChartMetrics Histo)
304 type PieApi = Summary " Chart API"
305 :> QueryParam "from" UTCTime
306 :> QueryParam "to" UTCTime
307 :> QueryParamR "ngramsType" TabType
308 :> Get '[JSON] (ChartMetrics Histo)
310 type TreeApi = Summary " Tree API"
311 :> QueryParam "from" UTCTime
312 :> QueryParam "to" UTCTime
313 :> QueryParamR "ngramsType" TabType
314 :> QueryParamR "listType" ListType
315 :> Get '[JSON] (ChartMetrics [MyTree])
317 -- Depending on the Type of the Node, we could post
318 -- New documents for a corpus
319 -- New map list terms
320 -- :<|> "process" :> MultipartForm MultipartData :> Post '[JSON] Text
322 ------------------------------------------------------------------------
324 type TreeAPI = QueryParams "type" NodeType :> Get '[JSON] (Tree NodeTree)
326 treeAPI :: NodeId -> GargServer TreeAPI
329 ------------------------------------------------------------------------
330 -- | Check if the name is less than 255 char
331 rename :: NodeId -> RenameNode -> Cmd err [Int]
332 rename nId (RenameNode name') = U.update (U.Rename nId name')
334 putNode :: forall err a. (HasNodeError err, JSONB a, ToJSON a)
338 putNode n h = fromIntegral <$> updateHyperdata n h
339 -------------------------------------------------------------