Coverage for src/lexigram/admin/services/form_renderer.py: 0%

28 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-24 23:18 +0800

1"""Form Renderer Service for Lexigram Admin. 

2 

3Provides a unified interface for rendering schema-driven forms with 

4built-in RBAC enforcement. 

5""" 

6 

7from __future__ import annotations 

8 

9from dataclasses import replace as dc_replace 

10from typing import Any 

11 

12from lexigram.admin.forms.components import FormSchema, FormSchemaGenerator 

13from lexigram.admin.rbac.service import PermissionService 

14from lexigram.admin.ui.organisms.dynamic_form import DynamicForm 

15from lexigram.di.decorators import inject 

16 

17 

18@inject 

19class FormRenderer: 

20 """Service for rendering forms with RBAC integration. 

21 

22 Orchestrates schema generation, permission filtering, and 

23 component instantiation. 

24 """ 

25 

26 def __init__( 

27 self, 

28 generator: FormSchemaGenerator, 

29 permission_service: PermissionService | None = None, 

30 ) -> None: 

31 self.generator = generator 

32 self.permission_service = permission_service 

33 

34 async def render_form( 

35 self, 

36 model: type | None = None, 

37 schema: FormSchema | None = None, 

38 user: Any = None, 

39 resource_name: str = "unknown", 

40 action: str = "", 

41 method: str = "POST", 

42 initial_data: dict[str, Any] | None = None, 

43 submit_text: str = "Submit", 

44 hx_post: str | None = None, 

45 hx_target: str | None = None, 

46 hx_swap: str = "outerHTML", 

47 ) -> DynamicForm: 

48 """Render a form for the given model/schema and user context. 

49 

50 Args: 

51 model: Pydantic model class to generate schema from. 

52 schema: Explicit FormSchema (overrides model). 

53 user: Current user for RBAC filtering. 

54 resource_name: Name of the resource for RBAC checks. 

55 action: Form action URL. 

56 method: HTTP method. 

57 initial_data: Initial values for the form. 

58 submit_text: Text for the submit button. 

59 hx_post: HTMX post URL. 

60 hx_target: HTMX target selector. 

61 hx_swap: HTMX swap strategy. 

62 

63 Returns: 

64 A DynamicForm component instance ready for rendering. 

65 """ 

66 # 1. Resolve base schema 

67 if schema: 

68 base_schema = schema 

69 elif model: 

70 base_schema = self.generator.from_pydantic(model) 

71 else: 

72 base_schema = FormSchema() 

73 

74 # Update resource name in schema if provided 

75 if resource_name != "unknown": 

76 base_schema.resource_name = resource_name 

77 

78 # 2. Apply RBAC filtering if user is provided 

79 if user: 

80 form_schema = await base_schema.filter_for_user( 

81 user, 

82 resource_name, 

83 self.permission_service, 

84 ) 

85 else: 

86 form_schema = base_schema 

87 

88 # 3. Apply initial data as defaults if provided 

89 if initial_data: 

90 for idx, field in enumerate(form_schema.fields): 

91 if field.name in initial_data: 

92 form_schema.fields[idx] = dc_replace( 

93 field, default=initial_data[field.name] 

94 ) 

95 

96 # 4. Create and return the DynamicForm 

97 return DynamicForm( 

98 schema=form_schema, 

99 action=action, 

100 method=method, 

101 submit_text=submit_text, 

102 hx_post=hx_post, 

103 hx_target=hx_target, 

104 hx_swap=hx_swap, 

105 )