Description
A Mesh-Plugin to export any Mesh content dynamically into a CSV File.
Building
Build the Plugin with maven:
mvn clean package
After building, take the build Plugin mesh-export-plugin-{VERSION}.jar from the target folder.
Note: If there comes a error while building with maven, try building it without running the Tests (as there currently are none):
-Dmaven.test.skip=true/-DskipTests$ mvn -Dmaven.test.skip=true clean package # Or $ mvn -DskipTests clean package
Installation
Simply install this plugin like any other Mesh-Plugin by putting it into the /plugins Folder.
You can then manually deploy it when the server is still running or restart the server to load it on startup.
Configuration
In the configuration you may configure webroot-paths to fetch configuration-nodes; cache-times for config-values provided by MappingProviders; and the role-name to access the export endpoint.
| Setting | Type | Default Value | Description |
|---|---|---|---|
|
|
null |
Webroot Paths to the configuration nodes. |
|
|
|
How long the configuration nodes are being cached in milliseconds. |
|
|
|
If it should check and create the role (if needed) on startup. |
|
|
|
The Role name to control who has access to the export. |
WebrootPathConfiguration
A single Webroot path can be configured to be used in multiple projects. If you leave the projects empty, it’ll use it on all projects.
| Setting | Type | Required | Description |
|---|---|---|---|
|
|
Yes |
The webroot-path to the configuration-node. Slashes in the beginning and end are stripped. |
|
|
No |
A list of project-names for which this path is valid for. If none is specified, it’s available for all projects. |
Configuraion Values
Configuration Values are used for CONFIG_VALUE mappings to map the value to a proper output value.
This is usually used for Options, i.e: * "0" → "Unknown" * "1" → "Male" * "2" → "Female" * "3" → "Different"
Configuration Nodes
Currently the only out of the box supported possibility is to create "Configuration Nodes" which are loaded with the ["WebrootPathConfiguration"](#webrootpathconfiguration) options.
The format of the configuration-nodes should to look like this:
Configuration Schema:
{
"name": "example_configuration",
"segmentField": "slug",
"fields": [
{
"name": "slug",
"type": "string"
},
{
"name": "exampleOption",
"type": "list",
"listType": "micronode",
"allow": [
"example_option"
]
}
]
}
NOTE: You do not have to name your segmentField
slug, it’s just a convention. You may also add more unrelated fields, as all fields which are not a micro-node list and do not have aidandvaluestring field will be ignored.
Option Micro-Schema:
{
"name": "example_option",
"fields": [
{
"name": "id",
"type": "string"
},
{
"name": "value",
"type": "string"
}
]
}
Configuration Node:
{
"language": "en",
"schema": {
"name": "example_configuration"
},
"parentNode": {
"uuid": "00112233445566778899aabbccddeeff"
},
"fields": {
"slug": "config",
"exampleOption": [
{
"microschema": { "name": { "example_option" } },
"fields": { "id": "1", "value": "Example Option Value 001" }
},
{
"microschema": { "name": { "example_option" } },
"fields": { "id": "2", "value": "Another Example Option Value 002" }
},
{
"microschema": { "name": { "example_option" } },
"fields": { "id": "3", "value": "Some other Option Value 003" }
}
]
}
}
Plugin Configuration:
webrootPaths:
- path: "/config"
Example Mapping:
{
"path": "fields.name",
"label": "Name",
"type": "STRING"
},
{
"path": "fields.someOptionField",
"label": "Value Label",
"type": "CONFIG_VALUE",
"configKey": "exampleOption"
}
Example Node:
{
// ...
"fields": {
"name": "Example Node 01",
"someOptionField": "1"
}
// ...
}
Output:
"Name";"Value Label"
"Example Node 01";"Example Option Value 001"
Helper-Plugins
When regular Nodes via Webroot aren’t your cup of tea, then you can create a Helper-Plugin which gets you your Config-Values with custom logic.
Setup
First, you need to add a dependency to the export-plugin extension for the interfaces to implement:
<dependency>
<groupId>com.gentics.mesh.plugin.commercial</groupId>
<artifactId>mesh-export-plugin-extension-api</artifactId>
<version>YOUR_VERSION_HERE</version>
<scope>provided</scope>
</dependency>
IMPORTANT: The scope has to be set to
provided, otherwise your Plugin will not be detected by the Export-Plugin and won’t get used!
Additionally, you should add the Export-Plugin as dependency to your Plugin, or it might load in the
wrong order which will break your Plugin.
To do so, simply add a new Plugin-Dependencies entry to your Plugin-Manifest like so:
Plugin-Dependencies: export
See https://pf4j.org/doc/plugins.html#notes-about-plugin-dependencies for more information.
Implementation
Implementation is quite simple: Implement the [ExportConfigValuePlugin](extension-api/src/main/java/com/gentics/mesh/plugin/export/extension/ExportConfigValuePlugin.java) interface into your Mesh-Plugin.
Then create a [ConfigValueProvider](extension-api/src/main/java/com/gentics/mesh/plugin/export/extension/ConfigValueProvider.java) and pass it out in your Plugin.
That’s it! Now you get a call whenever config-values should be loaded again. Caching is already handled beforehand, so you don’t have to worry about it.
For examples, you can just take a look at the [ExportPlugin](plugin/src/main/java/com/gentics/mesh/plugin/export/ExportPlugin.java) and the [WebrootConfigValueProvider](plugin/src/main/java/com/gentics/mesh/plugin/export/WebrootConfigValueProvider.java) implementation.
Request
To export the relevant data to a CSV File, we use a Subset of a regular GraphQL request. The endpoint registers to each Project and can be called the following way:
POST /api/v2/<project>/plugins/export/csv
The body has following format:
| Property | Type | Required | Description |
|---|---|---|---|
|
String |
Required |
The GraphQL Query to execute to get elements to export. |
|
[ |
Required |
Variables for the GraphQL Query. |
|
[ |
Required |
The mapping definitions to map the data to CSV colums. |
|
Integer |
Optional |
Limits the amount of Nodes (NOT Rows!) to export. |
|
String |
Optional |
Path to where the root of the elements to export start. Defaults to |
|
String |
Optional |
Path to the "hasNextPage" flag to determine if more Pages exist. Defauls to |
|
[ |
Optional |
Filter which filters out elements. |
|
[ |
Optional |
Mapping for boolean values. |
|
String |
Optional |
Date-Format to use for dates. |
|
String |
Optional |
The Locale in which the date should be formatted. |
|
String |
Optional |
The zone-name in which the date should be formatted. |
|
String |
Optional |
The Locale in which the number should be formatted. |
|
|
Optional |
The number style. Defaults to |
|
String |
Optional |
Currency to display when the |
|
Number |
Optional |
Minimum amount of fraction-digits to render. Defaults to |
|
Number |
Optional |
Maximum amount of fraction-digits to render. Defaults to |
Query
In query, you define the GraphQL Query to use when loading the nodes.
The resulting data can be nested to any depth, but it should only be loaded what is necessary.
On default, a GraphQL Query like this is expected:
query ($query: String, $page: Long, $perPage: Long) {
nodes(query: $query, page: $page, perPage: $perPage) {
hasNextPage
elements {
uuid
}
}
}
Variables
| Property | Type | Required | Description |
|---|---|---|---|
|
String |
Required |
The Elasticsearch-Query to filter the nodes you want to export. |
|
Integer |
Optional |
Useful if you need to keep the memory footprint low. |
|
Integer |
Optional |
Can be used to skip first n pages. This is discouraged however. |
Mapping
A mapping represents a Single Column, or a Multi Column (Depending on the Type) in the CSV File. The only important difference between these, is how expended multi-values are being output.
Following properties are available for mappings:
| Name | Type | Mapping-Types | Required |
|---|---|---|---|
Description |
|
String |
All |
Required |
The Path to the property that should be rendered. Ignores any arrays and simply appends all values together. |
|
[Type](#types) |
All |
Required |
The Type of the property that should be rendered. See next section for further info. |
|
String |
All |
Required for single-column |
The label for the Column. |
|
[Filter](#filtering) |
All |
Optional |
A filter object to filter the entire (multi) column. See #Filtering for further info. |
|
[Mapping](#mapping)[] |
|
Required |
An addtional Array of Mappings for the sub-elements, relative to each element. |
|
[Filter](#filtering) |
|
Optional |
A Filter to filter out individual sub-elements. |
|
Any |
All |
Optional |
The value it should use when the value could not be found or when it’s null. |
|
[ |
|
Optional |
Mapping for boolean values. |
|
String |
|
Reqiured |
The key from the combined config to load the mapping from. |
|
String |
|
Optional |
Custom Date ( |
|
String |
|
Optional |
The Locale in which the date should be formatted. |
|
String |
|
Optional |
The zone-name in which the date should be formatted. |
|
[Type](#types) |
|
Required |
The type of element that is in the list. |
|
Boolean |
|
Optional |
If it should create a new Row for each value instead of joining all values together. |
|
Integer |
|
Optional |
The limit of how many elements should be expanded into their own Row. |
|
String |
|
Optional |
The Locale in which the number should be formatted. |
|
|
|
Optional |
The number style. Defaults to |
|
String |
|
Optional |
Currency to display when the |
|
Number |
|
Optional |
Minimum amount of fraction-digits to render. Defaults to |
|
Number |
|
Optional |
Maximum amount of fraction-digits to render. Defaults to |
|
String |
|
Required |
The GraphQL query for loading the foreign Node References. |
|
String |
|
Required |
The ElasticSearch query for filtering the foreign Node References. It replaces |
|
Integer |
|
Optional |
Maximal amount of Nodes that should be loaded/output. |
|
String |
|
Required |
The definition of the template. Requires to contain the replace token "%value%". |
|
String |
|
Required |
The regexp definition to use to capture the data of the value. |
|
Integer |
|
Optional |
Flags to pass along with the definition. Defaults to |
|
String |
|
Required |
The template which renders the output value. Groups from the Match may be replaced with |
Note: When the Type is not
LIST, all values but one gathered from thepathare getting dropped.
Types
| Name | Type | Description | STRING |
|---|---|---|---|
Single |
Renders the string value without additional formatting. |
|
Single |
Renders the number in a Excel friendly format. |
|
Single |
Renders the boolean as |
|
Single |
Renders the date with the provided format. Defaults to a Excel friendly Date format. |
|
Single |
Renders the value with the template-definition by replacing the token |
|
Single |
Renders the template by replacing the |
|
Single |
Attempts to map the value to the loaded config-values and outputs the mapped value. |
|
Meta |
Meta Type to render multiple values. Must be used in combination of another Type. |
|
Multi |
Used for micronodes and directly referenced nodes to group the values together. |
`FOREIGN_REFERENCE ` |
Multi |
Multi-Column
With Multi-Column mappings, you need to specify a subMapping, which is relative to the current object.
The label in this Mapping is getting ignored and is not required.
When the elements are getting expended to multiple rows with the combination of the LIST Type,
then each element stays inside of the boundry of the group-column and creates a proper matrix with
all variations in the resulting CSV file.
You can also filter each element of the result via the subFilter property.
The paths for filtering are then relative to the filtered object instead of the root-node.
To access the root-node again, you may use $ as first entry.
Example:
Node:
{
"uuid": "00112233445566778899aabbccddeeff",
"fields": {
"someOtherField": true,
"myMicroNode": [{
"fields": {
"nestedField": "value123"
}
}, {
"fields": {
"missingNestedField": "hello world"
}
}]
}
}
Mapping:
{
"type": "SUB_NODE",
"path": "fields.myMicroNode",
"label": "Sample",
"subFilter": {
"and": [
{
"equals": {
"path": "$.fields.someOtherField",
"value": true
},
{
"exists": "fields.nestedField"
}
}
]
},
"subMapping": [
{
"type": "STRING",
"path": "fields.nestedField",
"label": "My cool value"
}
]
}
In this example, only the first element would pass through.
Filtering
Filtering is done on a (Single or Multi-)Column basis and should only be used where regular ElasticSearch filtering does not work. It allows you to filter out elements which you might not want to output in the CSV File.
There’s currently only two filters which can be used together with grouping- and meta-filters. You may ever only specify one type of filter in a filter object (similar to an elasticsearch query-object).
Paths are specified by each segment separated by .-characters, just like in elasticsearch or in the mapping-path.
Additionally, when using filtering in the sub-filter context, you can start the path with $ to get back
to the root object.
Following Types are available:
| Name | Type | Value | Description |
|---|---|---|---|
|
Group |
Filter[] |
All provided filters must match. Must specify at least one filter. |
|
Group |
Filter[] |
One or more of provided filter must match. |
|
Meta |
Filter |
Inverts the result of the provided Filter. |
|
Meta |
String |
If the value on the specified path is |
|
Meta |
String |
If the value on the specified path is present. |
|
[Equals](#equals) |
Checks if the element-value and the filter-value are the same. |
|
|
[Range](#range) |
Checks if the element-value is in the range of the filter-range. |
|
|
[In](#in) |
Checks if the element-value and any of the filter-values are the same. |
Example:
{
"and": [
{
"not": {
"equals": {
"path": "fields.myField",
"value": null
}
}
},
{
"range": {
"path": "created",
"gte": "2020-01-01T00:00:00Z"
}
},
{
"or": [
{
"equals": {
"path": "fields.myField",
"value": 1
}
},
{
"in": {
"path": "fields.otherField",
"values": ["hello", "world"]
}
}
]
}
]
}
=== Equals
Checks if the element-value and the filter-value are the same.
| Property | Type | Required | Description |
|---|---|---|---|
|
String |
Required |
Path to the element-value. |
|
Anything |
Optional |
The filter-value to check against. Defaults to |
|
String |
Optional |
The path to the other value to compare against. May only be set if |
The Equality check is based on the java.util.Objects.equals implementation: Objects.equals(filterValue, elementValue).
Note: When no value from
pathcould be found, it’s defaulting tonull.
Range
Checks if the element-value is in the range of the filter-range.
| Property | Type | Required | Description |
|---|---|---|---|
|
String |
Required |
Path to the element-value. Must point to a Number or Date field. |
|
Number, Date |
Optional |
Value to check if it’s greater than to it. |
|
Number, Date |
Optional |
Value to check if it’s greater than or equal to it. |
|
Number, Date |
Optional |
Value to cehck if it’s lower than to it. |
|
Number, Date |
Optional |
Value to cehck if it’s lower than or equal to it. |
While the Fields gt, gte, lt, and lte are optional, one of these must be defined.
You may also not define gt and gte, or lt and lte together:
// Invalid
{
"range": {
"path": "fields.value",
"gt" 1,
"gte": 2
}
}
// Valid
{
"range": {
"path": "fields.value",
"gt": 1,
"lt" 10
}
}
Note: When no value from
pathcould be found, the Filter will fail.
In
Checks if any element-value equals one of the filter-values.
If the value on path is an array, it’ll check if there’s at least one element in the cross-section.
To fail on these arrays, use the strict option.
| Property | Type | Required | Description |
|---|---|---|---|
|
String |
Required |
Path to the element-value(s). |
|
Array |
Optional |
An array of any value which is compared against the element-value. |
|
String |
Optional |
Path to the values to check against. |
|
Boolean |
Optional |
If the filter should fail when the |
Misc Configurations
Formatters like for Dates (dateFormat, dateLocale, and dateZone) or Numbers (numberLocale, numberStyle, numberCurrency, numberFractionMinimum, and numberFractionMaximum) or inline mappings like booleanMapping are defaulted on default.
You can then override these defaults for the request by overriding them on the root of the request, or go even more detailed by providing it in the mapping option for a column.
Override Hierachy: * Default Value * Request Value * Mapping Value
Boolean Mapping
With this option you can configure the output of boolean values. The mapping is a simple object like this:
{
"true": "yes",
"false": "no",
"null": "unknown"
}
On default, the mapping is set to the following:
* true: "1"
* false and null: "0"
