3.0.20190714142556
Version: 3.0.20190714142556
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:- Relationships handling got automated and simplified.
- The JPA integration got simplified and more powerful.
- Crnk got reactive and can be integrated with frameworks like Vert.x.
- Support for code generation.
- OpenTracing integration.
- More powerful means to enforce authorization.
- Nesting of resources for cleaner, more intuitive APIs.
- Support for facetted search.
- ...
![]() |
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
- The V2 suffixes have been removed from the repository interfaces. They have been in place for historic reasons to replace QueryParams-based repositories.
- QueryParams has been deprecated in favor of QuerySpec for a long time. With this release QueryParams has been removed from crnk-core. It was a cause for substantial legacy and duplication and its removal helped further streamline the implementation. Should you still desire QueryParams support and cannot migrate for one reason or the other, it is possible to take the past QuerySpec to QueryParams conversion and apply it outside of Crnk core in your own repositories.
- @JsonApiRelation.oppositeName has been deprecated in favor of @JsonApiRelation.mappedBy. See below for more information. Be aware of the changed semantics. The property must only be set on one side.
- Repository interfaces make us of the Collection type rather than Iterable. The later was cumbersome to use and not necessary for use cases at hand.
- Resource identifier no longer must extend Serializable. It was not a requirement and complicated the handling of generic type arguments.
- Most deprecated methods have been removed.
- RelationshipRepositoryV2 and BulkRelationshipRepositoryV2 have been deprecated in favor of OneRelationshipRepository and ManyRelationshipRepository. The new repositories have a number of advantages. Finders of the new repositories follow the concepts of BulkRelationshipRepositoryV2 to have efficiency by default. RelationshipMatch is used by default to bind the repository to resources. Repositories may still implement both interfaces to gain the original RelationshipRepositoryV2 functionality.
- JPA repositories with DTO mapping now behave exactly like regular resources. Make sure that the DTOs are properly annotation with @JsonApiRelation and @JsonApiRelationId. No information is inferred from the underlying entities.
- RepositoryDecoratorFactory has been simplified to a single decorateRepository method and WrappedResourceRepository, WrappedOneRelationshipRepository and WrappedManyRelationshipRepository. There are no dedicated decoration repository interfaces anymore, just the regular interfaces.
- Decorators have been removed form the JpaRepositoryConfig. The regular RepositoryDecoratorFactory can be used instead.
- New defaults for @JsonApiRelation.lookup and @JsonApiRelation.repositoryBehavior should allow to eliminate a large part of the custom configuration. For more information see below.
- crnk-jpa has been renamed to crnk-data-jpa and the Java package updated accordingly.
- crnk-activiti has been renamed to crnk-data-activiti and the Java package updated accordingly.
- The SecurityProvider interface gained a SecurityProviderContext parameter in order to more easily integrate with reactive systems.
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 ListHave 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!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; } ... }
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.
![]() |
![]() |
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]=Doerather 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:
- The presentation model consists of explorer and editor resources.
- An editor resource represents a screen for a single resource that either can be viewed or edited.
- An explorer resource represents a collection of resources displayed in table form with sorting, filtering, paging.
- Each resource is composed of many different elements from tables, pagers, queries, forms and input elements. All together make up the screen.
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
WithSecurityConfig.setExposeRepositories(true)the SecurityModule sets up repositories to make its internal rules available from the REST API:
- <contextPath>/security/callerPermission
- <contextPath>/security/role
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,nameIt 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;