3.0.20190714142556

Version: 3.0.20190714142556

Documentation (pdf)

With this release comes a new major version of Crnk! There have been a great number of incremental improvements to the code base over the past year: Crnk 3.0 has taken the features and learnings from this time and improves upon some of the configuration defaults. This in turn allows to setup resources and relationships more simply, more quickly and with fewer lines of code. All together has the potential to quite drastically reduce the code base. For example, in this subsequent example screenshot the JPA module has been combined with facetted search to create an advanced user screen with sorting, facets, paging and full-text search. No implementation necessary on the backend-side!

The release is complemented by a number of new features. Most notably the generation of Asciidoc documentation and a type-safe query API. While being a major release, changes required should be minimal (see below).

Cleanup & Migration

Update Examples application

Still work in progress, but crnk-example has been updated to use the various 3.0 features.

Simplified Relationship Setup with @JsonApiRelation.mappedBy

@JsonApiRelation.oppositeName has been deprecated in favor of @JsonApiRelation.mappedBy. It must be set for bi-directional relationships to point from the non-owning to the owning side:
@JsonApiResource(type = "tasks")
public class Task {

    @JsonApiRelation
    private Project project;

    ...
}

@JsonApiResource(type = "tasks")
public class Task {

    @JsonApiRelation(mappedBy="project")
    private Set<Task> tasks;

    ...
}

The owning side is typically the single-valued field. This is because the single-valued field is something like a column in a database table that can be accessed and updated. In contrast, the multi-valued field can only be obtained by specifying issuing a query against the single-valued field and as such not suitable to be the owner. The behavior matches with similar JPA-related annotations.

Under the hood the mappedBy property will trigger a suitable default for @JsonApiRelation.repositoryBehavior and @JsonApiRelation.lookup on both sides, simplifying the relationship setup for most use cases. If no relationship repository can be setup, Crnk will no fail directly upon startup.

Improved default values for @JsonApiRelation

Relationship implementation can greatly vary. For this reason the various attributes of @JsonApiRelation historically had most features disabled by default (forwarding, serialization, etc.) to let developers configure what is needed. Over the past year a variety of tooling has been added to simplify the relationship setup, which in turn made the conservative defaults less suited than in the past. With this release new defaults have been introduced that inspect the resources and repositories more closely to decide for a particular default value for @JsonApiRelation.lookUp and @JsonApiRelation.repositoryBehavior.

In many cases it should no longer be necessary to specify those properties, for example:

@JsonApiResource(type = "task")
public class Task {
    @JsonApiId
    public String id;

    @JsonApiRelation // not backed by repository
    public Project project;

    @JsonApiRelation // backed by repository
    public List subTasks;
}

@JsonApiResource(type = "project")
public class Project {
    @JsonApiId
    public String id;

    @JsonApiRelation(mappedBy = "project")
    public Set tasks;

    @JsonApiRelationId
    public Project parentId;

    @JsonApiRelation
    public Project parent;
}

class SubTaskRepository extends OneRelationshipRepositoryBase {
    @Override
    public RelationshipMatcher getMatcher() {
        RelationshipMatcher matcher = new RelationshipMatcher();
        matcher.rule().field("subTasks").add();
        return matcher;
    }
    ...
}
Have a look at the updated documentation and the new example in the documentation. More information is available in the documentation. Please provide feedback in this area!

Type-safe QuerySpec construction with a Crnk Annotation Processor

Available as a preview in this release

An annotation processor is introduced that allows the type-safe construction of QuerySpec and PathSpec.
UserQuerySpec querySpec = new UserQuerySpec();
        querySpec.sort().loginId().desc();
        querySpec.filter().projects().id().filter(FilterOperator.EQ, 12);
        querySpec.field().loginId();
        querySpec.include().projects();
More information can be found here.

More flexible Gradle generator plugin

The Gradle Typescript generator adds support for the generation of artifacts other than Typescript.
buildscript {
	dependencies {
		classpath "io.crnk:crnk-gen-gradle:${version}"
	}
}

apply plugin: 'crnk-gen'
crnkGen {
    runtime {
        configuration = 'typescriptGenRuntime'
    }

    forked = true
    resourcePackages = ['io.crnk.example.api']

    typescript {
        enabled = true
        format = 'PLAINJSON'
        genDir = file('src/resources')
    }
}
For this purpose the project has been split into `crnk-gen-gradle`, `crnk-gen-typescript` and a number of helper projects. More information about Typescript generation is available here.

Documentation generation with AsciiDoc

The new crnk-gen-asciidoc project helps automating REST documentation. It makes use of two kinds of sources: resource classes and test cases. The resource classes give an overview of a REST endpoint with a list of available resources, attributes and relationships. Whereas test cases can serve as examples for developers. This mix of model-driven and test-driven documentation together with the standardization provided by JSON:API can greatly help in writing consistent, documented REST endpoints.

For more information have a look at the documentation and crnk-example.

Allow QuerySpec conversion to Criteria query

The following code snipped shows how to convert a QuerySpec to Criteria query without involving repositories:
JpaCriteriaQueryFactory queryFactory = JpaCriteriaQueryFactory.newInstance(em);

