XWiki GraphQL API
GraphQL API to expose the XWiki model for usecases where REST is too verbose or produces too much back and forward between the client and the server. |
Type | JAR |
Category | |
Developed by | |
Active Installs | 1 |
Rating | |
License | GNU Lesser General Public License 2.1 |
Compatibility | XWiki 12.6+ |
Table of contents
Description
Inspiration: https://github.com/phillip-kruger/graphql-example
It follows the Eclipse MicroProfile GraphQL API Specifications using the SmallRye GraphQL Implementation. It uses the code-first approach, rather than the schema-first traditional approach, and extracts the schema from the annotated model classes and methods defined in the code.
The API is accessible through the /graphql endpoint, implemented using the XWiki Reference API
The generated schema can be obtained by getting the following URL:
A very basic query (in this case, the title and content for the Main.WebHome document) can be done by using curl:
One recommended client for experimenting with the API inside your browser would be the Altair Firefox add-on.
Authentication
The standard XWiki authentication methods are supported, similar to the REST API:
- HTTP Basic Authentication
- XWiki Session (browser session)
Model
For the moment, the XWiki model is based on the com.xpn.xwiki.api.* classes and a jandex index is provided for these as part of the api jar.
For the future, it may be a good idea to explicitly define an XWiki model (or a supported subset of it) and not depend on the com.xpn.xwiki.api.* classes since they bring a lot of unnecessary noise.
Queries
Examples of currently supported basic CRUD queries on the XWiki model:
- Get (only) the specified document fields, objects and attachments:{
document(documentReference: "Sandbox.WebHome") {
title
author
creationDate
contentUpdateDate
syntax
content
objects {
reference
}
attachmentList {
filename
date
}
}
}- Output:{
"data": {
"document": {
"title": "Sandbox",
"author": "XWiki.superadmin",
"creationDate": "2020-07-31T17:12:50",
"contentUpdateDate": "2020-07-31T17:12:50",
"syntax": "XWiki 2.1",
"content": "The sandbox is a part of your wiki that you can freely modify. It's meant to let you practice editing. You will ...",
"objects": [],
"attachmentList": [
{
"filename": "XWikiLogo.png",
"date": "2020-07-31T17:12:50"
}
]
}
}
}
- Output:
- Get all documents, but only the specified fields:{
documents {
documentReference
pageReference
title
author
}
}- Output:{
"data": {
"documents": [
{
"documentReference": "xwiki:WikiManager.Translations",
"pageReference": "xwiki:WikiManager/Translations",
"title": "Translations",
"author": "XWiki.superadmin"
},
{
"documentReference": "xwiki:XWiki.DocumentTreeTranslations",
"pageReference": "xwiki:XWiki/DocumentTreeTranslations",
"title": "Document Tree Translations",
"author": "XWiki.superadmin"
},
{
"documentReference": "xwiki:XWiki.Notifications.Code.EmailJobClass",
"pageReference": "xwiki:XWiki/Notifications/Code/EmailJobClass",
"title": "EmailJobClass",
"author": "XWiki.superadmin"
},
{
"documentReference": "xwiki:AppWithinMinutes.TextArea",
"pageReference": "xwiki:AppWithinMinutes/TextArea",
"title": "$services.localization.render('platform.appwithinminutes.classEditorTextAreaFieldName')",
"author": "XWiki.superadmin"
},
...
- Output:
- Mixing both individual and all documents requests within the same query:{
document(documentReference: "Main.WebHome") {
title
author
creationDate
contentUpdateDate
syntax
content
}
documents {
title
}
}- Output:{
"data": {
"document": {
"title": "Home",
"author": "XWiki.superadmin",
"creationDate": "2020-07-31T17:13:06",
"contentUpdateDate": "2020-07-31T17:13:06",
"syntax": "XWiki 2.1",
"content": "{{box cssClass=\"floatinginfobox\"}}\n{{velocity}}\n{{html clean=\"false\"}}\n## ..."
},
"documents": [
{
"title": "Translations"
},
{
"title": "Document Tree Translations"
},
{
"title": "EmailJobClass"
},
...
- Output:
- Create or update a document:mutation {
createOrUpdateDocument(
documentReference: "Sandbox.NewDocument"
title: "Document Title"
content: "Hello2!"
) {
documentReference
}
}- Output:{
"data": {
"createOrUpdateDocument": {
"documentReference": "xwiki:Sandbox.NewDocument"
}
}
}
- Output:
- Delete a document:mutation {
deleteDocument(documentReference: "Sandbox.NewDocument") {
documentReference
}
}- Ouput:{
"data": {
"deleteDocument": {
"documentReference": "xwiki:Sandbox.NewDocument"
}
}
}
- Ouput:
More details on the current API and the supported queries and mutations can be found either in the above mentioned generated schema, or in the code.
Extending the model
The API can be easily extended by any XWiki extension that comes with a pre-built jandex index containing the extra model classes together with an entry point annotated with the @GraphQLApi (org.eclipse.microprofile.graphql.GraphQLApi) java annotation. Such an index can be precomputed at build time using the jandex maven plugin.
To better demonstrate how this can be done, a sample project has been provided that contains a sample POJO API supporting basic CRUD operations on a Basket class containing a list of Fruit subclasses.
The entry point of the sample project is the MyFruitBasketGraphQlApi class that is annotated with @GraphQLApi and that exposes one @Query method to get the basket of fruits and two @Mutation methods to add or remove fruits to/from the basket.
The sample project's pom.xml depends on the api-graphql module and indexes the sample project's classes at build time with the jandex-maven-plugin.
Building this sample extension and installing it at the farm level (similarly to the api-graphql extension) will allow you to perform queries such as:
- Getting the fruits from the basket:{
basket {
fruits {
name
color
}
}
}- Output:{
"data": {
"basket": {
"fruits": [
{
"name": "apple",
"color": "red"
},
{
"name": "apple",
"color": "red"
},
{
"name": "orange",
"color": "orange"
},
{
"name": "banana",
"color": "yellow"
}
]
}
}
}
- Output:
- Adding a fruit to the basket:mutation{
addFruit(type: "Orange") {
color
name
}
}- Output:{
"data": {
"addFruit": {
"color": "orange",
"name": "orange"
}
}
}
- Output:
- Removing a fruit from the basket:mutation{
removeFruit(index: 0){
name
color
}
}- Output:{
"data": {
"removeFruit": {
"name": "apple",
"color": "red"
}
}
}
- Output:
Current Limitations and TODO
- XWikiDocument contains 2 getters returning void: XWikiDocument.getUniqueLinkedEntityReferences (2 signatures), causing issues when attempted to be queried
- We can not use @Inject-ed XWiki components inside GraphqlApi (or any other @GraphQLApi annotated entry-point class) since its lifecycle is managed by the graphql library, which is not aware of XWiki components. The only solution is to use Utils.getComponent.
- At each request, the schema is reloaded and re-initialized. The advantage is that any installed extension will be easily picked up and the model will be extended. The disadvantage is the performance impact caused by not reusing an already initialized schema and only reloading it when changes are done (i.e. extension installed/uninstalled).
Prerequisites & Installation Instructions
Before XWiki 12.7 (not needed for 12.7+):
- Delete the existing antlr4-runtime-4.5.1-1.jar core extension (located in the webapps/xwiki/WEB-INF/libs folder) and replace it with a version greater than or equal to 4.7.2 (as required by the graphql-java dependency).
- Restart XWiki and install the GraphQL API extension.
Release Notes
v0.2
v0.1
First version.
- Hardcoded model
- No user authentication (always using "XWiki.Admin")
Dependencies
Dependencies for this extension (org.xwiki.contrib:api-graphql 0.2):
- org.xwiki.commons:xwiki-commons-component-default 12.4
- org.xwiki.platform:xwiki-platform-oldcore 12.4
- org.xwiki.platform:xwiki-platform-query-manager 12.4
- io.smallrye:smallrye-graphql 1.0.7
- io.smallrye:smallrye-graphql-schema-builder 1.0.7
- org.xwiki.platform:xwiki-platform-container-servlet 12.4
- jakarta.json:jakarta.json-api 1.1.6
- org.glassfish:jakarta.json 1.1.6
- jakarta.json.bind:jakarta.json.bind-api 1.0.2
- org.eclipse:yasson 1.0.7