Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions agrest-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?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>
<artifactId>agrest-parent</artifactId>
<groupId>io.agrest</groupId>
<version>5.0.M8-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>agrest-spring-boot-starter</artifactId>
<name>agrest-spring-boot-starter: Spring Boot starter for Agrest</name>
<description>Spring Boot starter for Agrest</description>

<dependencies>
<dependency>
<groupId>io.agrest</groupId>
<artifactId>agrest-annotations</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.agrest</groupId>
<artifactId>agrest-engine</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>build-tests</id>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.agrest.spring.starter;

import io.agrest.runtime.AgRuntimeBuilder;

public interface AgRuntimeCustomizer {

void customize(AgRuntimeBuilder builder);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package io.agrest.spring.starter;

import io.agrest.meta.AgSchema;
import io.agrest.runtime.AgRuntime;
import io.agrest.runtime.jackson.IJacksonService;
import io.agrest.runtime.protocol.IEntityUpdateParser;
import io.agrest.spring.starter.exceptions.RestResponseEntityExceptionHandler;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnWebApplication
@ConditionalOnProperty(prefix = "agrest", name = "enabled")
public class AgrestAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public AgRuntime agRuntime(ObjectProvider<AgRuntimeCustomizer> customizerProvider) {
var builder = AgRuntime.builder();
customizerProvider.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder.build();
}

@Bean
@ConditionalOnMissingBean
public IJacksonService jacksonService(AgRuntime runtime) {
return runtime.service(IJacksonService.class);
}

@Bean
@ConditionalOnMissingBean
public DataResponseWriter dataResponseWriter(IJacksonService jacksonService) {
return new DataResponseWriter(jacksonService);
}

@Bean
@ConditionalOnMissingBean
public EntityUpdateReaderProcessor entityUpdateReaderProcessor(AgRuntime runtime) {
return new EntityUpdateReaderProcessor(runtime.service(IEntityUpdateParser.class), runtime.service(AgSchema.class));
}

@Bean
@ConditionalOnMissingBean
public EntityUpdateReader entityUpdateReader(EntityUpdateReaderProcessor processor) {
return new EntityUpdateReader(processor);
}

@Bean
@ConditionalOnMissingBean
public EntityUpdateCollectionReader entityUpdateCollectionReader(EntityUpdateReaderProcessor processor) {
return new EntityUpdateCollectionReader(processor);
}

@Bean
@ConditionalOnMissingBean
public SimpleResponseWriter simpleResponseWriter(IJacksonService jacksonService) {
return new SimpleResponseWriter(jacksonService);
}

@Bean
public RestResponseEntityExceptionHandler exceptionHandler() {
return new RestResponseEntityExceptionHandler();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.agrest.spring.starter;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "agrest")
public class AgrestProperties {

private boolean enabled = false;


public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.agrest.spring.starter;

import com.fasterxml.jackson.core.JsonGenerator;
import io.agrest.DataResponse;
import io.agrest.runtime.jackson.IJacksonService;

import java.io.IOException;
import java.util.List;

import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;

public class DataResponseWriter implements HttpMessageConverter<DataResponse<?>> {

private final IJacksonService jacksonService;

public DataResponseWriter(IJacksonService jacksonService) {
this.jacksonService = jacksonService;
}

@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return false;
}

@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return DataResponse.class.isAssignableFrom(clazz);
}

@Override
public List<MediaType> getSupportedMediaTypes() {
return null;
}

@Override
public DataResponse<?> read(Class<? extends DataResponse<?>> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}

@Override
public void write(DataResponse<?> dataResponse, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
var entityStream = outputMessage.getBody(); // todo: do we need to close this?
jacksonService.outputJson(out -> writeData(dataResponse, out), entityStream);
}

protected void writeData(DataResponse<?> t, JsonGenerator out) throws IOException {
t.getEncoder().encode(null, t, out);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package io.agrest.spring.starter;

import io.agrest.AgException;
import io.agrest.EntityUpdate;
import io.agrest.reflect.Types;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;

import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;

public class EntityUpdateCollectionReader<T> implements GenericHttpMessageConverter<Collection<EntityUpdate<T>>> {

private final EntityUpdateReaderProcessor reader;

public EntityUpdateCollectionReader(EntityUpdateReaderProcessor reader) {this.reader = reader;}

@Override
public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
var classForType = Types.getClassForType(type).orElse(Object.class);
if (!Collection.class.isAssignableFrom(classForType) || !MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) {
return false;
}

return Types.unwrapTypeArgument(type)
.filter(collectionParam -> collectionParam instanceof ParameterizedType)
.map(collectionParam -> (ParameterizedType) collectionParam)
.filter(collectionParam -> EntityUpdate.class.equals((collectionParam).getRawType()))
.isPresent();
}

@Override
public Collection<EntityUpdate<T>> read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
Type entityUpdateType = Types.unwrapTypeArgument(type)
.orElseThrow(() -> AgException.internalServerError("Invalid request entity collection type: %s", type));

return reader.read(entityUpdateType, inputMessage.getBody());
}

@Override
public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
return false;
}

@Override
public void write(Collection<EntityUpdate<T>> entityUpdates, Type type, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

}

@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return false;
}

@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return false;
}

@Override
public List<MediaType> getSupportedMediaTypes() {
return null;
}

@Override
public Collection<EntityUpdate<T>> read(Class<? extends Collection<EntityUpdate<T>>> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}

@Override
public void write(Collection<EntityUpdate<T>> entityUpdates, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

}
}
Loading