All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alon.spring.crud.api.controller.CrudController Maven / Gradle / Ivy

Go to download

Fornece implementação básica e expansível para criação API's CRUD com Spring Boot e Spring Data JPA.

There is a newer version: 1.0.9
Show newest version
package com.alon.spring.crud.api.controller;

import com.alon.spring.crud.api.controller.input.Options;
import com.alon.spring.crud.api.controller.input.OptionsNormalizer;
import com.alon.spring.crud.api.controller.input.SearchInput;
import com.alon.spring.crud.api.controller.input.SearchResolver;
import com.alon.spring.crud.api.controller.input.mapper.InputMapper;
import com.alon.spring.crud.api.controller.input.mapper.ModelMapperInputMapper;
import com.alon.spring.crud.api.controller.output.OutputPage;
import com.alon.spring.crud.api.projection.ProjectionRepresentation;
import com.alon.spring.crud.api.projection.ProjectionService;
import com.alon.spring.crud.domain.model.BaseEntity;
import com.alon.spring.crud.domain.service.CrudService;
import com.alon.spring.crud.domain.service.SearchCriteria;
import com.alon.spring.crud.domain.service.exception.*;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.ResponseEntity.BodyBuilder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
import org.springframework.web.server.ResponseStatusException;

import javax.validation.Valid;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Collections;
import java.util.List;

public abstract class CrudController<
        MANAGED_ENTITY_ID_TYPE extends Serializable,
        MANAGED_ENTITY_TYPE extends BaseEntity,
        CREATE_INPUT_TYPE, 
        UPDATE_INPUT_TYPE,
        SEARCH_INPUT_TYPE extends SearchInput,
        SERVICE_TYPE extends CrudService
