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

com.hotels.road.paver.controller.SchemaController Maven / Gradle / Ivy

/**
 * Copyright (C) 2016-2019 Expedia, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hotels.road.paver.controller;

import static com.hotels.road.paver.controller.PaverConstants.CONTEXT_PATH;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.avro.AvroTypeException;
import org.apache.avro.Schema;
import org.apache.avro.SchemaParseException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.RequiredArgsConstructor;

import com.hotels.road.exception.InvalidKeyPathException;
import com.hotels.road.exception.InvalidSchemaException;
import com.hotels.road.exception.InvalidSchemaVersionException;
import com.hotels.road.exception.UnknownRoadException;
import com.hotels.road.model.core.SchemaVersion;
import com.hotels.road.paver.service.PaverService;
import com.hotels.road.paver.service.exception.NoSuchSchemaException;
import com.hotels.road.rest.model.StandardResponse;
import com.hotels.road.schema.gdpr.InvalidPiiAnnotationException;

@Api(tags = "schema")
@RestController
@RequestMapping(CONTEXT_PATH + "/roads/{name}/schemas")
@RequiredArgsConstructor
public class SchemaController {
  private static final String LATEST_NOT_SUPPORTED_MESSAGE = "'latest' keyword is not supported when deleting schemas. Please specify concrete version instead.";
  static final String SCHEMA_DELETE_REQUEST = "Request to delete schema version %s received.";
  static final String DELETE_LATEST_SCHEMA_ERROR = "Only the latest schema can be deleted. Expected version: %s, got: %s";
  static final String LATEST = "latest";

  private final PaverService service;

  @ApiOperation(value = "Returns all schemas keyed by version number")
  @ApiResponses({
      @ApiResponse(code = 200, message = "List of schemas returned successfully.", response = StandardResponse.class),
      @ApiResponse(code = 404, message = "Road not found.", response = StandardResponse.class) })
  @GetMapping
  public Map getSchemas(@PathVariable String name) throws UnknownRoadException {
    return service.getActiveSchemas(name);
  }

  @ApiOperation(value = "Adds a new schema to a road")
  @ApiResponses({
      @ApiResponse(code = 200, message = "Request to add a new schema received.", response = StandardResponse.class),
      @ApiResponse(code = 404, message = "Road not found.", response = StandardResponse.class),
      @ApiResponse(code = 409, message = "Schema is not compatible with previous schemas.", response = StandardResponse.class) })
  @PreAuthorize("@paverAuthorisation.isAuthorised(authentication)")
  @PostMapping
  public StandardResponse addNewSchema(
      @ApiParam(name = "name", value = "road name", required = true) @PathVariable String name,
      @RequestBody Schema schema)
    throws UnknownRoadException, InvalidKeyPathException {
    service.addSchema(name, schema);
    return StandardResponse.successResponse("Request to add a new schema received.");
  }

  @ApiOperation(value = "Adds a new schema with a specific version to a road")
  @ApiResponses({
      @ApiResponse(code = 200, message = "Request to add a new schema received.", response = StandardResponse.class),
      @ApiResponse(code = 400, message = "Invalid Schema Version. The requested version must be greater than the largest registered version.", response = StandardResponse.class),
      @ApiResponse(code = 404, message = "Road not found.", response = StandardResponse.class),
      @ApiResponse(code = 409, message = "Schema is not compatible with previous schemas.", response = StandardResponse.class) })
  @PreAuthorize("@paverAuthorisation.isAuthorised(authentication)")
  @PostMapping("/{version}")
  public StandardResponse addNewSchema(
      @ApiParam(name = "name", value = "road name", required = true) @PathVariable String name,
      @PathVariable int version,
      @RequestBody Schema schema)
    throws UnknownRoadException, InvalidKeyPathException, InvalidSchemaVersionException {
    service.addSchema(name, schema, version);
    return StandardResponse.successResponse("Request to add a new schema received.");
  }

  @ApiOperation(value = "Returns a specific version of a schema")
  @ApiResponses({
      @ApiResponse(code = 200, message = "Requested schema returned successfully.", response = StandardResponse.class),
      @ApiResponse(code = 404, message = "Road or schema not found.", response = StandardResponse.class) })
  @GetMapping("/{version}")
  public Schema getSchema(
      @ApiParam(name = "name", value = "road name", required = true) @PathVariable String name,
      @PathVariable String version)
    throws NoSuchSchemaException, UnknownRoadException {
    SchemaVersion schema;
    if (LATEST.equalsIgnoreCase(version)) {
      schema = service.getLatestActiveSchema(name);
    } else {
      schema = service.getActiveSchema(name, Integer.parseInt(version));
    }
    return schema.getSchema();
  }

  @ApiOperation(value = "Deletes the latest version of a schema")
  @ApiResponses({
      @ApiResponse(code = 200, message = "Request to delete a schema received.", response = StandardResponse.class),
      @ApiResponse(code = 400, message = "Invalid Schema Version. Only the latest schema can be deleted.", response = StandardResponse.class),
      @ApiResponse(code = 404, message = "Road or Schema not found.", response = StandardResponse.class) })
  @PreAuthorize("@paverAuthorisation.isAuthorised(authentication)")
  @DeleteMapping("/{version}")
  public ResponseEntity deleteLatestSchema(
      @ApiParam(name = "name", value = "road name", required = true) @PathVariable String name,
      @PathVariable String version)
    throws UnknownRoadException, NoSuchSchemaException {
    if (LATEST.equalsIgnoreCase(version)) {
      return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(
          StandardResponse.failureResponse(LATEST_NOT_SUPPORTED_MESSAGE));
    }

    int versionInt = Integer.parseInt(version);

    SchemaVersion latestSchemaVersion = service.getLatestActiveSchema(name);
    if (versionInt == latestSchemaVersion.getVersion()) {
      service.deleteSchemaVersion(name, versionInt);
      return ResponseEntity.status(HttpStatus.OK).body(
          StandardResponse.successResponse(String.format(SCHEMA_DELETE_REQUEST, versionInt)));
    } else {
      String message = String.format(DELETE_LATEST_SCHEMA_ERROR, latestSchemaVersion.getVersion(), versionInt);
      return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(StandardResponse.failureResponse(message));
    }
  }

  @ExceptionHandler(AvroTypeException.class)
  public ResponseEntity avroTypeExceptionExceptionHandler(HttpServletRequest request, Exception e) {
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(
        StandardResponse.failureResponse("Invalid schema. " + e.getMessage()));
  }

  @ExceptionHandler(SchemaParseException.class)
  public ResponseEntity schemaParseException(HttpServletRequest request, Exception e) {
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(
        StandardResponse.failureResponse("Invalid schema. " + e.getMessage()));
  }

  @ExceptionHandler(NumberFormatException.class)
  public ResponseEntity numberFormatExceptionHandler(HttpServletRequest request, Exception e) {
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(
        StandardResponse.failureResponse("Invalid version number. " + e.getMessage()));
  }

  @ExceptionHandler(InvalidSchemaException.class)
  public ResponseEntity invalidSchemaExceptionHandler(HttpServletRequest request, Exception e) {
    return ResponseEntity.status(HttpStatus.CONFLICT).body(
        StandardResponse.failureResponse("Invalid schema. " + e.getMessage()));
  }

  @ExceptionHandler(InvalidSchemaVersionException.class)
  public ResponseEntity invalidSchemaVersionException(HttpServletRequest request, Exception e) {
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(
        StandardResponse.failureResponse("Invalid schema version. " + e.getMessage()));
  }

  @ExceptionHandler(InvalidPiiAnnotationException.class)
  public ResponseEntity invalidPiiAnnotationException(HttpServletRequest request, Exception e) {
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(StandardResponse.failureResponse(e.getMessage()));
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy