33require "grape"
44require "json"
55require "securerandom"
6+ require_relative "helpers"
67require_relative "../handlers/base"
78require_relative "../handlers/default"
89require_relative "../core/logger_factory"
@@ -12,6 +13,8 @@ module Hooks
1213 module App
1314 # Factory for creating configured Grape API classes
1415 class API
16+ include Hooks ::App ::Helpers
17+
1518 # Create a new configured API class
1619 def self . create ( config :, endpoints :, log :)
1720 # Store startup time for uptime calculation
@@ -40,107 +43,7 @@ def self.create(config:, endpoints:, log:)
4043 # Use class_eval to dynamically define routes
4144 api_class . class_eval do
4245 # Define helper methods first, before routes
43- helpers do
44- # Enforce request size and timeout limits
45- def enforce_request_limits ( config )
46- # Check content length (handle different header formats and sources)
47- content_length = headers [ "Content-Length" ] || headers [ "CONTENT_LENGTH" ] ||
48- headers [ "content-length" ] || headers [ "HTTP_CONTENT_LENGTH" ] ||
49- env [ "CONTENT_LENGTH" ] || env [ "HTTP_CONTENT_LENGTH" ]
50-
51- # Also try to get from request object directly
52- content_length ||= request . content_length if respond_to? ( :request ) && request . respond_to? ( :content_length )
53-
54- content_length = content_length &.to_i
55-
56- if content_length && content_length > config [ :request_limit ]
57- error! ( "request body too large" , 413 )
58- end
59-
60- # Note: Timeout enforcement would typically be handled at the server level (Puma, etc.)
61- end
62-
63- # Verify the incoming request using the configured authentication method
64- def validate_auth! ( payload , headers , endpoint_config )
65- auth_config = endpoint_config [ :auth ]
66- auth_plugin_type = auth_config [ :type ] . downcase
67- secret_env_key = auth_config [ :secret_env_key ]
68-
69- return unless secret_env_key
70-
71- secret = ENV [ secret_env_key ]
72- unless secret
73- error! ( "secret '#{ secret_env_key } ' not found in environment" , 500 )
74- end
75-
76- auth_class = nil
77-
78- case auth_plugin_type
79- when "hmac"
80- auth_class = Plugins ::Auth ::HMAC
81- when "shared_secret"
82- auth_class = Plugins ::Auth ::SharedSecret
83- else
84- error! ( "Custom validators not implemented in POC" , 500 )
85- end
86-
87- unless auth_class . valid? (
88- payload :,
89- headers :,
90- secret :,
91- config : endpoint_config
92- )
93- error! ( "authentication failed" , 401 )
94- end
95- end
96-
97- # Parse request payload
98- def parse_payload ( raw_body , headers , symbolize : true )
99- content_type = headers [ "Content-Type" ] || headers [ "CONTENT_TYPE" ] || headers [ "content-type" ] || headers [ "HTTP_CONTENT_TYPE" ]
100-
101- # Try to parse as JSON if content type suggests it or if it looks like JSON
102- if content_type &.include? ( "application/json" ) || ( raw_body . strip . start_with? ( "{" , "[" ) rescue false )
103- begin
104- parsed_payload = JSON . parse ( raw_body )
105- parsed_payload = parsed_payload . transform_keys ( &:to_sym ) if symbolize && parsed_payload . is_a? ( Hash )
106- return parsed_payload
107- rescue JSON ::ParserError
108- # If JSON parsing fails, return raw body
109- end
110- end
111-
112- # Return raw body for all other cases
113- raw_body
114- end
115-
116- # Load handler class
117- def load_handler ( handler_class_name , handler_dir )
118- # Convert class name to file name (e.g., Team1Handler -> team1_handler.rb)
119- # E.g.2: GithubHandler -> github_handler.rb
120- # E.g.3: GitHubHandler -> git_hub_handler.rb
121- file_name = handler_class_name . gsub ( /([A-Z])/ , '_\1' ) . downcase . sub ( /^_/ , "" ) + ".rb"
122- file_path = File . join ( handler_dir , file_name )
123-
124- if File . exist? ( file_path )
125- require file_path
126- Object . const_get ( handler_class_name ) . new
127- else
128- raise LoadError , "Handler #{ handler_class_name } not found at #{ file_path } "
129- end
130- rescue => e
131- error! ( "failed to load handler #{ handler_class_name } : #{ e . message } " , 500 )
132- end
133-
134- # Determine HTTP error code from exception
135- def determine_error_code ( exception )
136- case exception
137- when ArgumentError then 400
138- when SecurityError then 401
139- when NotImplementedError then 501
140- else 500
141- end
142- end
143- end
46+ helpers Helpers
14447
14548 # Define operational endpoints
14649 get captured_config [ :health_path ] do
0 commit comments