> {
	
    protected final SERVICE_TYPE service;
    
    @Autowired
    protected ProjectionService projectionService;

    @Autowired
    protected OptionsNormalizer optionsNormalizer;

    @Autowired
    private SearchResolver searchResolver;
    
    protected InputMapper createInputMapper;
    protected InputMapper updateInputMapper;

    protected final boolean disableContentCaching;

    protected Class managedEntityClass = extractManagedEntityType();
    
    public CrudController(SERVICE_TYPE service) {
        this.service = service;
        this.createInputMapper = new ModelMapperInputMapper<>(managedEntityClass);
        this.updateInputMapper = new ModelMapperInputMapper<>(managedEntityClass);
        this.disableContentCaching = true;
    }

    public CrudController(SERVICE_TYPE service, boolean disableContentCaching) {
        this.service = service;
        this.createInputMapper = new ModelMapperInputMapper<>(managedEntityClass);
        this.updateInputMapper = new ModelMapperInputMapper<>(managedEntityClass);
        this.disableContentCaching = disableContentCaching;
    }
    
    protected CrudController(SERVICE_TYPE service, 
    		InputMapper createInputMapper,
    		InputMapper updateInputMapper) {

        this(service, createInputMapper, updateInputMapper, true);
    }

    protected CrudController(SERVICE_TYPE service,
             InputMapper createInputMapper,
             InputMapper updateInputMapper,
             boolean disableContentCaching) {

        this.service = service;
        this.createInputMapper = createInputMapper;
        this.updateInputMapper = updateInputMapper;
        this.disableContentCaching = disableContentCaching;
    }

    @ApiOperation(value = "Search resources",
            nickname = "Search", produces = MediaType.APPLICATION_JSON_VALUE)
    @GetMapping
    public ResponseEntity search(
            SEARCH_INPUT_TYPE search,
            Pageable pageable,
            @Valid Options options,
            ServletWebRequest request
    ) {
        if (disableContentCaching)
            ShallowEtagHeaderFilter.disableContentCaching(request.getRequest());

        optionsNormalizer.normalizeOptions(options,
                this::getCollectionDefaultProjection, this::getCollectionAllowedProjections);

        Specification specification = searchResolver.resolve(search);

        SearchCriteria criteria = SearchCriteria.of()
                .filter(specification)
                .pageable(pageable)
                .expand(options.getExpand())
                .build();

        Page page = service.search(criteria);

        OutputPage response;

        try {
            response = projectionService.project(options.getProjection(), page);
        } catch (ProjectionException e) {
            if (optionsNormalizer.projectDefaultOnError(options.getProjection(), this::getCollectionDefaultProjection))
                response = projectionService.project(getCollectionDefaultProjection(), page);
            else
                throw e;
        }

        return buildHttpGETResponseEntity(HttpStatus.OK)
                .body(response);
    }

    @ApiOperation(value = "Read a resource", nickname = "Read", produces = MediaType.APPLICATION_JSON_VALUE)
    @GetMapping("/{id}")
    public ResponseEntity read(
            @PathVariable MANAGED_ENTITY_ID_TYPE id,
            @Valid Options options,
            ServletWebRequest request
    ) throws ReadException {
        if (disableContentCaching)
            ShallowEtagHeaderFilter.disableContentCaching(request.getRequest());

        optionsNormalizer.normalizeOptions(options,
                this::getSingleDefaultProjection, this::getSingleAllowedProjections);

        MANAGED_ENTITY_TYPE entity;

        try {
            entity = service.read(id, List.copyOf(options.getExpand()));
        } catch (NotFoundException ex) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, ex.getMessage());
        }

        Object response;

        try {
            response = projectionService.project(options.getProjection(), entity);
        } catch (ProjectionException e) {
            if (optionsNormalizer.projectDefaultOnError(options.getProjection(), this::getSingleDefaultProjection))
                response = projectionService.project(getSingleDefaultProjection(), entity);
            else
                throw e;
        }

        return buildHttpGETResponseEntity(HttpStatus.OK)
                .body(response);
    }

    @ApiOperation(value = "Create a resource", nickname = "Create",
            consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping
    protected ResponseEntity create(@RequestBody @Valid CREATE_INPUT_TYPE input) throws CreateException {
        MANAGED_ENTITY_TYPE entity = createInputMapper.map(input);
        
        entity = service.create(entity);
        
        Object response = projectionService.project(getSingleDefaultProjection(), entity);

        return ResponseEntity
                .status(HttpStatus.CREATED)
                .body(response);
        
    }

    @ApiOperation(value = "Update a resource", nickname = "Update",
            consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @PutMapping("/{id}")
    public ResponseEntity update(
            @PathVariable MANAGED_ENTITY_ID_TYPE id,
            @RequestBody @Valid UPDATE_INPUT_TYPE input) throws UpdateException {

        MANAGED_ENTITY_TYPE entity = service.read(id);
        updateInputMapper.map(input, entity);

        entity = service.update(entity);

        Object response = projectionService.project(getSingleDefaultProjection(), entity);

        return ResponseEntity
                .status(HttpStatus.OK)
                .body(response);
        
    }

    @ApiOperation(value = "Delete a resource", nickname = "Delete")
    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(@PathVariable MANAGED_ENTITY_ID_TYPE id) throws DeleteException {
        try {
            service.delete(id);
        } catch (NotFoundException ex) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, ex.getMessage());
        }
    }

    @ApiOperation(value = "Get available projections and their representations",
            nickname = "Get projections", produces = MediaType.APPLICATION_JSON_VALUE)
    @GetMapping("/projections")
    public List getRepresentations() {
        return projectionService.getEntityRepresentations(extractManagedEntityType(),
                this::getSingleDefaultProjection, this::getCollectionDefaultProjection);
    }

    public BodyBuilder buildHttpGETResponseEntity(HttpStatus status) {
        return ResponseEntity.status(status);
    }

    protected String getSingleDefaultProjection() {
        return ProjectionService.NOP_PROJECTION;
    }

    protected List getSingleAllowedProjections() {
        return Collections.emptyList();
    }

    protected String getCollectionDefaultProjection() {
        return ProjectionService.NOP_PROJECTION;
    }

    protected List getCollectionAllowedProjections() {
        return Collections.emptyList();
    }

    private final > Class extractManagedEntityType() {
        ParameterizedType classType = (ParameterizedType) getClass().getGenericSuperclass();
        return (Class) classType.getActualTypeArguments()[1];
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy