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'
234 chartApi :: NodeId -> GargServer ChartApi
235 chartApi id' = getChart id'
239 ------------------------------------------------------------------------
240 data RenameNode = RenameNode { r_name :: Text }
243 -- TODO unPrefix "r_" FromJSON, ToJSON, ToSchema, adapt frontend.
244 instance FromJSON RenameNode
245 instance ToJSON RenameNode
246 instance ToSchema RenameNode
247 instance Arbitrary RenameNode where
248 arbitrary = elements [RenameNode "test"]
249 ------------------------------------------------------------------------
250 ------------------------------------------------------------------------
251 type CatApi = Summary " To Categorize NodeNodes: 0 for delete, 1/null neutral, 2 favorite"
252 :> ReqBody '[JSON] NodesToCategory
255 data NodesToCategory = NodesToCategory { ntc_nodesId :: [NodeId]
256 , ntc_category :: Int
260 -- TODO unPrefix "ntc_" FromJSON, ToJSON, ToSchema, adapt frontend.
261 instance FromJSON NodesToCategory
262 instance ToJSON NodesToCategory
263 instance ToSchema NodesToCategory
265 catApi :: CorpusId -> GargServer CatApi
268 putCat :: CorpusId -> NodesToCategory -> Cmd err [Int]
269 putCat cId cs' = nodeNodesCategory $ map (\n -> (cId, n, ntc_category cs')) (ntc_nodesId cs')
271 ------------------------------------------------------------------------
272 -- TODO adapt FacetDoc -> ListDoc (and add type of document as column)
273 -- Pairing utilities to move elsewhere
274 type PairingApi = Summary " Pairing API"
275 :> QueryParam "view" TabType
276 -- TODO change TabType -> DocType (CorpusId for pairing)
277 :> QueryParam "offset" Int
278 :> QueryParam "limit" Int
279 :> QueryParam "order" OrderBy
280 :> Get '[JSON] [FacetDoc]
283 type Pairs = Summary "List of Pairs"
284 :> Get '[JSON] [AnnuaireId]
285 pairs :: CorpusId -> GargServer Pairs
287 ns <- getNodeNode cId
288 pure $ map _nn_node2_id ns
290 type PairWith = Summary "Pair a Corpus with an Annuaire"
291 :> "annuaire" :> Capture "annuaire_id" AnnuaireId
292 :> "list" :> Capture "list_id" ListId
295 pairWith :: CorpusId -> GargServer PairWith
296 pairWith cId aId lId = do
297 r <- pairing cId aId lId
298 _ <- insertNodeNode [ NodeNode cId aId Nothing Nothing]
301 ------------------------------------------------------------------------
302 type ChartApi = Summary " Chart API"
303 :> QueryParam "from" UTCTime
304 :> QueryParam "to" UTCTime
305 :> Get '[JSON] (ChartMetrics Histo)
306 :<|> Summary "SepGen IncExc chart update"
307 :> QueryParam "list" ListId
308 :> QueryParamR "ngramsType" TabType
309 :> QueryParam "limit" Int
312 type PieApi = Summary " Chart API"
313 :> QueryParam "from" UTCTime
314 :> QueryParam "to" UTCTime
315 :> QueryParamR "ngramsType" TabType
316 :> Get '[JSON] (ChartMetrics Histo)
318 type TreeApi = Summary " Tree API"
319 :> QueryParam "from" UTCTime
320 :> QueryParam "to" UTCTime
321 :> QueryParamR "ngramsType" TabType
322 :> QueryParamR "listType" ListType
323 :> Get '[JSON] (ChartMetrics [MyTree])
325 -- Depending on the Type of the Node, we could post
326 -- New documents for a corpus
327 -- New map list terms
328 -- :<|> "process" :> MultipartForm MultipartData :> Post '[JSON] Text
330 ------------------------------------------------------------------------
332 type TreeAPI = QueryParams "type" NodeType :> Get '[JSON] (Tree NodeTree)
334 treeAPI :: NodeId -> GargServer TreeAPI
337 ------------------------------------------------------------------------
338 -- | Check if the name is less than 255 char
339 rename :: NodeId -> RenameNode -> Cmd err [Int]
340 rename nId (RenameNode name') = U.update (U.Rename nId name')
342 putNode :: forall err a. (HasNodeError err, JSONB a, ToJSON a)
346 putNode n h = fromIntegral <$> updateHyperdata n h
347 -------------------------------------------------------------