[GraphQL] refactoring of endpoints

Mostly, it's about reducing their number and making things cleaner.

E.g. tree is not a different request, but a combination of various
"nodes" requests to get children/parent.
parent 931051af
Pipeline #7589 failed with stages
in 18 minutes and 45 seconds
<!--
* Copyright (c) 2021 GraphQL Contributors
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
This is a copy-paste from
https://github.com/graphql/graphiql/tree/main/examples/graphiql-cdn
adjusted to get the GGTX bearer token.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GraphiQL 4 with React 19 and GraphiQL Explorer</title>
<style>
body {
margin: 0;
overflow: hidden; /* in Firefox */
}
#graphiql {
height: 100dvh;
}
.loading {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 4rem;
}
</style>
<link
rel="stylesheet"
href="https://esm.sh/graphiql@4.0.0/dist/style.css"
/>
<link
rel="stylesheet"
href="https://esm.sh/@graphiql/plugin-explorer@4.0.0/dist/style.css"
/>
<!-- Note: the ?standalone flag bundles the module along with all of its `dependencies`, excluding `peerDependencies`, into a single JavaScript file. -->
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react@19.1.0",
"react/jsx-runtime": "https://esm.sh/react@19.1.0/jsx-runtime",
"react-dom": "https://esm.sh/react-dom@19.1.0",
"react-dom/client": "https://esm.sh/react-dom@19.1.0/client",
"graphiql": "https://esm.sh/graphiql@4.0.0?standalone&external=react,react/jsx-runtime,react-dom,@graphiql/react",
"@graphiql/plugin-explorer": "https://esm.sh/@graphiql/plugin-explorer@4.0.0?standalone&external=react,react/jsx-runtime,react-dom,@graphiql/react,graphql",
"@graphiql/react": "https://esm.sh/@graphiql/react@0.30.0?standalone&external=react,react/jsx-runtime,react-dom,graphql,@graphiql/toolkit",
"@graphiql/toolkit": "https://esm.sh/@graphiql/toolkit@0.11.2?standalone&external=graphql",
"graphql": "https://esm.sh/graphql@16.11.0"
}
}
</script>
<script type="module">
// Import React and ReactDOM
import React from 'react';
import ReactDOM from 'react-dom/client';
// Import GraphiQL and the Explorer plugin
import { GraphiQL } from 'graphiql';
import { createGraphiQLFetcher } from '@graphiql/toolkit';
import { explorerPlugin } from '@graphiql/plugin-explorer';
// find gargantext local storage session cookie
const sessions = JSON.parse(localStorage.getItem('garg-sessions')) || [];
const token = (sessions.filter((s) => s.backend.baseUrl == window.location.origin)[0] || {}).token;
console.log('found token', token);
const fetcher = createGraphiQLFetcher({
// url: 'https://countries.trevorblades.com',
url: 'http://localhost:8008/gql',
headers: {
'Authorization': `Bearer ${token}`,
},
});
const explorer = explorerPlugin();
function App() {
return React.createElement(GraphiQL, {
fetcher,
plugins: [explorer],
});
}
const container = document.getElementById('graphiql');
const root = ReactDOM.createRoot(container);
root.render(React.createElement(App));
</script>
</head>
<body>
<div id="graphiql">
<div class="loading">Loading…</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="SwaggerUI" />
<title>SwaggerUI</title>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-bundle.js" crossorigin></script>
<script>
window.onload = () => {
window.ui = SwaggerUIBundle({
url: `${window.location.origin}/swagger.json`,
dom_id: '#swagger-ui',
});
};
</script>
</body>
</html>
......@@ -1020,7 +1020,7 @@
"debouncing": "0.1.2",
"graphql-client": {
"git": "https://github.com/OxfordAbstracts/purescript-graphql-client",
"ref": "v10.0.3",
"ref": "694551e8baa992310eedf0a9d90f6e23b2e8d964",
"dependencies": [
"aff",
"affjax",
......@@ -1780,7 +1780,7 @@
"graphql-client": {
"type": "git",
"url": "https://github.com/OxfordAbstracts/purescript-graphql-client",
"rev": "7f85c4d64ab4b767ecc999429c1c37f7a6ad4816",
"rev": "694551e8baa992310eedf0a9d90f6e23b2e8d964",
"dependencies": [
"aff",
"affjax",
......
......@@ -20,7 +20,8 @@ workspace:
# https://github.com/OxfordAbstracts/purescript-graphql-client/issues/138
graphql-client:
git: https://github.com/OxfordAbstracts/purescript-graphql-client
ref: v10.0.3
ref: 694551e8baa992310eedf0a9d90f6e23b2e8d964
#ref: v10.0.3
dependencies:
- aff
- affjax
......
......@@ -17,7 +17,7 @@ import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), SpinnerTheme(..))
import Gargantext.Components.Category (ratingSimpleLoader)
import Gargantext.Components.Document.Types (DocPath, Document(..), LoadedData, initialState)
import Gargantext.Components.GraphQL.Endpoints (getContextNgrams)
import Gargantext.Components.GraphQL.Endpoints (getNgramsForContextAndList)
import Gargantext.Components.NgramsTable.AutoSync (useAutoSync)
import Gargantext.Components.Node (NodePoly(..))
import Gargantext.Core.NgramsTable.Functions (addNewNgramA, applyNgramsPatches, coreDispatch, findNgramRoot, setTermListA, computeCache)
......@@ -76,7 +76,7 @@ layoutCpt = R2.hereComponent here "layout" hCpt
useLoader
{ errorHandler: Nothing
, herePrefix: hp
, loader: \p -> getContextNgrams session p.contextId p.listId
, loader: \p -> getNgramsForContextAndList session p.contextId p.listId
, path: { contextId: nodeId, listId }
, render: \contextNgrams ->
layoutWithContextNgrams $ Record.merge props { contextNgrams }
......
......@@ -6,6 +6,7 @@ import DOM.Simple (window)
import Data.Array as A
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Traversable (traverse_)
import Effect (Effect)
import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Gargantext.AsyncTasks as GAT
......@@ -27,12 +28,12 @@ import Gargantext.Components.Forest.Tree.Node.Action.Update (updateRequest)
import Gargantext.Components.Forest.Tree.Node.Action.Upload (uploadArbitraryFile, uploadFile)
import Gargantext.Components.Forest.Tree.Node.Box (nodePopupView)
import Gargantext.Components.Forest.Tree.Node.Tools.SubTree.Types (SubTreeOut(..))
import Gargantext.Components.GraphQL.Endpoints (getNode, getTreeFirstLevel)
import Gargantext.Components.GraphQL.Endpoints (getNodeById, getTreeFirstLevel)
import Gargantext.Components.GraphQL.Node (Node)
import Gargantext.Components.GraphQL.Tree (TreeFirstLevel, TreeNode)
import Gargantext.Components.GraphQL.Tree (TreeFirstLevel)
import Gargantext.Config.REST (AffRESTError)
import Gargantext.Config.Utils (handleRESTError)
import Gargantext.Hooks.LinkHandler (useLinkHandler)
import Gargantext.Hooks.LinkHandler (useLinkHandler, Methods)
import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Routes (AppRoute(Home), appPath, nodeTypeAppRoute)
import Gargantext.Sessions (Session(..), sessionId)
......@@ -101,10 +102,10 @@ folderViewMainCpt = here.component "folderViewMainCpt" cpt
$ parent
<> childrenEl
makeFolderElements :: Array TreeNode -> Record FolderViewProps -> Array R.Element
makeFolderElements :: Array Node -> Record FolderViewProps -> Array R.Element
makeFolderElements folders' props = makeFolderElementsMap <$> folders'
where
makeFolderElementsMap :: TreeNode -> R.Element
makeFolderElementsMap :: Node -> R.Element
makeFolderElementsMap node = folder
{ nodeId: node.id
, linkId: node.id
......@@ -117,7 +118,7 @@ folderViewMainCpt = here.component "folderViewMainCpt" cpt
, text: node.name
}
makeParentFolder :: TreeNode -> Maybe TreeNode -> Record FolderViewProps -> Array R.Element
makeParentFolder :: Node -> Maybe Node -> Record FolderViewProps -> Array R.Element
makeParentFolder root (Just parent) props =
[ folder
{ linkId: parent.id
......@@ -133,7 +134,7 @@ folderViewMainCpt = here.component "folderViewMainCpt" cpt
]
makeParentFolder _ Nothing _ = []
sortFolders :: TreeNode -> TreeNode -> Ordering
sortFolders :: Node -> Node -> Ordering
sortFolders a b = compare a.id b.id
type FolderProps =
......@@ -298,7 +299,9 @@ backButtonSmartMainCpt = here.component "backButtonSmartMain" cpt
[ H.i { className: "fa fa-arrow-left", title: "Previous view" } []
]
where
action rootId pId handlers
action :: Int -> Maybe Int -> Record Methods -> Effect Unit
action _rootId Nothing _handlers = pure unit
action rootId (Just pId) handlers
| rootId == pId = handlers.goToRoute Home
| otherwise = handlers.goToPreviousPage unit
......@@ -315,7 +318,7 @@ loadFolders :: Record LoadProps -> AffRESTError TreeFirstLevel
loadFolders { nodeId, session } = getTreeFirstLevel session nodeId
loadNode :: Record LoadProps -> AffRESTError Node
loadNode { nodeId, session } = getNode session nodeId
loadNode { nodeId, session } = getNodeById session nodeId
type PerformActionProps =
( boxes :: Boxes
......
......@@ -11,7 +11,8 @@ import Data.String (Pattern(..), split)
import Gargantext.Components.App.Store as Store
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.GraphQL.Endpoints (getBreadcrumb, getNodeChildren, getNodeParent)
import Gargantext.Components.GraphQL.Tree (BreadcrumbInfo, TreeNode)
import Gargantext.Components.GraphQL.Node (Node)
import Gargantext.Components.GraphQL.Tree (BreadcrumbInfo)
import Gargantext.Config.REST (AffRESTError)
import Gargantext.Ends (Backend(..))
import Gargantext.Hooks.Loader (useLoader)
......@@ -195,10 +196,10 @@ breadcrumbViewMainCpt = here.component "breadcrumbViewMainCpt" cpt
pure $
R.fragment items
makeBreadcrumbElements :: Array TreeNode -> Session -> String -> Boolean -> Array R.Element
makeBreadcrumbElements :: Array Node -> Session -> String -> Boolean -> Array R.Element
makeBreadcrumbElements items' session format openTreeNodes = makeBreadcrumbElementsMap <$> items'
where
makeBreadcrumbElementsMap :: TreeNode -> R.Element
makeBreadcrumbElementsMap :: Node -> R.Element
makeBreadcrumbElementsMap node = breadcrumbItem
{ linkId: node.id
, linkNodeType: node.node_type
......@@ -353,8 +354,8 @@ loadBreadcrumbData { route: CorpusDocument _s corpusId listId documentId, sessio
loadBreadcrumbData { route: Dashboard _s nodeId, session } = do
loadBreadcrumbDataRaw { nodeId, session }
loadBreadcrumbData { route: Document _s listId documentId, session } = do
corpora <- getNodeParent session listId GT.Corpus
let nodeId = maybe listId _.id $ either (const Nothing) A.head corpora
corpus <- getNodeParent session listId GT.Corpus
let nodeId = either (const listId) _.id corpus
loadBreadcrumbDataRaw { nodeId, session }
loadBreadcrumbData { route: Folder _s nodeId, session } = do
loadBreadcrumbDataRaw { nodeId, session }
......
......@@ -260,16 +260,17 @@ nodeSpanCpt = here.component "nodeSpan" cpt
NT.NUpdateWorkerProgress ji atl -> do
-- TODO Fire this only once!
here.log3 "[nodeSpan] update job progress" ji atl
let wtl = GT.WorkerTaskWithLog { task: ji, lastLog: Just atl }
if GT.asyncTaskLogIsFinished atl then do
-- Handle error but only when the task is not already in the storage
-- (we want to avoid reporting the error multiple times, from different places).
-- See e.g. https://gitlab.iscpif.fr/gargantext/purescript-gargantext/issues/728
-- (error is thrown before the task marks any progress)
hasTask <- GAT.hasTask props.id ji boxes.tasks
hasTask <- GAT.hasTask props.id wtl boxes.tasks
unless hasTask $ do
handleErrorInAsyncTaskLog boxes.errors atl
else do
GAT.insert props.id ji boxes.tasks
GAT.insert props.id wtl boxes.tasks
_ -> pure unit
ws <- T.read boxes.wsNotification
let action = NT.InsertCallback (NT.UpdateTree props.id) ("node-span-" <> show props.id) cb
......
......@@ -25,6 +25,8 @@ import Gargantext.Routes (AppRoute)
import Gargantext.Sessions (Session(..))
import Gargantext.Types (NodeType)
import Gargantext.Utils.Reactix as R2
import GraphQL.Client.Args (OrArg, IgnoreArg, AndArg)
import GraphQL.Client.AsGql (AsGql)
import GraphQL.Client.BaseClients.Urql (UrqlClient, createClient)
import GraphQL.Client.Operation (OpMutation, OpQuery)
import GraphQL.Client.Query (decodeGqlRes) --, mutationJson)
......@@ -32,6 +34,7 @@ import GraphQL.Client.SafeQueryName (safeQueryName)
import GraphQL.Client.ToGqlString (toGqlQueryString)
import GraphQL.Client.Types (class GqlQuery, Client(..), class QueryClient, clientQuery, defQueryOpts, clientMutation, defMutationOpts)
import GraphQL.Client.Variables (class VarsTypeChecked, getVarsJson, getVarsTypeNames)
import Prim.Row as Row
import Simple.JSON as JSON
import Type.Data.List (Nil')
import Type.Proxy (Proxy(..))
......@@ -122,6 +125,7 @@ getClient (Session { token, backend: Backend b }) = createClient { headers, url:
queryGql
:: forall query returns
. GqlQuery Nil' OpQuery Schema query returns
-- => Row.Union Schema sr schema
=> JSON.ReadForeign returns
=> Session
-> String
......@@ -147,21 +151,24 @@ mutationGql session name q = do
-- Schema
type Schema =
{ annuaire_contacts :: { contact_id :: Int } -> Array AnnuaireContact
, context_ngrams :: { context_id :: Int, list_id :: Int } -> Array String
, contexts :: { context_id :: Int, node_id :: Int } -> Array GQLCTX.NodeContext
, contexts_for_ngrams :: { corpus_id :: Int, ngrams_terms :: GQLCTX.NgramsTerms, and_logic :: String } -> Array GQLCTX.Context
, imt_schools :: {} -> Array GQLIMT.School
{ annuaireContact :: { contactId :: Int } -> AnnuaireContact
, context :: { contextId :: Int, nodeId :: Int } -> GQLCTX.NodeContext
, contextsForNgrams :: { corpus_id :: Int, ngrams_terms :: GQLCTX.NgramsTerms, and_logic :: String } -> Array GQLCTX.Context
, corpus :: { id :: Int } -> GQLNode.Corpus
, languages :: {} -> Array GQLNLP.Language
, node_children :: { node_id :: Int, child_type :: NodeType } -> Array GQLNode.Node
, node_parent :: { node_id :: Int, parent_type :: NodeType } -> Array GQLNode.Node
, nodes :: { node_id :: Int } -> Array GQLNode.Node
, nodes_corpus :: { corpus_id :: Int } -> Array GQLNode.Corpus
, user_infos :: { user_id :: Int } -> Array UserInfo
, users :: { user_id :: Int } -> Array User
, ngramsForContextAndListId :: { contextId :: Int, listId :: Int } -> Array String
, node :: { id :: Int } -> GQLNode.Node
, nodes ::
{ parentId :: AsGql "Int" Int
, containsChildId :: AsGql "Int" Int
, deepChildId :: AsGql "Int" Int
, nodeType :: AsGql "NodeType" NodeType
}
-> Array GQLNode.Node
, schools :: {} -> Array GQLIMT.School
, team :: { team_node_id :: Int } -> Team
, tree :: { root_id :: Int } -> TreeFirstLevel
, tree_branch :: { node_id :: Int } -> BreadcrumbInfo
, user :: { id :: Int } -> User
, userInfo :: { userId :: Int } -> UserInfo
}
type Mutation =
......
......@@ -48,9 +48,9 @@ type AnnuaireContact =
}
type AnnuaireContactQuery =
{ annuaire_contacts ::
{ annuaireContact ::
Args
{ contact_id :: Var "id" Int }
{ contactId :: Var "id" Int }
{ ac_title :: Unit
, ac_source :: Unit
, ac_id :: Unit
......@@ -70,8 +70,8 @@ type AnnuaireContactQuery =
annuaireContactQuery :: AnnuaireContactQuery
annuaireContactQuery =
{ annuaire_contacts:
{ contact_id: Var :: _ "id" Int } =>>
{ annuaireContact:
{ contactId: Var :: _ "id" Int } =>>
GGQL.getFieldsStandard (Proxy :: _ AnnuaireContact)
}
......
......@@ -5,10 +5,11 @@ module Gargantext.Components.GraphQL.Context
, Hyperdata
, NodeContext
, NodeContext_
, nodeContextQuery
, ContextByIdAndNodeQuery
, getContextByIdAndNodeQuery
, NodeContextCategoryM
, contextsForNgramsQuery
, contextNgramsQuery
, getContextsForNgramsQuery
, getNgramsForContextAndListQuery
, NgramsTerms(..)
) where
......@@ -70,11 +71,11 @@ type NodeContext_ =
type NodeContext = Record NodeContext_
type NodeContextQuery =
{ contexts ::
type ContextByIdAndNodeQuery =
{ context ::
Args
{ context_id :: Var "context_id" Int
, node_id :: Var "node_id" Int
{ contextId :: Var "contextId" Int
, nodeId :: Var "nodeId" Int
}
{ nc_id :: Unit
, nc_node_id :: Unit
......@@ -84,17 +85,17 @@ type NodeContextQuery =
}
}
nodeContextQuery :: NodeContextQuery
nodeContextQuery =
{ contexts:
{ context_id: Var :: _ "context_id" Int
, node_id: Var :: _ "node_id" Int
getContextByIdAndNodeQuery :: ContextByIdAndNodeQuery
getContextByIdAndNodeQuery =
{ context:
{ contextId: Var :: _ "contextId" Int
, nodeId: Var :: _ "nodeId" Int
} =>>
GGQL.getFieldsStandard (Proxy :: _ NodeContext)
}
type ContextsForNgramsQuery =
{ contexts_for_ngrams ::
{ contextsForNgrams ::
Args
{ corpus_id :: Var "corpus_id" Int
, ngrams_terms :: Var "ngrams_terms" NgramsTerms
......@@ -131,9 +132,9 @@ type ContextsForNgramsQuery =
}
}
contextsForNgramsQuery :: ContextsForNgramsQuery
contextsForNgramsQuery =
{ contexts_for_ngrams:
getContextsForNgramsQuery :: ContextsForNgramsQuery
getContextsForNgramsQuery =
{ contextsForNgrams:
{ corpus_id: Var :: _ "corpus_id" Int
, ngrams_terms: Var :: _ "ngrams_terms" NgramsTerms
, and_logic: Var :: _ "and_logic" String
......@@ -141,20 +142,20 @@ contextsForNgramsQuery =
GGQL.getFieldsStandard (Proxy :: _ Context)
}
type ContextNgramsQuery =
{ context_ngrams ::
type NgramsForContextAndListQuery =
{ ngramsForContextAndListId ::
Args
{ context_id :: Var "context_id" Int
, list_id :: Var "list_id" Int
{ contextId :: Var "contextId" Int
, listId :: Var "listId" Int
}
Unit
}
contextNgramsQuery :: ContextNgramsQuery
contextNgramsQuery =
{ context_ngrams:
{ context_id: Var :: _ "context_id" Int
, list_id: Var :: _ "list_id" Int
getNgramsForContextAndListQuery :: NgramsForContextAndListQuery
getNgramsForContextAndListQuery =
{ ngramsForContextAndListId:
{ contextId: Var :: _ "contextId" Int
, listId: Var :: _ "listId" Int
} =>> unit
}
......
......@@ -2,7 +2,7 @@ module Gargantext.Components.GraphQL.Endpoints where
import Data.Array as A
import Data.Bifunctor (rmap)
import Data.Either (Either(..))
import Data.Either (Either(..), note)
import Data.Map as Map
import Data.Maybe (Maybe(..))
import Data.Tuple (Tuple(..))
......@@ -12,10 +12,10 @@ import Gargantext.Components.GraphQL.Contact (AnnuaireContact, annuaireContactQu
import Gargantext.Components.GraphQL.Context as GQLCTX
import Gargantext.Components.GraphQL.IMT as GQLIMT
import Gargantext.Components.GraphQL.NLP as GQLNLP
import Gargantext.Components.GraphQL.Node (Corpus, Node, nodeChildrenQuery, nodeParentQuery, nodesQuery, nodesCorpusQuery)
import Gargantext.Components.GraphQL.Node (Corpus, Node, getNodeChildrenQuery, getNodeParentQuery, getNodeByIdQuery, getCorpusByIdQuery, getNodesQuery)
import Gargantext.Components.GraphQL.Team (Team, teamQuery)
import Gargantext.Components.GraphQL.Tree (TreeFirstLevel, treeFirstLevelQuery, BreadcrumbInfo, breadcrumbQuery)
import Gargantext.Components.GraphQL.User (UserInfo, userInfoQuery, User, userQuery)
import Gargantext.Components.GraphQL.Tree (TreeFirstLevel, getTreeByRootIdQuery, BreadcrumbInfo, breadcrumbQuery)
import Gargantext.Components.GraphQL.User (UserInfo, getUserInfoQuery, User, getUserByIdQuery)
import Gargantext.Components.Lang (Lang)
import Gargantext.Config.REST (RESTError(..), AffRESTError)
import Gargantext.Core.NgramsTable.Types (NgramsTerm(..))
......@@ -23,8 +23,9 @@ import Gargantext.Prelude
import Gargantext.Routes (AppRoute(..))
import Gargantext.Sessions (Session(..))
import Gargantext.Types (CorpusId, NodeType)
import Gargantext.Utils.GraphQL (unwrapGraphQLResult)
import Gargantext.Utils.Reactix as R2
import GraphQL.Client.Args (onlyArgs)
import GraphQL.Client.Args (onlyArgs, OrArg(..), IgnoreArg(..))
import GraphQL.Client.Variables (withVars)
here :: R2.Here
......@@ -32,67 +33,50 @@ here = R2.here "Gargantext.Components.GraphQL.Endpoints"
getIMTSchools :: Session -> AffRESTError (Array GQLIMT.School)
getIMTSchools session = do
eRes <- queryGql session "get imt schools" $ GQLIMT.schoolsQuery
pure $ rmap _.imt_schools eRes
eRes <- queryGql session "get imt schools" $ GQLIMT.getSchoolsQuery
pure $ rmap _.schools eRes
-- liftEffect $ here.log2 "[getIMTSchools] imt_schools" imt_schools
-- pure $ Right imt_schools
getNode :: Session -> Int -> AffRESTError Node
getNode session nodeId = do
eRes <- queryGql session "get nodes" $ nodesQuery `withVars` { id: nodeId }
case eRes of
Left err -> pure $ Left err
Right { nodes } -> do
-- liftEffect $ here.log2 "[getNode] node" nodes
pure $ case A.head nodes of
Nothing -> Left (CustomError $ "node with id" <> show nodeId <> " not found")
Just node -> Right node
getNodeById :: Session -> Int -> AffRESTError Node
getNodeById session nodeId = do
let params = { id: nodeId }
eRes <- queryGql session "get node" $ getNodeByIdQuery `withVars` params
pure $ _.node <$> eRes
getNodeCorpus :: Session -> Int -> AffRESTError Corpus
getNodeCorpus session corpusId = do
eRes <- queryGql session "get nodes corpus" $
nodesCorpusQuery `withVars` { id: corpusId }
case eRes of
Left err -> pure $ Left err
Right { nodes_corpus } -> do
liftEffect $ here.log2 "[getNodesCorpus] nodes_corpus" nodes_corpus
pure $ case A.head nodes_corpus of
Nothing -> Left (CustomError $ "corpus with id" <> show corpusId <> " not found")
Just corpus -> Right corpus
getCorpusById :: Session -> Int -> AffRESTError Corpus
getCorpusById session corpusId = do
let params = { id: corpusId }
eRes <- queryGql session "get corpus by id" $
getCorpusByIdQuery `withVars` params
pure $ _.corpus <$> eRes
getNodeParent :: Session -> Int -> NodeType -> AffRESTError (Array Node)
getNodeParent :: Session -> Int -> NodeType -> AffRESTError Node
getNodeParent session nodeId parentType = do
let params = { nodeId, parentType }
eRes <- queryGql session "get node parent" $
nodeParentQuery `withVars`
{ id: nodeId
, parent_type: parentType
}
-- liftEffect $ here.log2 "[getNodeParent] node_parent" node_parent
--pure node_parent
pure $ rmap _.node_parent eRes
getNodeParentQuery nodeId parentType
pure $ unwrapGraphQLResult (Tuple "node parent" params) (_.parent <$> eRes)
getNodeChildren :: Session -> Int -> NodeType -> AffRESTError (Array Node)
getNodeChildren session nodeId childType = do
eRes <- queryGql session "get node child" $
nodeChildrenQuery `withVars`
let
params =
{ id: nodeId
, child_type: childType
, nodeType: childType
}
eRes <- queryGql session "get node child" $
getNodeChildrenQuery `withVars` params
-- liftEffect $ here.log2 "[getNodeParent] node_parent" node_parent
--pure node_parent
pure $ rmap _.node_children eRes
pure $ rmap _.nodes eRes
getUser :: Session -> Int -> AffRESTError User
getUser session id = do
eRes <- queryGql session "get user" $ userQuery `withVars` { id }
case eRes of
Left err -> pure $ Left err
Right { users } -> do
liftEffect $ here.log2 "[getUser] users" users
pure $ case A.head users of
Nothing -> Left (CustomError $ "user with id " <> show id <> " not found")
Just u -> Right u
let params = { id }
eRes <- queryGql session "get user by id" $ getUserByIdQuery `withVars` params
pure $ _.user <$> eRes
updateUserPubmedAPIKey :: Session -> Int -> String -> AffRESTError Unit
updateUserPubmedAPIKey session user_id api_key = do
......@@ -130,45 +114,31 @@ updateUserEPOAPIToken session user_id api_token = do
getUserInfo :: Session -> Int -> AffRESTError UserInfo
getUserInfo session id = do
eRes <- queryGql session "get user infos" $ userInfoQuery `withVars` { id }
case eRes of
Left err -> pure $ Left err
Right { user_infos } -> do
liftEffect $ here.log2 "[getUserInfo] user infos" user_infos
pure $ case A.head user_infos of
Nothing -> Left (CustomError $ "user with id " <> show id <> " not found")
-- NOTE Contact is at G.C.N.A.U.C.Types
Just ui -> Right ui
eRes <- queryGql session "get user info" $ getUserInfoQuery `withVars` { id }
pure $ _.userInfo <$> eRes
getAnnuaireContact :: Session -> Int -> AffRESTError AnnuaireContact
getAnnuaireContact session id = do
let params = { id }
eRes <- queryGql session "get annuaire contact" $
annuaireContactQuery `withVars` { id }
case eRes of
Left err -> pure $ Left err
Right { annuaire_contacts } -> do
liftEffect $ here.log2 "[getAnnuaireContact] data" annuaire_contacts
pure $ case A.head annuaire_contacts of
Nothing -> Left (CustomError $ "contact id=" <> show id <> " not found")
Just r -> Right r
annuaireContactQuery `withVars` params
pure $ _.annuaireContact <$> eRes
getTreeFirstLevel :: Session -> Int -> AffRESTError TreeFirstLevel
getTreeFirstLevel session id = do
eRes <- queryGql session "get tree first level" $ treeFirstLevelQuery `withVars` { id }
case eRes of
Left err -> pure $ Left err
Right { tree } -> do
-- liftEffect $ here.log2 "[getTreeFirstLevel] tree first level" tree
pure $ Right tree -- TODO: error handling
let params = { id }
eRes <- queryGql session "get tree first level" $
getTreeByRootIdQuery id
-- getTreeByRootIdQuery `withVars` params
pure $ (\{ root, children, parents } -> { root, children, parent: A.head parents }) <$> eRes
-- pure $ (\{ root } -> { root, children: [], parent: Nothing }) <$> eRes
getTeam :: Session -> Int -> AffRESTError Team
getTeam session id = do
eRes <- queryGql session "get team" $ teamQuery `withVars` { id }
case eRes of
Left err -> pure $ Left err
Right { team } -> do
liftEffect $ here.log2 "[getTree] data" team
pure $ Right team
let params = { id }
eRes <- queryGql session "get team" $ teamQuery `withVars` params
pure $ _.team <$> eRes
type SharedFolderId = Int
type TeamNodeId = Int
......@@ -194,29 +164,25 @@ deleteTeamMembership session sharedFolderId teamNodeId = do
getToken (Session { token }) = token
getNodeContext :: Session -> Int -> Int -> AffRESTError GQLCTX.NodeContext
getNodeContext session context_id node_id = do
let query = GQLCTX.nodeContextQuery `withVars` { context_id, node_id }
eRes <- queryGql session "get node context" query
case eRes of
Left err -> pure $ Left err
Right { contexts } -> do
--liftEffect $ here.log2 "[getNodeContext] node context" contexts
case A.head contexts of
Nothing -> pure $ Left $ CustomError "no node context found"
Just context -> pure $ Right context -- TODO: error handling
getNodeContext session contextId nodeId = do
let params = { contextId, nodeId }
eRes <- queryGql session "get context by id and node" $
GQLCTX.getContextByIdAndNodeQuery `withVars` params
pure $ _.context <$> eRes
type ContextsForNgramsGQL = { contexts_for_ngrams :: Array GQLCTX.Context }
type ContextsForNgramsGQL = { contextsForNgrams :: Array GQLCTX.Context }
getContextsForNgrams :: Session -> CorpusId -> Array String -> Boolean -> AffRESTError (Array GQLCTX.Context)
getContextsForNgrams session corpus_id ngrams_terms logic = do
let
query = GQLCTX.contextsForNgramsQuery `withVars`
params =
{ corpus_id
, ngrams_terms: GQLCTX.NgramsTerms ngrams_terms
, and_logic: show logic
}
query = GQLCTX.getContextsForNgramsQuery `withVars` params
eRes <- queryGql session "get contexts for ngrams" query
pure $ rmap _.contexts_for_ngrams eRes
pure $ rmap _.contextsForNgrams eRes
updateNodeContextCategory :: Session -> Int -> Int -> Int -> AffRESTError Int
updateNodeContextCategory session context_id node_id category = do
......@@ -238,21 +204,16 @@ getLanguages :: Session -> AffRESTError (Map.Map Lang GQLNLP.LanguageProperties)
getLanguages session = do
let query = GQLNLP.nlpQuery
eRes <- queryGql session "get languages" query
case eRes of
Left err -> pure $ Left err
Right { languages } -> do
liftEffect $ here.log2 "[getLanguages] languages" languages
pure $ Right $ Map.fromFoldable $ (\{ lt_lang, lt_server } -> Tuple lt_lang lt_server) <$> languages
pure $ (\res -> Map.fromFoldable $ (\{ lt_lang, lt_server } -> Tuple lt_lang lt_server) <$> res.languages) <$> eRes
getContextNgrams :: Session -> Int -> Int -> AffRESTError (Array NgramsTerm)
getContextNgrams session context_id list_id = do
let query = GQLCTX.contextNgramsQuery `withVars` { context_id, list_id }
eRes <- queryGql session "get context ngrams" query
pure $ rmap (\{ context_ngrams } -> NormNgramsTerm <$> context_ngrams) eRes
getNgramsForContextAndList :: Session -> Int -> Int -> AffRESTError (Array NgramsTerm)
getNgramsForContextAndList session contextId listId = do
let query = GQLCTX.getNgramsForContextAndListQuery `withVars` { contextId, listId }
eRes <- queryGql session "get ngrams for context and list" query
pure $ rmap (\{ ngramsForContextAndListId } -> NormNgramsTerm <$> ngramsForContextAndListId) eRes
getBreadcrumb :: Session -> Int -> AffRESTError BreadcrumbInfo
getBreadcrumb session node_id = do
eRes <- queryGql session "get breadcrumb branch" $ breadcrumbQuery `withVars` { node_id }
getBreadcrumb session nodeId = do
eRes <- queryGql session "get breadcrumb branch" $ breadcrumbQuery nodeId
-- liftEffect $ here.log2 "[getBreadcrumb] breadcrumb" tree_branch
pure $ rmap _.tree_branch eRes
pure $ eRes
......@@ -11,15 +11,15 @@ type School =
}
type SchoolsQuery =
{ imt_schools ::
{ schools ::
{ school_id :: Unit
, school_longName :: Unit
, school_shortName :: Unit
}
}
schoolsQuery :: SchoolsQuery
schoolsQuery =
{ imt_schools:
getSchoolsQuery :: SchoolsQuery
getSchoolsQuery =
{ schools:
GGQL.getFieldsStandard (Proxy :: _ School)
}
module Gargantext.Components.GraphQL.Node where
import Data.Maybe (Maybe(..))
import Gargantext.Prelude
import Gargantext.Types (NodeType)
import Gargantext.Utils.GraphQL as GGQL
import Gargantext.Types (NodeType)
import GraphQL.Client.Args (Args, (=>>))
import GraphQL.Client.Alias ((:))
import GraphQL.Client.Args (Args, (=>>), OrArg(..), IgnoreArg(..))
import GraphQL.Client.Variable (Var(..))
import Type.Proxy (Proxy(..))
type Corpus =
{ id :: Int
, name :: String
, parent_id :: Int
, parent_id :: Maybe Int
, type_id :: Int
, node_type :: NodeType
}
......@@ -19,59 +21,88 @@ type Corpus =
type Node =
{ id :: Int
, name :: String
, parent_id :: Int
, parent_id :: Maybe Int
, type_id :: Int
, node_type :: NodeType
}
type NodesCorpusQuery =
{ nodes_corpus ::
type NodeQ =
{ id :: Unit
, name :: Unit
, parent_id :: Unit
, type_id :: Unit
, node_type :: Unit
}
nodeQ :: NodeQ
nodeQ = GGQL.getFieldsStandard (Proxy :: _ Node)
type CorpusByIdQuery =
{ corpus ::
Args
{ corpus_id :: Var "id" Int }
{ id :: Unit
, name :: Unit
, parent_id :: Unit
, type_id :: Unit
, node_type :: Unit
}
{ id :: Var "id" Int }
NodeQ
}
type NodesQuery =
{ nodes ::
type NodeByIdQuery =
{ node ::
Args
{ node_id :: Var "id" Int }
{ id :: Unit
, name :: Unit
, parent_id :: Unit
, type_id :: Unit
, node_type :: Unit
}
{ id :: Var "id" Int }
NodeQ
}
getNodeByIdQuery :: NodeByIdQuery
getNodeByIdQuery =
{ node: { id: Var :: _ "id" Int } =>> nodeQ
}
nodesQuery :: NodesQuery
nodesQuery =
{ nodes: { node_id: Var :: _ "id" Int } =>>
GGQL.getFieldsStandard (Proxy :: _ Node)
type NodesQueryArgs =
{ parentId :: OrArg IgnoreArg Int
, containsChildId :: OrArg IgnoreArg Int
, nodeType :: OrArg IgnoreArg NodeType
}
nodesCorpusQuery :: NodesCorpusQuery
nodesCorpusQuery =
{ nodes_corpus: { corpus_id: Var :: _ "id" Int } =>>
type NodesQuery =
{ nodes :: Args NodesQueryArgs NodeQ }
getNodesQuery :: NodesQueryArgs -> NodesQuery
getNodesQuery args =
{ nodes: args =>> nodeQ }
-- getNodesQuery :: Maybe Int -> Maybe Int -> NodesQuery
-- getNodesQuery mParentId mChildId =
-- { nodes: { parentId: mVar mParentId
-- , containsChildId: mVar mChildId } =>>
-- -- , nodeType: Var :: _ "nodeType" (Maybe NodeType) } =>>
-- nodeQ
-- }
-- where
-- mVar Nothing = ArgL IgnoreArg
-- mVar (Just x) = ArgR x
getCorpusByIdQuery :: CorpusByIdQuery
getCorpusByIdQuery =
{ corpus: { id: Var :: _ "id" Int } =>>
GGQL.getFieldsStandard (Proxy :: _ Corpus)
}
nodeParentQuery =
{ node_parent:
{ node_id: Var :: _ "id" Int
, parent_type: Var :: _ "parent_type" NodeType
} =>>
GGQL.getFieldsStandard (Proxy :: _ Node)
getNodeParentQuery id nodeType =
{ parent:
nodes
:
{ containsChildId: id
, nodeType: nodeType
}
=>>
nodeQ
}
where
nodes = Proxy :: Proxy "nodes"
nodeChildrenQuery =
{ node_children:
{ node_id: Var :: _ "id" Int
, child_type: Var :: _ "child_type" NodeType
getNodeChildrenQuery =
{ nodes:
{ parentId: Var :: _ "id" Int
, nodeType: Var :: _ "nodeType" NodeType
} =>>
GGQL.getFieldsStandard (Proxy :: _ Node)
nodeQ
}
module Gargantext.Components.GraphQL.Tree where
import Data.Maybe (Maybe(..))
import Gargantext.Components.GraphQL.Node (Node, NodeQ, nodeQ, getNodeByIdQuery, getNodesQuery)
import Gargantext.Prelude
import Data.Maybe (Maybe)
import Gargantext.Routes (AppRoute)
import Gargantext.Types (NodeType)
import GraphQL.Client.Args ((=>>))
import Gargantext.Utils.GraphQL as GGQL
import GraphQL.Client.Alias ((:), Alias)
import GraphQL.Client.Args ((=>>), Args, OrArg(ArgR), IgnoreArg)
import GraphQL.Client.Variable (Var(..))
type TreeNode =
{ name :: String
, id :: Int
, node_type :: NodeType
, parent_id :: Maybe Int
}
import Type.Proxy (Proxy(..))
type TreeFirstLevel =
{ root :: TreeNode
, children :: Array TreeNode
, parent :: Maybe TreeNode
{ root :: Node
, children :: Array Node
, parent :: Maybe Node
}
type BreadcrumbInfo =
{ parents :: Array TreeNode }
{ parents :: Array Node }
treeFirstLevelQuery =
{ tree: { root_id: Var :: _ "id" Int } =>>
{ root:
{ name: unit
, node_type: unit
, id: unit
, parent_id: unit
}
, children:
{ name: unit
, node_type: unit
, id: unit
, parent_id: unit
}
, parent:
{ name: unit
, node_type: unit
, id: unit
, parent_id: unit
}
}
-- | This is a composite query that is composed of calling "node" and
-- | "nodes" with appropriate arguments.
getTreeByRootIdQuery
:: Int
-> { root ::
Alias (Proxy "node")
( Args
{ id :: Int }
NodeQ
)
, children ::
Alias (Proxy "nodes")
( Args
{ parentId :: Int }
NodeQ
)
, parents ::
Alias (Proxy "nodes")
( Args
{ containsChildId :: Int }
NodeQ
)
}
getTreeByRootIdQuery id =
{ root:
node : { id: id } =>> nodeQ
, children:
nodes : { parentId: id } =>> nodeQ
, parents:
nodes : { containsChildId: id } =>> nodeQ
}
where
node = Proxy :: Proxy "node"
nodes = Proxy :: Proxy "nodes"
breadcrumbQuery =
{ tree_branch: { node_id: Var :: _ "node_id" Int } =>>
{ parents:
{ name: unit
, node_type: unit
, id: unit
, parent_id: unit
}
}
breadcrumbQuery
:: Int
-> { parents ::
Alias (Proxy "nodes")
( Args
{ deepChildId :: Int }
NodeQ
)
}
breadcrumbQuery id =
{ parents:
nodes : { deepChildId: id } =>>
nodeQ
}
where
nodes = Proxy :: Proxy "nodes"
......@@ -47,8 +47,8 @@ type UserInfoM =
, ui_cwDescription :: String
}
userInfoQuery =
{ user_infos: { user_id: Var :: _ "id" Int } =>>
getUserInfoQuery =
{ userInfo: { userId: Var :: _ "id" Int } =>>
{ ui_id: unit
, ui_username: unit
, ui_email: unit
......@@ -178,8 +178,8 @@ showUser
showMUser u = maybe "" showUser u
userQuery =
{ users: { user_id: Var :: _ "id" Int } =>>
getUserByIdQuery =
{ user: { id: Var :: _ "id" Int } =>>
{ u_id: unit
, u_hyperdata:
{ shared:
......
......@@ -6,7 +6,7 @@ import Data.Maybe (Maybe(..), isJust)
import Data.Tuple.Nested ((/\))
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Corpus.Layout (layout)
import Gargantext.Components.GraphQL.Endpoints (getNode)
import Gargantext.Components.GraphQL.Endpoints (getNodeById)
import Gargantext.Config.REST (logRESTError)
import Gargantext.Hooks.Loader (useLoaderEffect)
import Gargantext.Hooks.Session (useSession)
......@@ -39,7 +39,7 @@ nodeCpt = here.component "node" cpt
let
errorHandler = logRESTError (R2.herePrefix here "[corpusLayout]")
loader { nodeId: nodeId_, session: session_ } = getNode session_ nodeId_
loader { nodeId: nodeId_, session: session_ } = getNodeById session_ nodeId_
-- | Hooks
-- |
......
......@@ -21,6 +21,7 @@ import Gargantext.Utils.Glyphicon (classNamePrefix, glyphiconToCharCode)
import Gargantext.Utils.SimpleJSON (encodeJsonArgonaut)
import GraphQL.Client.Args (class ArgGql)
import GraphQL.Client.GqlType (class GqlType)
import GraphQL.Client.ToGqlString (class GqlArgString)
import Prim.Row (class Union)
import Reactix as R
import Simple.JSON as JSON
......@@ -198,6 +199,8 @@ instance Argonaut.EncodeJson NodeType where
-- instance ArgGql String NodeType
-- instance ArgGql NodeType NodeType
instance GqlType NodeType "NodeType!"
instance GqlArgString NodeType where
toGqlArgStringImpl = show
instance Show NodeType where
show NodeUser = "NodeUser"
......
......@@ -5,12 +5,18 @@ module Gargantext.Utils.GraphQL
( class GetFieldsStandard
, PropGetFieldsStandard
, getFieldsStandard
, unwrapGraphQLResult
) where
import Prelude
import Data.Array as A
import Data.Bifunctor (rmap)
import Data.Either (Either(..), note)
import Data.HeytingAlgebra (class HeytingAlgebraRecord, tt)
import Data.Maybe (Maybe)
import Data.Tuple (Tuple(..))
import Gargantext.Config.REST (RESTError(CustomError))
import Heterogeneous.Mapping (class HMap, class Mapping, hmap)
import Prim.RowList (class RowToList)
import Type.Proxy (Proxy(..))
......@@ -54,3 +60,15 @@ instance propGetFieldsStandard ::
) =>
Mapping PropGetFieldsStandard t fields where
mapping PropGetFieldsStandard _ = getFieldsStandard (Proxy :: _ t)
-- | GraphQL returns an Either Error a result type and one usually has to get the head of a
unwrapGraphQLResult
:: forall a params
. (Show params)
=> Tuple String params
-> Either RESTError (Array a)
-> Either RESTError a
unwrapGraphQLResult _ (Left err) = Left err
unwrapGraphQLResult (Tuple name params) (Right res) =
note (CustomError $ "[unwrapGraphQLResult] " <> name <> " with params " <> show params <> " not found")
(A.head res)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment