@@ -105,24 +105,45 @@ class SecurityRequirement(BaseModel):
105105 }
106106
107107
108+ class RoleRequirement (BaseModel ):
109+ """
110+ OBP Role requirement for an endpoint.
111+
112+ Represents a required role/entitlement to access the endpoint.
113+ """
114+ role : str = Field (..., description = "The role/entitlement name" )
115+ requires_bank_id : bool = Field (default = False , description = "Whether this role requires a bank_id context" )
116+
117+ model_config = {
118+ "extra" : "ignore" ,
119+ }
120+
121+
108122class EndpointSchema (BaseModel ):
109123 """
110- Full OpenAPI schema for an endpoint.
124+ Full schema for an endpoint.
111125
112126 Contains complete specification including parameters, request body,
113- responses, and security requirements.
127+ responses, security requirements, and roles from OBP resource docs .
114128 """
115129 path : str = Field (..., description = "API path template" )
116130 method : HttpMethod = Field (..., description = "HTTP method" )
117131 operation_id : str = Field (default = "" , description = "OpenAPI operationId" )
118132 summary : str = Field (default = "" , description = "Brief description" )
119133 description : str = Field (default = "" , description = "Detailed description (may contain HTML)" )
134+ description_markdown : str = Field (default = "" , description = "Description in markdown format" )
120135 tags : List [str ] = Field (default_factory = list , description = "Tags for categorization" )
121136 parameters : List [EndpointParameter ] = Field (default_factory = list , description = "Endpoint parameters" )
122137 requestBody : Optional [Dict [str , Any ]] = Field (default = None , description = "Request body specification" )
123138 responses : Dict [str , EndpointResponse ] = Field (default_factory = dict , description = "Response specifications by status code" )
139+ success_response_body : Optional [Dict [str , Any ]] = Field (default = None , description = "Example success response body" )
140+ error_response_bodies : List [str ] = Field (default_factory = list , description = "Possible error response messages" )
141+ typed_success_response_body : Optional [Dict [str , Any ]] = Field (default = None , description = "JSON Schema for success response" )
124142 security : List [Dict [str , Any ]] = Field (default_factory = list , description = "Security requirements" )
125- roles : List [str ] = Field (default_factory = list , description = "Required roles" )
143+ roles : List [RoleRequirement ] = Field (default_factory = list , description = "Required roles/entitlements" )
144+ is_featured : bool = Field (default = False , description = "Whether this endpoint is featured" )
145+ special_instructions : str = Field (default = "" , description = "Special instructions for using this endpoint" )
146+ connector_methods : List [str ] = Field (default_factory = list , description = "Related connector methods" )
126147
127148 model_config = {
128149 "extra" : "ignore" ,
@@ -150,18 +171,33 @@ def from_raw(cls, data: Dict[str, Any]) -> "EndpointSchema":
150171 if isinstance (param_data , dict ):
151172 parameters .append (EndpointParameter (** param_data ))
152173
174+ # Convert roles to RoleRequirement models
175+ roles = []
176+ for role_data in data .get ("roles" , []):
177+ if isinstance (role_data , dict ):
178+ roles .append (RoleRequirement (** role_data ))
179+ elif isinstance (role_data , str ):
180+ roles .append (RoleRequirement (role = role_data ))
181+
153182 return cls (
154183 path = data .get ("path" , "" ),
155184 method = HttpMethod (data .get ("method" , "GET" ).upper ()),
156185 operation_id = data .get ("operation_id" , "" ),
157186 summary = data .get ("summary" , "" ),
158187 description = data .get ("description" , "" ),
188+ description_markdown = data .get ("description_markdown" , "" ),
159189 tags = data .get ("tags" , []),
160190 parameters = parameters ,
161191 requestBody = data .get ("requestBody" ),
162192 responses = responses ,
193+ success_response_body = data .get ("success_response_body" ),
194+ error_response_bodies = data .get ("error_response_bodies" , []),
195+ typed_success_response_body = data .get ("typed_success_response_body" ),
163196 security = data .get ("security" , []),
164- roles = data .get ("roles" , []),
197+ roles = roles ,
198+ is_featured = data .get ("is_featured" , False ),
199+ special_instructions = data .get ("special_instructions" , "" ),
200+ connector_methods = data .get ("connector_methods" , []),
165201 )
166202
167203
@@ -330,6 +366,88 @@ def build_index_from_swagger(self, swagger_data: Dict[str, Any]) -> None:
330366 self ._save_schemas ()
331367 self ._schemas_loaded = True
332368
369+ def build_index_from_resource_docs (self , resource_docs_data : Dict [str , Any ]) -> None :
370+ """
371+ Build the lightweight endpoint index and full schemas from OBP resource docs.
372+
373+ Resource docs provide richer information than swagger, including:
374+ - Roles/entitlements required for each endpoint
375+ - Markdown descriptions
376+ - Example success/error response bodies
377+ - Typed response schemas
378+ - Connector methods
379+
380+ Args:
381+ resource_docs_data: Resource docs response from OBP API
382+ """
383+ self ._index = {}
384+ self ._schemas = {}
385+ self ._index_models = {}
386+ self ._schema_models = {}
387+
388+ resource_docs = resource_docs_data .get ("resource_docs" , [])
389+
390+ logger .info (f"Building index from { len (resource_docs )} resource docs..." )
391+
392+ for doc in resource_docs :
393+ if not isinstance (doc , dict ):
394+ continue
395+
396+ # Extract fields from resource doc format
397+ operation_id = doc .get ("operation_id" , "" )
398+ method = doc .get ("request_verb" , "GET" ).upper ()
399+ path = doc .get ("request_url" , "" )
400+ summary = doc .get ("summary" , "" )
401+ tags = doc .get ("tags" , [])
402+
403+ # Use operation_id as the endpoint ID (fallback to generated ID if not present)
404+ if operation_id :
405+ endpoint_id = operation_id
406+ else :
407+ endpoint_id = self ._generate_endpoint_id (method , path )
408+ logger .warning (f"No operation_id for { method } { path } , using generated ID: { endpoint_id } " )
409+
410+ # Store lightweight index entry
411+ self ._index [endpoint_id ] = {
412+ "id" : endpoint_id ,
413+ "method" : method ,
414+ "path" : path ,
415+ "operation_id" : operation_id ,
416+ "summary" : summary ,
417+ "tags" : tags
418+ }
419+
420+ # Extract roles from resource docs format
421+ roles = doc .get ("roles" , [])
422+
423+ # Store full schema separately (keyed by endpoint_id)
424+ self ._schemas [endpoint_id ] = {
425+ "path" : path ,
426+ "method" : method ,
427+ "operation_id" : operation_id ,
428+ "summary" : summary ,
429+ "description" : doc .get ("description" , "" ),
430+ "description_markdown" : doc .get ("description_markdown" , "" ),
431+ "tags" : tags ,
432+ "parameters" : [], # Resource docs don't provide parameters in the same way
433+ "requestBody" : None ,
434+ "responses" : {},
435+ "success_response_body" : doc .get ("success_response_body" ),
436+ "error_response_bodies" : doc .get ("error_response_bodies" , []),
437+ "typed_success_response_body" : doc .get ("typed_success_response_body" ),
438+ "security" : [],
439+ "roles" : roles ,
440+ "is_featured" : doc .get ("is_featured" , False ),
441+ "special_instructions" : doc .get ("special_instructions" , "" ),
442+ "connector_methods" : doc .get ("connector_methods" , []),
443+ }
444+
445+ logger .info (f"Built index with { len (self ._index )} endpoints" )
446+ logger .info (f"Built schemas for { len (self ._schemas )} endpoints" )
447+ self ._save_index ()
448+ self ._save_schemas ()
449+ self ._schemas_loaded = True
450+
333451 def _generate_endpoint_id (self , method : str , path : str ) -> str :
334452 """Generate a unique ID for an endpoint."""
335453 # Clean the path for use in ID
0 commit comments