Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
haskell-gargantext
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
199
Issues
199
List
Board
Labels
Milestones
Merge Requests
12
Merge Requests
12
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
gargantext
haskell-gargantext
Commits
0b9e0385
Commit
0b9e0385
authored
Feb 09, 2022
by
Alp Mestanogullari
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add client executable to run 'scripts' against a running Garg backend
parent
41fe94e7
Pipeline
#2454
passed with stage
in 45 minutes and 51 seconds
Changes
9
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
249 additions
and
33 deletions
+249
-33
Auth.hs
bin/gargantext-client/Auth.hs
+44
-0
Core.hs
bin/gargantext-client/Core.hs
+24
-0
Main.hs
bin/gargantext-client/Main.hs
+24
-0
Options.hs
bin/gargantext-client/Options.hs
+14
-0
Script.hs
bin/gargantext-client/Script.hs
+45
-0
Tracking.hs
bin/gargantext-client/Tracking.hs
+68
-0
cabal.project
cabal.project
+7
-15
Client.hs
src/Gargantext/API/Client.hs
+22
-17
stack.yaml
stack.yaml
+1
-1
No files found.
bin/gargantext-client/Auth.hs
0 → 100644
View file @
0b9e0385
module
Auth
where
import
Core
import
Options
import
Control.Monad.IO.Class
import
Data.Text.Encoding
(
encodeUtf8
)
import
Options.Generic
import
Servant.Client
import
qualified
Servant.Auth.Client
as
SA
import
Gargantext.API.Client
import
qualified
Gargantext.API.Admin.Auth.Types
as
Auth
import
qualified
Gargantext.Core.Types.Individu
as
Auth
import
qualified
Gargantext.Database.Admin.Types.Node
as
Node
-- | Authenticate and use the resulting Token to perform
-- auth-restricted actions
withAuthToken
::
ClientOpts
-- ^ source of user/pass data
->
(
SA
.
Token
->
Node
.
NodeId
->
ClientM
a
)
-- ^ do something once authenticated
->
ClientM
a
withAuthToken
opts
act
-- both user and password CLI arguments passed
|
Helpful
(
Just
usr
)
<-
user
opts
,
Helpful
(
Just
pw
)
<-
pass
opts
=
do
authRes
<-
postAuth
(
Auth
.
AuthRequest
usr
(
Auth
.
GargPassword
pw
))
case
Auth
.
_authRes_valid
authRes
of
-- authentication failed, this function critically needs it to
-- be able to run the action, so we abort
Nothing
->
problem
$
"invalid auth response: "
++
maybe
""
(
show
.
Auth
.
_authInv_message
)
(
Auth
.
_authRes_inval
authRes
)
-- authentication went through, we can run the action
Just
(
Auth
.
AuthValid
tok
tree_id
)
->
do
let
tok'
=
SA
.
Token
(
encodeUtf8
tok
)
whenVerbose
opts
$
do
liftIO
.
putStrLn
$
"[Debug] Authenticated: token="
++
show
tok
++
", tree_id="
++
show
tree_id
act
tok'
tree_id
-- user and/or pass CLI arguments not passed
|
otherwise
=
problem
"auth-protected actions require --user and --pass"
bin/gargantext-client/Core.hs
0 → 100644
View file @
0b9e0385
module
Core
(
problem
,
whenVerbose
)
where
import
Options
import
Options.Generic
import
Control.Exception
import
Control.Monad
import
Control.Monad.Catch
import
Servant.Client
newtype
GargClientException
=
GCE
String
instance
Show
GargClientException
where
show
(
GCE
s
)
=
"Garg client exception: "
++
s
instance
Exception
GargClientException
-- | Abort with a message
problem
::
String
->
ClientM
a
problem
=
throwM
.
GCE
-- | Only run the given computation when the @--verbose@ flag is
-- passed.
whenVerbose
::
Monad
m
=>
ClientOpts
->
m
()
->
m
()
whenVerbose
opts
act
=
when
(
unHelpful
$
verbose
opts
)
act
bin/gargantext-client/Main.hs
0 → 100644
View file @
0b9e0385
module
Main
where
import
Control.Monad
import
Network.HTTP.Client
import
Options.Generic
import
Servant.Client
import
Options
import
Script
(
script
)
main
::
IO
()
main
=
do
-- we parse CLI options
opts
@
(
ClientOpts
(
Helpful
uri
)
_
_
(
Helpful
verb
))
<-
getRecord
"Gargantext client"
mgr
<-
newManager
defaultManagerSettings
burl
<-
parseBaseUrl
uri
when
verb
$
do
putStrLn
$
"[Debug] user: "
++
maybe
"<none>"
show
(
unHelpful
$
user
opts
)
putStrLn
$
"[Debug] backend: "
++
show
burl
-- we run 'script' from the Script module, reporting potential errors
res
<-
runClientM
(
script
opts
)
(
mkClientEnv
mgr
burl
)
case
res
of
Left
err
->
putStrLn
$
"[Client error] "
++
show
err
Right
a
->
print
a
bin/gargantext-client/Options.hs
0 → 100644
View file @
0b9e0385
{-# LANGUAGE TypeOperators #-}
module
Options
where
import
Options.Generic
-- | Some general options to be specified on the command line.
data
ClientOpts
=
ClientOpts
{
url
::
String
<?>
"URL to gargantext backend"
,
user
::
Maybe
Text
<?>
"(optional) username for auth-restricted actions"
,
pass
::
Maybe
Text
<?>
"(optional) password for auth-restricted actions"
,
verbose
::
Bool
<?>
"Enable verbose output"
}
deriving
(
Generic
,
Show
)
instance
ParseRecord
ClientOpts
bin/gargantext-client/Script.hs
0 → 100644
View file @
0b9e0385
module
Script
(
script
)
where
import
Control.Monad.IO.Class
import
Gargantext.API.Client
import
Servant.Client
import
Auth
import
Core
import
Options
import
Tracking
-- | An example script. Tweak, rebuild and re-run the executable to see the
-- effect of your changes. You can hit any gargantext endpoint in the body
-- of 'script' using the many (many!) client functions exposed by the
-- 'Gargantext.API.Client' module.
--
-- Don't forget to pass @--user@ and @--pass@ if you're using 'withAuthToken'.
script
::
ClientOpts
->
ClientM
()
script
opts
=
do
-- we start by asking the backend for its version
ver
<-
getBackendVersion
liftIO
.
putStrLn
$
"Backend version: "
++
show
ver
-- next we authenticate using the credentials given on the command line
-- (through --user and --pass), erroring out loudly if the auth creds don't
-- go through, running the continuation otherwise.
withAuthToken
opts
$
\
tok
userNode
->
do
liftIO
.
putStrLn
$
"user node: "
++
show
userNode
steps
<-
-- we run a few client computations while tracking some EKG metrics
-- (any RTS stats or routing-related data), which means that we sample the
-- metrics at the beginning, the end, and in between each pair of steps.
tracking
opts
[
"rts.gc.bytes_allocated"
]
[
(
"get roots"
,
do
roots
<-
getRoots
tok
liftIO
.
putStrLn
$
"roots: "
++
show
roots
)
,
(
"get user node detail"
,
do
userNodeDetail
<-
getNode
tok
userNode
liftIO
.
putStrLn
$
"user node details: "
++
show
userNodeDetail
)
]
-- we pretty print the values we sampled for all metrics and the
-- results of all the steps
whenVerbose
opts
(
ppTracked
steps
)
bin/gargantext-client/Tracking.hs
0 → 100644
View file @
0b9e0385
{-# LANGUAGE TupleSections #-}
module
Tracking
(
tracking
,
ppTracked
,
EkgMetric
,
Step
)
where
import
Core
import
Options
import
Control.Monad.IO.Class
import
Data.List
(
intersperse
)
import
Data.Text
(
Text
)
import
Servant.Client
import
System.Metrics.Json
(
Value
)
import
Gargantext.API.Client
import
qualified
Data.Text
as
T
-- | e.g @["rts", "gc", "bytes_allocated"]@
type
EkgMetric
=
[
Text
]
-- | Any textual description of a step
type
Step
=
Text
-- | Track EKG metrics before/after running a bunch of computations
-- that can talk to the backend.
tracking
::
ClientOpts
->
[
Text
]
-- ^ e.g @["rts.gc.bytes_allocated"]@
->
[(
Step
,
ClientM
a
)]
->
ClientM
[
Either
[(
EkgMetric
,
Value
)]
(
Step
,
a
)]
-- no steps, nothing to do
tracking
_
_
[]
=
return
[]
-- no metrics to track, we just run the steps
tracking
_
[]
steps
=
traverse
runStep
steps
-- metrics to track: we intersperse metric fetching and steps,
-- starting and ending with metric fetching
tracking
opts
ms'
steps
=
mix
(
Left
<$>
fetchMetrics
)
(
map
runStep
steps
)
where
fetchMetrics
::
ClientM
[(
EkgMetric
,
Value
)]
fetchMetrics
=
flip
traverse
ms
$
\
metric
->
do
whenVerbose
opts
$
liftIO
.
putStrLn
$
"[Debug] metric to track: "
++
T
.
unpack
(
T
.
intercalate
"."
metric
)
dat
<-
(
metric
,)
<$>
getMetricSample
metric
whenVerbose
opts
$
liftIO
.
putStrLn
$
"[Debug] metric pulled: "
++
show
dat
return
dat
mix
::
ClientM
a
->
[
ClientM
a
]
->
ClientM
[
a
]
mix
x
xs
=
sequence
$
[
x
]
++
intersperse
x
xs
++
[
x
]
ms
=
map
(
T
.
splitOn
"."
)
ms'
-- ^ A trivial function to print results of steps and sampled metrics
ppTracked
::
Show
a
=>
[
Either
[(
EkgMetric
,
Value
)]
(
Step
,
a
)]
->
ClientM
()
ppTracked
[]
=
return
()
ppTracked
(
Right
(
step
,
a
)
:
rest
)
=
do
liftIO
.
putStrLn
$
"[step: "
++
T
.
unpack
step
++
"] returned: "
++
show
a
ppTracked
rest
ppTracked
(
Left
ms
:
rest
)
=
do
liftIO
.
putStrLn
$
unlines
[
T
.
unpack
(
T
.
intercalate
"."
metric
)
++
" = "
++
show
val
|
(
metric
,
val
)
<-
ms
]
ppTracked
rest
runStep
::
(
Step
,
ClientM
a
)
->
ClientM
(
Either
e
(
Step
,
a
))
runStep
(
step
,
act
)
=
Right
.
(
step
,)
<$>
act
cabal.project
View file @
0b9e0385
packages: .
-- ../servant-job
-- ../ekg-json
-- ../../../code/servant/servant
-- ../../../code/servant/servant-server
-- ../../../code/servant/servant-client-core
-- ../../../code/servant/servant-client
-- ../../../code/servant/servant-auth/servant-auth
-- ../../../code/servant/servant-auth/servant-auth-client
-- ../../../code/servant/servant-auth/servant-auth-server
allow-newer: base, accelerate, servant, time
allow-newer: base, accelerate, servant, time, classy-prelude
-- Patches
source-repository-package
...
...
@@ -20,7 +11,7 @@ source-repository-package
source-repository-package
type: git
location: https://github.com/alpmestan/ekg-json.git
tag:
c7bde4851a7cd41b3f3debf0c57f11bbcb11d698
tag:
fd7e5d7325939103cd87d0dc592faf644160341c
source-repository-package
type: git
...
...
@@ -53,7 +44,7 @@ source-repository-package
source-repository-package
type: git
location: https://gitlab.iscpif.fr/gargantext/crawlers/pubmed.git
tag:
a9d8e08a7ef82f90e29dfaced4071704a316339
4
tag:
9cdba6423decad5acfacb0f274212fd8723ce73
4
source-repository-package
type: git
...
...
@@ -112,7 +103,7 @@ source-repository-package
source-repository-package
type: git
location: https://github.com/delanoe/haskell-opaleye.git
tag:
d3ab7acd5ede737478763630035aa880f7e34444
tag:
756cb90f4ce725463d957bc899d764e0ed73738c
source-repository-package
type: git
...
...
@@ -146,4 +137,5 @@ source-repository-package
constraints: unordered-containers==0.2.14.*,
servant-ekg==0.3.1,
time==1.9.3
time==1.9.3,
stm==2.5.0.1
src/Gargantext/API/Client.hs
View file @
0b9e0385
{-# OPTIONS_GHC -freduction-depth=0 #-}
{-# OPTIONS_GHC -O0 #-}
{-# OPTIONS_GHC -O0 #-}
module
Gargantext.API.Client
where
...
...
@@ -55,17 +55,18 @@ import Servant.Job.Core
import
Servant.Job.Types
import
System.Metrics.Json
(
Sample
,
Value
)
-- * actual client functions for individual endpoints
-- * version API
getBackendVersion
::
ClientM
Text
-- * auth API
postAuth
::
AuthRequest
->
ClientM
AuthResponse
-- * admin api
getRoots
::
Token
->
ClientM
[
Node
HyperdataUser
]
putRoots
::
Token
->
ClientM
Int
-- not actually implemented in the backend
deleteNodes
::
Token
->
[
NodeId
]
->
ClientM
Int
--
*
node api
getNode
::
Token
->
NodeId
->
ClientM
(
Node
HyperdataAny
)
getContext
::
Token
->
ContextId
->
ClientM
(
Node
HyperdataAny
)
renameNode
::
Token
->
NodeId
->
RenameNode
->
ClientM
[
Int
]
...
...
@@ -155,7 +156,7 @@ killNodeDocumentUploadAsyncJob :: Token -> NodeId -> JobID 'Unsafe -> Maybe Limi
pollNodeDocumentUploadAsyncJob
::
Token
->
NodeId
->
JobID
'U
n
safe
->
Maybe
Limit
->
Maybe
Offset
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
waitNodeDocumentUploadAsyncJob
::
Token
->
NodeId
->
JobID
'U
n
safe
->
ClientM
(
JobOutput
JobLog
)
-- corpus api
--
*
corpus api
getCorpus
::
Token
->
CorpusId
->
ClientM
(
Node
HyperdataCorpus
)
renameCorpus
::
Token
->
CorpusId
->
RenameNode
->
ClientM
[
Int
]
postCorpus
::
Token
->
CorpusId
->
PostNode
->
ClientM
[
CorpusId
]
...
...
@@ -244,13 +245,13 @@ killCorpusDocumentUploadAsyncJob :: Token -> CorpusId -> JobID 'Unsafe -> Maybe
pollCorpusDocumentUploadAsyncJob
::
Token
->
CorpusId
->
JobID
'U
n
safe
->
Maybe
Limit
->
Maybe
Offset
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
waitCorpusDocumentUploadAsyncJob
::
Token
->
CorpusId
->
JobID
'U
n
safe
->
ClientM
(
JobOutput
JobLog
)
-- corpus node/node API
--
*
corpus node/node API
getCorpusNodeNode
::
Token
->
NodeId
->
NodeId
->
ClientM
(
Node
HyperdataAny
)
-- corpus export API
--
*
corpus export API
getCorpusExport
::
Token
->
CorpusId
->
Maybe
ListId
->
Maybe
NgramsType
->
ClientM
Corpus
-- * annuaire api
getAnnuaire
::
Token
->
AnnuaireId
->
ClientM
(
Node
HyperdataAnnuaire
)
renameAnnuaire
::
Token
->
AnnuaireId
->
RenameNode
->
ClientM
[
Int
]
postAnnuaire
::
Token
->
AnnuaireId
->
PostNode
->
ClientM
[
AnnuaireId
]
...
...
@@ -338,17 +339,17 @@ killAnnuaireDocumentUploadAsyncJob :: Token -> AnnuaireId -> JobID 'Unsafe -> Ma
pollAnnuaireDocumentUploadAsyncJob
::
Token
->
AnnuaireId
->
JobID
'U
n
safe
->
Maybe
Limit
->
Maybe
Offset
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
waitAnnuaireDocumentUploadAsyncJob
::
Token
->
AnnuaireId
->
JobID
'U
n
safe
->
ClientM
(
JobOutput
JobLog
)
-- contact api
--
*
contact api
postAnnuaireContactAsync
::
Token
->
AnnuaireId
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
postAnnuaireContactAsyncJob
::
Token
->
AnnuaireId
->
JobInput
Maybe
AddContactParams
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
killAnnuaireContactAsyncJob
::
Token
->
AnnuaireId
->
JobID
'U
n
safe
->
Maybe
Limit
->
Maybe
Offset
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
pollAnnuaireContactAsyncJob
::
Token
->
AnnuaireId
->
JobID
'U
n
safe
->
Maybe
Limit
->
Maybe
Offset
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
waitAnnuaireContactAsyncJob
::
Token
->
AnnuaireId
->
JobID
'U
n
safe
->
ClientM
(
JobOutput
JobLog
)
-- contact node/node API
--
*
contact node/node API
getAnnuaireContactNodeNode
::
Token
->
NodeId
->
NodeId
->
ClientM
(
Node
HyperdataContact
)
-- document ngrams api
--
*
document ngrams api
getDocumentNgramsTable
::
Token
->
DocId
->
TabType
->
ListId
->
Int
->
Maybe
Int
->
Maybe
ListType
->
Maybe
MinSize
->
Maybe
MaxSize
->
Maybe
Ngrams
.
OrderBy
->
Maybe
Text
->
ClientM
(
VersionedWithCount
NgramsTable
)
putDocumentNgramsTable
::
Token
->
DocId
->
TabType
->
ListId
->
Versioned
NgramsTablePatch
->
ClientM
(
Versioned
NgramsTablePatch
)
postRecomputeDocumentNgramsTableScore
::
Token
->
DocId
->
TabType
->
ListId
->
ClientM
Int
...
...
@@ -359,15 +360,14 @@ killDocumentNgramsTableAsyncJob :: Token -> DocId -> JobID 'Unsafe -> Maybe Limi
pollDocumentNgramsTableAsyncJob
::
Token
->
DocId
->
JobID
'U
n
safe
->
Maybe
Limit
->
Maybe
Offset
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
waitDocumentNgramsTableAsyncJob
::
Token
->
DocId
->
JobID
'U
n
safe
->
ClientM
(
JobOutput
JobLog
)
-- document export API
--
*
document export API
getDocumentExportJSON
::
Token
->
DocId
->
ClientM
DocumentExport
.
DocumentExport
getDocumentExportCSV
::
Token
->
DocId
->
ClientM
Text
--getDocumentExportCSV :: Token -> DocId -> ClientM [DocumentExport.Document]
-- count api
--
*
count api
postCountQuery
::
Token
->
Query
->
ClientM
Counts
-- graph api
--
*
graph api
getGraphHyperdata
::
Token
->
NodeId
->
ClientM
HyperdataGraphAPI
postGraphAsync
::
Token
->
NodeId
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
postGraphAsyncJob
::
Token
->
NodeId
->
JobInput
Maybe
()
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
...
...
@@ -382,6 +382,7 @@ postGraphRecomputeVersion :: Token -> NodeId -> ClientM Graph
getTree
::
Token
->
NodeId
->
[
NodeType
]
->
ClientM
(
Tree
NodeTree
)
getTreeFirstLevel
::
Token
->
NodeId
->
[
NodeType
]
->
ClientM
(
Tree
NodeTree
)
-- * new corpus API
postNewCorpusWithFormAsync
::
Token
->
NodeId
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
postNewCorpusWithFormAsyncJob
::
Token
->
NodeId
->
JobInput
Maybe
NewWithForm
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
killNewCorpusWithFormAsyncJob
::
Token
->
NodeId
->
JobID
'U
n
safe
->
Maybe
Limit
->
Maybe
Offset
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
...
...
@@ -394,6 +395,7 @@ killNewCorpusWithQueryAsyncJob :: Token -> NodeId -> JobID 'Unsafe -> Maybe Limi
pollNewCorpusWithQueryAsyncJob
::
Token
->
NodeId
->
JobID
'U
n
safe
->
Maybe
Limit
->
Maybe
Offset
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
waitNewCorpusWithQueryAsyncJob
::
Token
->
NodeId
->
JobID
'U
n
safe
->
ClientM
(
JobOutput
JobLog
)
-- * list API
getList
::
Token
->
NodeId
->
ClientM
(
Headers
'[
H
eader
"Content-Disposition"
Text
]
(
Map
NgramsType
(
Versioned
NgramsTableMap
)))
postListJsonUpdateAsync
::
Token
->
NodeId
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
postListJsonUpdateAsyncJob
::
Token
->
NodeId
->
JobInput
Maybe
WithFile
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
...
...
@@ -407,11 +409,14 @@ killListCsvUpdateAsyncJob :: Token -> NodeId -> JobID 'Unsafe -> Maybe Limit ->
pollListCsvUpdateAsyncJob
::
Token
->
NodeId
->
JobID
'U
n
safe
->
Maybe
Limit
->
Maybe
Offset
->
ClientM
(
JobStatus
'S
a
fe
JobLog
)
waitListCsvUpdateAsyncJob
::
Token
->
NodeId
->
JobID
'U
n
safe
->
ClientM
(
JobOutput
JobLog
)
-- * public API
getPublicData
::
ClientM
[
PublicData
]
getPublicNodeFile
::
NodeId
->
ClientM
(
Headers
'[
H
eader
"Content-Type"
Text
]
BSResponse
)
-- ekg api
-- * ekg api
-- | get a sample of all metrics
getMetricsSample
::
ClientM
Sample
-- | open @<backend:port\/ekg\/index.html@ to see a list of metrics
getMetricSample
::
[
Text
]
->
ClientM
Value
-- * unpacking of client functions to derive all the individual clients
...
...
stack.yaml
View file @
0b9e0385
...
...
@@ -57,7 +57,7 @@ extra-deps:
-
git
:
https://github.com/delanoe/servant-static-th.git
commit
:
8cb8aaf2962ad44d319fcea48442e4397b3c49e8
-
git
:
https://github.com/alpmestan/ekg-json.git
commit
:
c7bde4851a7cd41b3f3debf0c57f11bbcb11d698
commit
:
fd7e5d7325939103cd87d0dc592faf644160341c
# Databases libs
-
git
:
https://github.com/delanoe/haskell-opaleye.git
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment