@@ -4,17 +4,158 @@ module Html2rss
44 module Web
55 module Api
66 module V1
7- module Contract
7+ ##
8+ # Shared API v1 contract constants and payload builders.
9+ module Contract # rubocop:disable Metrics/ModuleLength
810 CODES = {
911 unauthorized : Html2rss ::Web ::UnauthorizedError ::CODE ,
1012 forbidden : Html2rss ::Web ::ForbiddenError ::CODE ,
1113 internal_server_error : Html2rss ::Web ::InternalServerError ::CODE
1214 } . freeze
1315
16+ ERROR_KINDS = {
17+ auth : 'auth' ,
18+ input : 'input' ,
19+ network : 'network' ,
20+ server : 'server'
21+ } . freeze
22+
23+ NEXT_ACTIONS = {
24+ enter_token : 'enter_token' ,
25+ correct_input : 'correct_input' ,
26+ retry : 'retry' ,
27+ wait : 'wait' ,
28+ none : 'none'
29+ } . freeze
30+
31+ RETRY_ACTIONS = {
32+ alternate : 'alternate' ,
33+ primary : 'primary' ,
34+ none : 'none'
35+ } . freeze
36+
37+ READINESS_PHASES = {
38+ link_created : 'link_created' ,
39+ feed_ready : 'feed_ready' ,
40+ feed_not_ready_yet : 'feed_not_ready_yet' ,
41+ preview_unavailable : 'preview_unavailable'
42+ } . freeze
43+
44+ PREVIEW_STATUSES = {
45+ pending : 'pending' ,
46+ ready : 'ready' ,
47+ degraded : 'degraded' ,
48+ unavailable : 'unavailable'
49+ } . freeze
50+
1451 MESSAGES = {
1552 auto_source_disabled : 'Auto source feature is disabled' ,
1653 health_check_failed : 'Health check failed'
1754 } . freeze
55+
56+ class << self
57+ # Builds the structured API error envelope used by JSON API routes.
58+ #
59+ # @param error [StandardError]
60+ # @return [Hash{Symbol=>Object}] structured API error details.
61+ def failure_payload ( error )
62+ metadata = failure_metadata ( error )
63+ base = {
64+ message : client_message_for ( error ) ,
65+ code : error_code_for ( error ) ,
66+ kind : metadata [ :kind ] ,
67+ retryable : metadata [ :retryable ] ,
68+ next_action : metadata [ :next_action ] ,
69+ retry_action : metadata [ :retry_action ]
70+ }
71+ metadata [ :next_strategy ] ? base . merge ( next_strategy : metadata [ :next_strategy ] ) : base
72+ end
73+
74+ # Builds a warning entry for readiness/status responses.
75+ #
76+ # @param code [String]
77+ # @param message [String]
78+ # @param retryable [Boolean]
79+ # @param next_action [String]
80+ # @return [Hash{Symbol=>Object}] structured warning payload.
81+ def warning ( code :, message :, retryable :, next_action :)
82+ {
83+ code : code ,
84+ message : message ,
85+ retryable : retryable ,
86+ next_action : next_action
87+ }
88+ end
89+
90+ private
91+
92+ # @param error [StandardError]
93+ # @return [Hash{Symbol=>Object}]
94+ def failure_metadata ( error )
95+ case error
96+ when Html2rss ::Web ::AutoSourceDisabledError , Html2rss ::Web ::HealthCheckFailedError
97+ non_retryable_server_failure_metadata
98+ when Html2rss ::Web ::UnauthorizedError then auth_failure_metadata
99+ when Html2rss ::Web ::BadRequestError , Html2rss ::Web ::ForbiddenError then input_failure_metadata
100+ else
101+ generic_failure_metadata ( error )
102+ end
103+ end
104+
105+ # @param error [StandardError]
106+ # @return [Hash{Symbol=>Object}]
107+ def generic_failure_metadata ( error )
108+ kind = Html2rss ::Web ::ErrorClassification . network_error? ( error ) ? :network : :server
109+ {
110+ kind : ERROR_KINDS [ kind ] ,
111+ retryable : true ,
112+ next_action : NEXT_ACTIONS [ :retry ] ,
113+ retry_action : RETRY_ACTIONS [ :primary ]
114+ }
115+ end
116+
117+ # @return [Hash{Symbol=>Object}]
118+ def auth_failure_metadata
119+ {
120+ kind : ERROR_KINDS [ :auth ] ,
121+ retryable : false ,
122+ next_action : NEXT_ACTIONS [ :enter_token ] ,
123+ retry_action : RETRY_ACTIONS [ :none ]
124+ }
125+ end
126+
127+ # @return [Hash{Symbol=>Object}]
128+ def input_failure_metadata
129+ {
130+ kind : ERROR_KINDS [ :input ] ,
131+ retryable : false ,
132+ next_action : NEXT_ACTIONS [ :correct_input ] ,
133+ retry_action : RETRY_ACTIONS [ :none ]
134+ }
135+ end
136+
137+ # @return [Hash{Symbol=>Object}]
138+ def non_retryable_server_failure_metadata
139+ {
140+ kind : ERROR_KINDS [ :server ] ,
141+ retryable : false ,
142+ next_action : NEXT_ACTIONS [ :none ] ,
143+ retry_action : RETRY_ACTIONS [ :none ]
144+ }
145+ end
146+
147+ # @param error [StandardError]
148+ # @return [String]
149+ def client_message_for ( error )
150+ error . is_a? ( Html2rss ::Web ::HttpError ) ? error . message : Html2rss ::Web ::HttpError ::DEFAULT_MESSAGE
151+ end
152+
153+ # @param error [StandardError]
154+ # @return [String]
155+ def error_code_for ( error )
156+ error . respond_to? ( :code ) ? error . code : CODES [ :internal_server_error ]
157+ end
158+ end
18159 end
19160 end
20161 end
0 commit comments