PathSpec idAttr = PathSpec.of(TestEntity.ATTR_id);
QuerySpec querySpec = new QuerySpec(TestEntity.class);
querySpec.addFilter(idAttr.filter(FilterOperator.GT, 0L));
querySpec.addSort(idAttr.sort(Direction.DESC));
querySpec.includeField(PathSpec.of("oneRelatedValue"));

JpaCriteriaQuery<TestEntity> query = queryFactory.query(TestEntity.class);
JpaQueryExecutor<TestEntity> executor = query.buildExecutor(querySpec);
List<TestEntity> resultList = executor.getResultList();

InMemoryHttpAdapter to connect CrnkClient with a server

There is a new InMemoryHttpAdapter implementation completing OkHttp, Apache HTTP client and RestTemplate. InMemoryHttpAdapter takes a CrnkBoot instance as parameter to directly connecting to a server without having to go through any TCP/HTTP transport layer. Requests are still serialized and deserialized, but no running HTTP server is necessary. Unit testing can greatly benefit from such a more simpler and faster setup:
    String url = ...
    CrnkClient client = new CnrkClient(url).
    client.setHttpAdapter(new InMemoryHttpAdapter(crnkBoot, url));

HttpAdapterListener for a vendor-neutral HTTP request/response interception

A new HttpAdapterListener can be added with addListener to HttpAdapter. It allows to cover many of the typical use cases like adding security headers without having to get and customize the underlying HTTP implementation.

crnk-format-plain-json in CrnkClient

The PlainJsonFormatModule can now be added to CrnkClient. This is in particular useful together with the Asciidoc generator to create documentation in the plain json format for consumer making use of it.

Prettier URLs computation

Computed links (CrnkClient, pagination, etc.) by default omit the `EQ` operator and type information where not necessary. This leads to more readable URLs like:
    http://127.0.0.1/api/tasks?filter[name]=Doe
rather than
    http://127.0.0.1/api/tasks?filter[tasks][name][EQ]=Doe

OpenTracing Support

OpenTracing is a vendor-neutral API to write trace information. crnk-monitor-opentracing introduces a new module OpenTracingServerModule that brings support for OpenTracing to Crnk. It can be used properly set the names of spans. More information is available in the documentation.

Singular Nested Resources

Support for nested resources has been improved by introducing support singular nested resources. Thereby a parent can hold a single nested resource as single-valued relationships. Urls then look like:

http://example.com/posts/1/header

An example looks like:

@JsonApiResource(type = "header", nested = true)
public class PostHeader {

	@JsonApiId
	@JsonApiRelationId
	private String postId;

	private String value;

	@JsonApiRelation(opposite = "header", lookUp = LookupIncludeBehavior.AUTOMATICALLY_WHEN_NULL,
			repositoryBehavior = RelationshipRepositoryBehavior.FORWARD_OWNER)
	private Post post;

	...
}
More information can be found here.

More flexible EntityManager setup with JPA module

JpaModule.createServerModule(...) comes with a new flavor that allows to pass the EntityManager as java.util.Supplier. Every time a repository makes use of the EntityManager, the supplier is called and must return an instance. The supplier together with the existing TransactionRunner allows for custom JPA setups and transaction handling.

Presentation model for Crnk UI

Available as a preview in this release

With this release we started refactoring the backend side of crnk-ui. It now offers a presentation model for all its managed resources:

In subsequent releases the presentation model will be refined and crnk-ui on the frontend side updated accordingly. But not only crnk-ui, any kind of application can make use of it to build generic but still custom frontends.

Singular Nested Resources

Support for nested resources has been improved by introducing support singular nested resources. Thereby a parent can hold a single nested resource as single-valued relationships. Urls then look like:

http://example.com/posts/1/header

An example looks like:

@JsonApiResource(type = "header", nested = true)
public class PostHeader {

	@JsonApiId
	@JsonApiRelationId
	private String postId;

	private String value;

	@JsonApiRelation(opposite = "header", lookUp = LookupIncludeBehavior.AUTOMATICALLY_WHEN_NULL,
			repositoryBehavior = RelationshipRepositoryBehavior.FORWARD_OWNER)
	private Post post;

	...
}
More information can be found here.

Specify access mode with @JsonApiResource

@JsonApiResource has new properties to configure the allowed access modes to a resource: sortable, filterable, postable, readable, patchable and deletable. An example looks like:
@JsonApiResource(
    type = "tasks", sortable = false, filterable = true,
    postable = false, readable = false, patchable = false, deletable = false
)
public class AccessDeniedTestResource {

    ...

}
The new properties match the ones from @JsonApiField and provide the base line for it. For example, if a resource does not allow filtering, none of its fields will.

New Security-related repositories

With
SecurityConfig.setExposeRepositories(true)
the SecurityModule sets up repositories to make its internal rules available from the REST API:

Support for GROUP operator by facets

The FacetModule gained support for the GROUP operator:
http://127.0.0.1:8080/api/facet?filter[values][GROUP]=priority,name
It works equivalent to SQL GROUP BY queries. In the example a facet count is computed for every pair of priority and name attribute.

Support for JsonInclude.Include.NON_EMPTY

For example in the subsequent snippet the default 0 value will be omitted:
@JsonApiId
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private long id;