# frozen_string_literal: true require "sinatra" require "puma" require "route_downcaser" require "prometheus/middleware/collector" require "prometheus/middleware/exporter" require_relative "server_utils" # Server with endpoints generated based on Entities with CRUD operations for them class RestServer < Sinatra::Base set :server, :puma enable :logging if ENV["debug"] == "true" set :bind, "0.0.0.0" use RouteDowncaser::DowncaseRouteMiddleware use Prometheus::Middleware::Collector use Prometheus::Middleware::Exporter helpers ServerUtils before do @request_id = SecureRandom.uuid headers["X-Correlation-Id"] = @request_id headers["Access-Control-Allow-Origin"] = "*" log({ msg: "Request" }) if request.fullpath != "" end after do log({ msg: "Response", status: response.status }) if request.fullpath != "" end get "/favicon.ico" do send_file File.join(__dir__, "rest.ico") end SWAGGER_FILES = %w[index.css swagger.html swagger-initializer.js swagger-ui-bundle.js swagger-ui-standalone-preset.js swagger-ui.css] SWAGGER_FILES.each do |filename| get "/#{filename.gsub(".html", "")}" do if filename.end_with? ".json" content_type :json elsif filename.end_with? ".css" content_type :css elsif filename.end_with? ".js" content_type :js end File.read(File.join(__dir__, "swagger", filename)) end end get "/swagger.json" do content_type :json SwaggerBuilder.build_swagger_for(Entities.models, request.host_with_port) end def has_id?(model, id) Entities.models[model].key?(id) end def not_have(id) [404, JSON.generate({ error: "'#{id}' not found" })] end get "/" do summary = { models: Entities.models.keys.to_s, docs_url: "Swagger docs" } summary[:db] = MongoClient.client.summary.to_s if ENV["mongodb"] JSON.generate(summary) rescue StandardError => e [500, e.message] end Entities.models.each_key do |model| post "/#{model.downcase}" do request_body = request.body.read data = {} data = JSON.parse(request_body) unless request_body.empty? id = "" if data["id"] id = data["id"].to_s else id = SecureRandom.uuid data["id"] = id end if ENV["mongodb"] MongoClient.insert(model, data, id) else Entities.models[model][id] = data end log({ msg: "Created #{id}" }) [201, JSON.generate({ id: id })] end options "/#{model.downcase}" do response["Allow"] = "*" response["Access-Control-Allow-Methods"] = "*" response["Access-Control-Allow-Headers"] = "*" end get "/#{model.downcase}" do if ENV["mongodb"] if params == {} JSON.generate(MongoClient.get_all(model)) else [200, JSON.generate(MongoClient.find_all(model, params))] end else return JSON.generate(Entities.models[model].values) if params == {} matching_values = Entities.models[model].values.find_all do |val| val[params.keys[0]].to_s == params.values[0] end return JSON.generate(matching_values) end rescue StandardError => e [404, "Nothing found using #{params}. Only first param considered. #{e.message}"] end get "/#{model.downcase}/:id" do |id| if ENV["mongodb"] results = MongoClient.find(model, id) return not_have(id) unless results.first JSON.generate(results.first) else return not_have(id) unless has_id?(model, id) JSON.generate(Entities.models[model][id]) end end options "/#{model.downcase}/:id" do response["Allow"] = "*" response["Access-Control-Allow-Origin"] = "*" response["Access-Control-Allow-Methods"] = "*" response["Access-Control-Allow-Headers"] = "*" end put "/#{model.downcase}/:id" do |id| data = JSON.parse(request.body.read) if ENV["mongodb"] results = MongoClient.find(model, id) return not_have(id) unless results.first MongoClient.update(model, id, data) else return not_have(id) unless has_id?(model, id) Entities.models[model][id] = data end 204 end delete "/#{model.downcase}/:id" do |id| if ENV["mongodb"] results = MongoClient.find(model, id) return not_have(id) unless results.first MongoClient.delete(model, id) else return not_have(id) unless has_id?(model, id) Entities.models[model].delete(id) end 204 end end end