# typed: false # frozen_string_literal: true require "openapi_first" module PlugApp module Middleware class OpenapiValidation API_PATH_PREFIX = "/api/" SPEC_PATH = Rails.root.join("lib/plug_app/schemas/api/2023-03-06/openapi.json") SPEC = OpenapiFirst.load(SPEC_PATH) def initialize(app) @app = app end def call(env) request = Rack::Request.new(env) return @app.call(env) unless request.path.starts_with?(API_PATH_PREFIX) && request.path.exclude?("/settings") begin # force content-type to JSON env["CONTENT_TYPE"] = "application/json" if env["CONTENT_TYPE"] != "application/json" validated_request = SPEC.validate_request(request) return @app.call(env) if validated_request.valid? case validated_request.error when OpenapiFirst::Schema::ValidationError error_arr = format_arr(validated_request.error.errors.map(&:error)) Rails.logger.error(error_arr) if print_user_api_errors? [PlugApp::HTTP::BAD_REQUEST_I, { "Content-Type" => "application/json" }, [error_arr]] else case validated_request.error.error_type when :not_found [PlugApp::HTTP::NOT_FOUND_I, { "Content-Type" => "application/json" }, [format_str("Not Found")]] else error_message = if validated_request.error.errors.present? format_arr(validated_request.error.errors.map(&:error)) else format_str(validated_request.error.message) end Rails.logger.error(error_message) if print_user_api_errors? [PlugApp::HTTP::BAD_REQUEST_I, { "Content-Type" => "application/json" }, [error_message]] end end rescue StandardError => e raise e unless Rails.env.production? logger.error( "openapi.request_validation.error", path: request.path, method: request.env["REQUEST_METHOD"], error_message: e.message, ) end end private def format_str(error) { errors: [ { message: error, }, ], }.to_json end private def format_arr(errors) { errors: errors.each_with_object([]) do |error, arr| arr << { message: error, } end, }.to_json end end end end