Skip to content

Commit

Permalink
Merge pull request #33 from CoreMedia/develop
Browse files Browse the repository at this point in the history
Release: updates for 3.0.0
  • Loading branch information
mfaust authored Oct 9, 2018
2 parents 7843172 + 03d9379 commit 1d0931d
Show file tree
Hide file tree
Showing 252 changed files with 5,116 additions and 1,716 deletions.
16 changes: 3 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
![Status: Active](https://documentation.coremedia.com/badges/badge_status_active.png "Status: Active")
![For CoreMedia CMS](https://documentation.coremedia.com/badges/badge_coremedia_cms.png "For CoreMedia CMS")
![Tested: 9.1710.1](https://documentation.coremedia.com/badges/badge_tested_coremedia_9-1710-1.png "Tested: 9.1710.1")
![Tested: 9.1801.1](https://documentation.coremedia.com/badges/badge_tested_coremedia_9-1801-1.png "Tested: 9.1801.1")
![Tested: 9.1807.1](https://documentation.coremedia.com/badges/badge_tested_coremedia_9-1807-1.png "Tested: 9.1807.1")

![CoreMedia Labs Logo](https://documentation.coremedia.com/badges/banner_coremedia_labs_wide.png "CoreMedia Labs Logo Title Text")

Expand Down Expand Up @@ -35,17 +34,8 @@ The workspace is comprised of the following modules:
* **headless-schema-generator**: The Generator Application for creating a schema definition from the CoreMedia Doctype Model
* **headless-pd**: Parent module for different processing descriptions (schema/queries etc.)
* **headless-performance-test**: Simple local performance test using a list of URLs
* **test-data**: Test content for sample client applications. Must be manually imported into the CoreMedia repository.


## Configuration

### Tomcat

The default configuration runs the Spring Boot application and web application with Tomcat 7, which is the default for the used CMS release.

Tomcat 8 can be configured by changing the properties `tomcat.id` and `tomcat.version` in the main `pom.xml` and removing the dependency
on `tomcat-juli` in module `headless-server-app`.
* **test-data**: Test content for sample client applications. Must be manually imported into the CoreMedia repository.
* **extensions**: Examples for extending/customizing the server. See the modules' `README.md` files for more details.


## Building and running the server
Expand Down
6 changes: 6 additions & 0 deletions extensions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Extension Parent Module

This is the parent module for extensions and customizations to the headless server.
All extensions are included in the build process but by default not added to the built *Spring Boot Application*/*WAR File*.

To add an extension to the application enabled it's dependency in the `extensions/extensions-bom/pom.xml` file.
26 changes: 26 additions & 0 deletions extensions/enhanced-linkbuilder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Link Builder Example

This module demonstrates the implementation of a custom `LinkBuilder`. The given example generates *real* binary
links to the media REST API instead of the generic URIs generated by the default simple builder.

If no base URI is configured (see below) the base is determined by the current request taking into account
parameters like `X-Forwarded-Host`.

## Configuration

1. Enable the extension by commenting out the dependency in `/extensions/extensions-bom/pom.xml`.
2. Set the link builder as default by setting its bean name in the processing definitions `definition.yml` file.
```
description: "My Schema Description"
linkBuilders:
default: !LinkBuilder "extendedLinkBuilder"
```
3. Optionally set fixed base URIs in the site indicator configuration for preview and live environments.
```
caasClients
00000000-0000-0000-0000-000000000000
pd default
mediaBaseUri_preview https://my.preview_proxy.com/
mediaBaseUri_live https://my.cdn.com/
```
4. Rebuild the server.
31 changes: 31 additions & 0 deletions extensions/enhanced-linkbuilder/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>
<groupId>com.coremedia.labs</groupId>
<artifactId>extensions</artifactId>
<version>1-SNAPSHOT</version>
</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>enhanced-linkbuilder</artifactId>

<description>Custom Link Builder Example</description>

<dependencies>
<dependency>
<groupId>com.coremedia.labs</groupId>
<artifactId>headless-server-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.coremedia.caas.extension.link;

import com.coremedia.caas.link.LinkBuilder;
import com.coremedia.caas.server.CaasServiceConfig;
import com.coremedia.caas.server.link.SimpleLinkBuilder;
import com.coremedia.caas.server.service.media.MediaResourceModel;
import com.coremedia.caas.server.service.media.MediaResourceModelFactory;
import com.coremedia.caas.service.repository.RootContext;
import com.coremedia.caas.service.repository.content.BlobProxy;
import com.coremedia.caas.service.repository.content.ContentProxy;
import com.coremedia.cap.common.IdHelper;

import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponents;

import static com.coremedia.caas.extension.link.LinkBuilderSetup.REQUEST_MEDIA_URI_COMPONENTS;
import static com.coremedia.caas.server.link.SimpleLinkBuilder.EMPTY_LINK;

@Component("extendedLinkBuilder")
public class ExtendedLinkBuilder implements LinkBuilder<String> {

private SimpleLinkBuilder delegate;
private CaasServiceConfig serviceConfig;


public ExtendedLinkBuilder(SimpleLinkBuilder delegate, CaasServiceConfig serviceConfig) {
this.delegate = delegate;
this.serviceConfig = serviceConfig;
}


@Override
public String createLink(Object target, RootContext rootContext) {
if (target instanceof ContentProxy) {
ContentProxy content = (ContentProxy) target;
if (content.isSubtypeOf("CMImage")) {
return createBinaryLink(content, rootContext, "data");
}
else if (content.isSubtypeOf("CMPicture")) {
return createBinaryLink(content, rootContext, "data");
}
else if (content.isSubtypeOf("CMDownload")) {
return createBinaryLink(content, rootContext, "data");
}
else if (content.isSubtypeOf("CMVisual") || content.isSubtypeOf("CMAudio")) {
String link = createBinaryLink(content, rootContext, "data");
if (EMPTY_LINK.equals(link)) {
link = content.getString("dataUrl");
}
return link;
}
}
return delegate.createLink(target, rootContext);
}


private String createBinaryLink(ContentProxy content, RootContext rootContext, String propertyName) {
BlobProxy blob = content.getBlob(propertyName);
if (blob != null && !blob.isEmpty()) {
Integer mediaId = IdHelper.parseContentId(content.getId());
if (serviceConfig.isBinaryUriHashesEnabled()) {
// fetch hash from media model
MediaResourceModel model = rootContext.getModelFactory().createModel(MediaResourceModelFactory.MODEL_NAME, content, propertyName);
String mediaHash = model.getHash();
// use pre-instantiated URI component
UriComponents uriComponents = rootContext.getRequestContext().getProperty(REQUEST_MEDIA_URI_COMPONENTS, UriComponents.class);
return uriComponents.expand(mediaId, propertyName, mediaHash).encode().toUriString();
}
else {
// use pre-instantiated URI component
UriComponents uriComponents = rootContext.getRequestContext().getProperty(REQUEST_MEDIA_URI_COMPONENTS, UriComponents.class);
return uriComponents.expand(mediaId, propertyName).encode().toUriString();
}
}
return EMPTY_LINK;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.coremedia.caas.extension.link;

import com.coremedia.caas.config.ProcessingDefinition;
import com.coremedia.caas.query.QueryDefinition;
import com.coremedia.caas.server.CaasServiceConfig;
import com.coremedia.caas.server.controller.interceptor.QueryExecutionInterceptorAdapter;
import com.coremedia.caas.server.controller.media.MediaController;
import com.coremedia.caas.server.service.request.ClientIdentification;
import com.coremedia.caas.service.repository.RootContext;
import com.coremedia.caas.service.request.RequestContext;

import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.Map;

import static org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.on;

@Component
public class LinkBuilderSetup extends QueryExecutionInterceptorAdapter {

public static final String REQUEST_MEDIA_URI_COMPONENTS = "linkBuilder.mediaUriConponents";


private CaasServiceConfig serviceConfig;


public LinkBuilderSetup(CaasServiceConfig serviceConfig) {
this.serviceConfig = serviceConfig;
}


@Override
public boolean preQuery(String tenantId, String siteId, ClientIdentification clientIdentification, RootContext rootContext, ProcessingDefinition processingDefinition, QueryDefinition queryDefinition, Map<String, Object> queryArgs, ServletWebRequest request) {
RequestContext requestContext = rootContext.getRequestContext();
// build prototype URI from either a configured base URI or the current request
String baseUri = clientIdentification.getOption(requestContext.isPreview() ? "mediaBaseUri_preview" : "mediaBaseUri_live", String.class);
UriComponentsBuilder prototype = baseUri != null ? UriComponentsBuilder.fromUriString(baseUri) : ServletUriComponentsBuilder.fromContextPath(request.getRequest());
// create UriComponents with placeholders based on prototype and controller method
UriComponents uriComponents;
if (serviceConfig.isBinaryUriHashesEnabled()) {
uriComponents = MvcUriComponentsBuilder.fromMethodCall(
prototype,
on(MediaController.class).getMedia(
tenantId,
siteId,
"{mediaId}",
"{propertyName}",
"{mediaHash}",
null,
null,
null,
null))
.build();
}
else {
uriComponents = MvcUriComponentsBuilder.fromMethodCall(
prototype,
on(MediaController.class).getMedia(
tenantId,
siteId,
"{mediaId}",
"{propertyName}",
null,
null,
null,
null))
.build();
}
requestContext.setProperty(REQUEST_MEDIA_URI_COMPONENTS, uriComponents);
return true;
}
}
34 changes: 34 additions & 0 deletions extensions/extensions-bom/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.coremedia.labs</groupId>
<artifactId>extensions</artifactId>
<version>1-SNAPSHOT</version>
</parent>

<artifactId>extensions-bom</artifactId>
<packaging>pom</packaging>

<description>Headless Server Extension/Example Module Bill of Material</description>

<dependencies>
<!-- enabled extension dependencies go here -->
<!--dependency>
<groupId>com.coremedia.labs</groupId>
<artifactId>enhanced-linkbuilder</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency-->
<!--dependency>
<groupId>com.coremedia.labs</groupId>
<artifactId>jslt-postprocessor</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency-->
</dependencies>

</project>
19 changes: 19 additions & 0 deletions extensions/jslt-postprocessor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# JSLT Postprocessor Example

This module demonstrates the use of postprocessors to transform the GraphQL query response into
JSON not easily generated by GraphQL queries or even violating GraphQL constraints. Use cases i.e. are
legacy systems which are consuming custom JSON formats.

The JSLT postprocessor uses the [JSLT library](https://github.com/schibsted/jslt). Check the GitHub repository
for further documentation on the available transformation rules.

## Configuration

1. Enable the extension by commenting out the dependency in `/extensions/extensions-bom/pom.xml`.
2. Mark the queries which you want to enable for postprocessing by adding the `jslt` option in the query header, i.e.
```
#!query name=sites view=navigation type=CMSiteImpl jslt=meta
```
3. Add a transformation template named after the given option value to the `/src/main/resources/jslt` directory of
this module.
4. Rebuild the server.
36 changes: 36 additions & 0 deletions extensions/jslt-postprocessor/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>
<groupId>com.coremedia.labs</groupId>
<artifactId>extensions</artifactId>
<version>1-SNAPSHOT</version>
</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>jslt-postprocessor</artifactId>

<description>JSLT Query Result Example Postprocessor</description>

<properties>
<jslt.version>0.1.3</jslt.version>
</properties>

<dependencies>
<dependency>
<groupId>com.coremedia.labs</groupId>
<artifactId>headless-server-core</artifactId>
<version>${project.version}</version>
</dependency>
<!-- third-party libs -->
<dependency>
<groupId>com.schibsted.spt.data</groupId>
<artifactId>jslt</artifactId>
<version>${jslt.version}</version>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.coremedia.caas.extension.jslt;

import com.coremedia.caas.config.ProcessingDefinition;
import com.coremedia.caas.query.QueryDefinition;
import com.coremedia.caas.server.controller.base.ResponseStatusException;
import com.coremedia.caas.server.controller.interceptor.QueryExecutionInterceptorAdapter;
import com.coremedia.caas.server.service.request.ClientIdentification;
import com.coremedia.caas.service.repository.RootContext;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.schibsted.spt.data.jslt.Expression;
import com.schibsted.spt.data.jslt.Parser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletWebRequest;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
@SuppressWarnings("unused")
public class JsltPostprocessor extends QueryExecutionInterceptorAdapter {

private static final Logger LOG = LoggerFactory.getLogger(JsltPostprocessor.class);


private ConcurrentHashMap<String, Expression> expressionCache = new ConcurrentHashMap<>();


@Override
public Object postQuery(Object resultData, String tenantId, String siteId, ClientIdentification clientIdentification, RootContext rootContext, ProcessingDefinition processingDefinition, QueryDefinition queryDefinition, Map<String, Object> queryArgs, ServletWebRequest request) {
// check for query transformation option
String transformer = queryDefinition.getOption("jslt");
if (transformer != null) {
try {
// run transformation template with name specified in query option
Expression jslt = expressionCache.computeIfAbsent(transformer, (name) -> Parser.compileResource("jslt/" + name + ".jslt"));
return jslt.apply(new ObjectMapper().valueToTree(resultData));
} catch (Exception e) {
LOG.error("JSON transformation failed", e);
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"__meta": "Transformed by JSLT",
* : .
}
Loading

0 comments on commit 1d0931d

Please sign in to comment.