diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..d73ca7c1394 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +accelerators/digit_client/digit_client/services/__pycache__ +accelerators/digit_client/digit_client/models/__pycache__ +accelerators/digit_client/digit_client/__pycache__ +accelerators/authenticate/__pycache__ +accelerators/digit_client/dist +accelerators/digit_client/tests/__pycache__ +accelerators/digit_client/digit_client/__pycache__ +accelerators/digit_client/build +accelerators/digit_client/build/lib diff --git a/accelerators/digit_client/build/lib/digit_client/auth.py b/accelerators/authenticate/auth.py similarity index 100% rename from accelerators/digit_client/build/lib/digit_client/auth.py rename to accelerators/authenticate/auth.py diff --git a/accelerators/digit_client/build/lib/digit_client/pre_install.py b/accelerators/authenticate/pre_install.py similarity index 99% rename from accelerators/digit_client/build/lib/digit_client/pre_install.py rename to accelerators/authenticate/pre_install.py index 3d151116a85..30ea87ea1bb 100644 --- a/accelerators/digit_client/build/lib/digit_client/pre_install.py +++ b/accelerators/authenticate/pre_install.py @@ -7,7 +7,7 @@ if current_dir not in sys.path: sys.path.insert(0, current_dir) -from .auth import DigitAuth +from auth import DigitAuth def get_user_choice(): print("\n=== DIGIT Client Authentication ===") diff --git a/accelerators/digit_client/build/lib/digit_client/__init__.py b/accelerators/digit_client/build/lib/digit_client/__init__.py index 2345eeff1d3..4328e62888a 100644 --- a/accelerators/digit_client/build/lib/digit_client/__init__.py +++ b/accelerators/digit_client/build/lib/digit_client/__init__.py @@ -1,52 +1,107 @@ -# __init__.py for digit_client package -import os -import sys -from pathlib import Path +""" +DIGIT Client Library for Python +""" -# Store auth file in the package directory instead of home -PACKAGE_DIR = os.path.dirname(os.path.abspath(__file__)) -AUTH_FILE = os.path.join(PACKAGE_DIR, '.digit_client_auth') +__version__ = "0.1.0" -def check_auth(): - """Check if authentication has been done""" - try: - return os.path.exists(AUTH_FILE) - except Exception: - return False - -def perform_auth(): - """Perform authentication""" - try: - # Ensure we import from the correct path - # sys.path.insert(0, os.path.dirname(PACKAGE_DIR)) - from digit_client.pre_install import main as auth_main - - success, access_token = auth_main() - if success and access_token: - # Create auth file after successful authentication - try: - # with open(AUTH_FILE, 'w') as f: - # f.write(access_token) - return True - except Exception as e: - print(f"Failed to create auth file: {e}") - return False - return False - except Exception as e: - print(f"Authentication error: {e}") - return False - -print("Initializing DIGIT Client...") -# Check authentication on import -if not check_auth(): - print("\nAuthentication required for DIGIT Client") - if not perform_auth(): - print("Authentication failed. Package cannot be used.") - sys.exit(1) - print("Authentication successful!") - -# Only import DigitAuth after successful authentication -from .auth import DigitAuth - -__version__ = '0.1' -__all__ = ['DigitAuth'] \ No newline at end of file +from .api_client import APIClient +from .config import Config +from .services import AuthenticationService, UserService, MDMSService, MDMSV2Service, AuthorizeService, WorkflowV2Service +from .request_config import RequestConfig, RequestInfo, RequestInfoBuilder +from .models.citizen_user import CitizenUser, Role, CitizenUserBuilder +from .models.search_models import UserSearchModel, UserSearchModelBuilder +from .models.user import User,UserBuilder +from .models.auth import AuthenticationRequest, AuthenticationRequestBuilder +from .models.mdms import ( + MdmsCriteriaReq, + MdmsCriteriaReqBuilder, + MdmsCriteria, + MdmsCriteriaBuilder, + ModuleDetail, + ModuleDetailBuilder, + MasterDetail, + MasterDetailBuilder +) +from .models.mdms_v2 import ( + SchemaDefinition, + SchemaDefinitionBuilder, + SchemaDefCriteria, + SchemaDefCriteriaBuilder, + AuditDetails, + AuditDetailsBuilder, + Mdms, + MdmsBuilder, + MdmsCriteriaV2, + MdmsCriteriaV2Builder, +) +from .models.workflow import ( + Document, + DocumentBuilder, + WorkflowAction, + WorkflowActionBuilder, + State, + StateBuilder, + ProcessInstance, + ProcessInstanceBuilder, + ProcessInstanceSearchCriteria, + ProcessInstanceSearchCriteriaBuilder, +) +from .models.AuthorizationRequest import AuthorizationRequest, AuthorizationRequestBuilder, Role, RoleBuilder +from .models.ActionRequest import ActionRequest, ActionBuilder, ActionRequestBuilder, Action +__all__ = [ + 'APIClient', + 'Config', + 'AuthenticationService', + 'UserService', + 'MDMSService', + 'MDMSV2Service', + 'AuthorizeService', + 'WorkflowV2Service', + 'RequestConfig', + 'RequestInfo', + 'RequestInfoBuilder', + 'CitizenUser', + 'Role', + 'UserSearchModel', + 'UserSearchModelBuilder', + 'User', + 'UserBuilder', + 'CitizenUserBuilder', + 'AuthenticationRequest', + 'AuthenticationRequestBuilder', + 'MdmsCriteriaReq', + 'MdmsCriteriaReqBuilder', + 'MdmsCriteria', + 'MdmsCriteriaBuilder', + 'ModuleDetail', + 'ModuleDetailBuilder', + 'MasterDetail', + 'MasterDetailBuilder', + 'SchemaDefinition', + 'SchemaDefinitionBuilder', + 'SchemaDefCriteria', + 'SchemaDefCriteriaBuilder', + 'AuditDetails', + 'AuditDetailsBuilder', + 'Mdms', + 'MdmsBuilder', + 'MdmsCriteriaV2', + 'MdmsCriteriaV2Builder', + 'AuthorizationRequest', + 'AuthorizationRequestBuilder', + 'Role', + 'RoleBuilder', + 'ActionRequest', + 'ActionBuilder', + 'ActionRequestBuilder', + 'WorkflowAction', + 'WorkflowActionBuilder', + 'Document', + 'DocumentBuilder', + 'State', + 'StateBuilder', + 'ProcessInstance', + 'ProcessInstanceBuilder', + 'ProcessInstanceSearchCriteria', + 'ProcessInstanceSearchCriteriaBuilder', +] \ No newline at end of file diff --git a/accelerators/digit_client/build/lib/digit_client/api_client.py b/accelerators/digit_client/build/lib/digit_client/api_client.py index a28a964c9db..3824e7b0931 100644 --- a/accelerators/digit_client/build/lib/digit_client/api_client.py +++ b/accelerators/digit_client/build/lib/digit_client/api_client.py @@ -11,9 +11,36 @@ def get(self, endpoint, params=None): response = requests.get(f"{self.base_url}/{endpoint}", headers=headers, params=params) return response.json() - def post(self, endpoint, json_data): - headers = {'Authorization': f'Bearer {self.auth_token}'} - response = requests.post(f"{self.base_url}/{endpoint}", headers=headers, json=json_data) + def post(self, endpoint, json_data=None, data=None, additional_headers=None, params=None): + """ + Make a POST request + + Args: + endpoint (str): API endpoint + json_data (dict, optional): JSON data to send + data (dict, optional): Form data to send + additional_headers (dict, optional): Additional headers to include + params (dict, optional): Query parameters to include in the URL + + Returns: + dict: Response JSON + """ + # headers = {'Authorization': f'Bearer {self.auth_token}'} + headers = {'Content-Type': 'application/json'} + if additional_headers: + headers.update(additional_headers) + print(headers) + print(json_data) + print(data) + print(params) + + response = requests.post( + f"{self.base_url}/{endpoint}", + headers=headers, + json=json_data if json_data is not None else None, + data=data if data is not None else None, + params=params if params is not None else None + ) return response.json() # Add more HTTP methods as needed \ No newline at end of file diff --git a/accelerators/digit_client/build/lib/digit_client/config.py b/accelerators/digit_client/build/lib/digit_client/config.py index 561d416ea85..29fe688f287 100644 --- a/accelerators/digit_client/build/lib/digit_client/config.py +++ b/accelerators/digit_client/build/lib/digit_client/config.py @@ -1,3 +1,13 @@ class Config: - API_ENDPOINT = "https://example.com/api" # Set your API endpoint - AUTH_TOKEN = "your_auth_token" # Set your authentication token \ No newline at end of file + API_ENDPOINT = "https://sandbox.digit.org" # Sandbox API endpoint + AUTH_TOKEN = None # Will be set during runtime + + @classmethod + def set_auth_token(cls, token: str): + """Set the authentication token for API requests""" + cls.AUTH_TOKEN = token + + @classmethod + def set_api_endpoint(cls, endpoint: str): + """Set the API endpoint (useful for switching between environments)""" + cls.API_ENDPOINT = endpoint \ No newline at end of file diff --git a/accelerators/digit_client/build/lib/digit_client/models/__init__.py b/accelerators/digit_client/build/lib/digit_client/models/__init__.py index 9db2b1183d3..06c33192c2c 100644 --- a/accelerators/digit_client/build/lib/digit_client/models/__init__.py +++ b/accelerators/digit_client/build/lib/digit_client/models/__init__.py @@ -1 +1,66 @@ -# __init__.py for models package \ No newline at end of file +# __init__.py for models package +from .user import User, UserBuilder +from .AuthorizationRequest import Role, RoleBuilder, AuthorizationRequest, AuthorizationRequestBuilder +from .ActionRequest import ActionRequest, ActionBuilder, ActionRequestBuilder, Action +from .citizen_user import CitizenUser, CitizenUserBuilder +from .mdms import MdmsCriteria, MdmsCriteriaBuilder, MdmsCriteriaReq, MdmsCriteriaReqBuilder, MasterDetailBuilder, ModuleDetailBuilder, ModuleDetail, MasterDetail +from .search_models import UserSearchModel, UserSearchModelBuilder +from .mdms_v2 import ( + SchemaDefinition, + SchemaDefinitionBuilder, + SchemaDefCriteria, + SchemaDefCriteriaBuilder, + AuditDetails, + AuditDetailsBuilder, + Mdms, + MdmsBuilder, + MdmsCriteriaV2, + MdmsCriteriaV2Builder, +) +from .workflow import Document, DocumentBuilder, WorkflowAction, WorkflowActionBuilder, State, StateBuilder, ProcessInstance, ProcessInstanceBuilder, ProcessInstanceSearchCriteria, ProcessInstanceSearchCriteriaBuilder + + +__all__ = ['Role', + 'RoleBuilder', + 'AuthorizationRequest', + 'AuthorizationRequestBuilder', + 'ActionRequest', + 'ActionBuilder', + 'ActionRequestBuilder', + 'Action', + 'User', + 'UserBuilder', + 'CitizenUser', + 'CitizenUserBuilder', + 'MdmsCriteria', + 'MdmsCriteriaBuilder', + 'UserSearchModel', + 'UserSearchModelBuilder', + 'AuditDetails', + 'SchemaDefinition', + 'SchemaDefinitionRequest', + 'SchemaDefCriteria', + 'Mdms', + 'MdmsCriteriaV2', + 'MdmsBuilder', + 'MdmsCriteriaV2Builder', + 'SchemaDefinitionBuilder', + 'SchemaDefinitionRequestBuilder', + 'SchemaDefCriteriaBuilder', + 'AuditDetailsBuilder', + 'MasterDetail', + 'MasterDetailBuilder', + 'ModuleDetail', + 'ModuleDetailBuilder', + 'MdmsCriteriaReq', + 'MdmsCriteriaReqBuilder', + 'Document', + 'DocumentBuilder', + 'WorkflowAction', + 'WorkflowActionBuilder', + 'State', + 'StateBuilder', + 'ProcessInstance', + 'ProcessInstanceBuilder', + 'ProcessInstanceSearchCriteria', + 'ProcessInstanceSearchCriteriaBuilder'] \ No newline at end of file diff --git a/accelerators/digit_client/build/lib/digit_client/models/citizen_user.py b/accelerators/digit_client/build/lib/digit_client/models/citizen_user.py new file mode 100644 index 00000000000..8df9481d42b --- /dev/null +++ b/accelerators/digit_client/build/lib/digit_client/models/citizen_user.py @@ -0,0 +1,305 @@ +from typing import List, Optional +from dataclasses import dataclass +from copy import deepcopy +from datetime import datetime + +@dataclass +class Role: + code: str + name: str + tenant_id: str + + def to_dict(self) -> dict: + return { + "code": self.code, + "name": self.name, + "tenantId": self.tenant_id + } + +@dataclass +class CitizenUser: + user_name: str + password: str + salutation: str + name: str + gender: str + mobile_number: str + email_id: str + tenant_id: str + roles: List[Role] + alt_contact_number: Optional[str] = "" + pan: Optional[str] = None + aadhaar_number: Optional[str] = None + permanent_address: Optional[str] = None + permanent_city: Optional[str] = None + permanent_pincode: Optional[str] = None + correspondence_city: Optional[str] = None + correspondence_pincode: Optional[str] = None + correspondence_address: Optional[str] = None + active: bool = True + dob: Optional[str] = None + pwd_expiry_date: Optional[str] = None + locale: str = "en_IN" + type: str = "CITIZEN" + signature: Optional[str] = None + account_locked: bool = False + father_or_husband_name: Optional[str] = None + blood_group: Optional[str] = None + identification_mark: Optional[str] = None + photo: Optional[str] = None + otp_reference: Optional[str] = None + + def to_dict(self) -> dict: + return { + "userName": self.user_name, + "password": self.password, + "salutation": self.salutation, + "name": self.name, + "gender": self.gender, + "mobileNumber": self.mobile_number, + "emailId": self.email_id, + "altContactNumber": self.alt_contact_number, + "pan": self.pan, + "aadhaarNumber": self.aadhaar_number, + "permanentAddress": self.permanent_address, + "permanentCity": self.permanent_city, + "permanentPincode": self.permanent_pincode, + "correspondenceCity": self.correspondence_city, + "correspondencePincode": self.correspondence_pincode, + "correspondenceAddress": self.correspondence_address, + "active": self.active, + "dob": self.dob, + "pwdExpiryDate": self.pwd_expiry_date, + "locale": self.locale, + "type": self.type, + "signature": self.signature, + "accountLocked": self.account_locked, + "fatherOrHusbandName": self.father_or_husband_name, + "bloodGroup": self.blood_group, + "identificationMark": self.identification_mark, + "photo": self.photo, + "otpReference": self.otp_reference, + "tenantId": self.tenant_id, + "roles": [role.to_dict() for role in self.roles] + } + +class CitizenUserBuilder: + """Builder class for creating CitizenUser objects""" + + def __init__(self): + # Required fields + self._user_name: Optional[str] = None + self._password: Optional[str] = None + self._name: Optional[str] = None + self._gender: Optional[str] = None + self._mobile_number: Optional[str] = None + self._tenant_id: Optional[str] = None + self._roles: List[Role] = [] + + # Optional fields with defaults + self._active: bool = True + self._locale: str = "en_IN" + self._type: str = "CITIZEN" + self._account_locked: bool = False + + # Optional fields + self._salutation: Optional[str] = None + self._email_id: Optional[str] = None + self._alt_contact_number: Optional[str] = None + self._pan: Optional[str] = None + self._aadhaar_number: Optional[str] = None + self._permanent_address: Optional[str] = None + self._permanent_city: Optional[str] = None + self._permanent_pincode: Optional[str] = None + self._correspondence_city: Optional[str] = None + self._correspondence_pincode: Optional[str] = None + self._correspondence_address: Optional[str] = None + self._dob: Optional[str] = None + self._pwd_expiry_date: Optional[str] = None + self._signature: Optional[str] = None + self._father_or_husband_name: Optional[str] = None + self._blood_group: Optional[str] = None + self._identification_mark: Optional[str] = None + self._photo: Optional[str] = None + self._otp_reference: Optional[str] = None + + def with_user_name(self, user_name: str) -> 'CitizenUserBuilder': + self._user_name = user_name + return self + + def with_password(self, password: str) -> 'CitizenUserBuilder': + self._password = password + return self + + def with_name(self, name: str) -> 'CitizenUserBuilder': + self._name = name + return self + + def with_gender(self, gender: str) -> 'CitizenUserBuilder': + self._gender = gender + return self + + def with_mobile_number(self, mobile_number: str) -> 'CitizenUserBuilder': + self._mobile_number = mobile_number + return self + + def with_tenant_id(self, tenant_id: str) -> 'CitizenUserBuilder': + self._tenant_id = tenant_id + return self + + def with_role(self, role: Role) -> 'CitizenUserBuilder': + self._roles.append(deepcopy(role)) + return self + + def with_roles(self, roles: List[Role]) -> 'CitizenUserBuilder': + self._roles.extend(deepcopy(roles)) + return self + + def with_salutation(self, salutation: str) -> 'CitizenUserBuilder': + self._salutation = salutation + return self + + def with_email(self, email_id: str) -> 'CitizenUserBuilder': + self._email_id = email_id + return self + + def with_alt_contact_number(self, alt_contact_number: str) -> 'CitizenUserBuilder': + self._alt_contact_number = alt_contact_number + return self + + def with_pan(self, pan: str) -> 'CitizenUserBuilder': + self._pan = pan + return self + + def with_aadhaar(self, aadhaar_number: str) -> 'CitizenUserBuilder': + self._aadhaar_number = aadhaar_number + return self + + def with_permanent_address(self, address: str) -> 'CitizenUserBuilder': + self._permanent_address = address + return self + + def with_permanent_city(self, city: str) -> 'CitizenUserBuilder': + self._permanent_city = city + return self + + def with_permanent_pincode(self, pincode: str) -> 'CitizenUserBuilder': + self._permanent_pincode = pincode + return self + + def with_correspondence_city(self, city: str) -> 'CitizenUserBuilder': + self._correspondence_city = city + return self + + def with_correspondence_pincode(self, pincode: str) -> 'CitizenUserBuilder': + self._correspondence_pincode = pincode + return self + + def with_correspondence_address(self, address: str) -> 'CitizenUserBuilder': + self._correspondence_address = address + return self + + def with_active(self, active: bool) -> 'CitizenUserBuilder': + self._active = active + return self + + def with_dob(self, dob: str) -> 'CitizenUserBuilder': + self._dob = dob + return self + + def with_pwd_expiry_date(self, expiry_date: str) -> 'CitizenUserBuilder': + self._pwd_expiry_date = expiry_date + return self + + def with_locale(self, locale: str) -> 'CitizenUserBuilder': + self._locale = locale + return self + + def with_type(self, type: str) -> 'CitizenUserBuilder': + self._type = type + return self + + def with_signature(self, signature: str) -> 'CitizenUserBuilder': + self._signature = signature + return self + + def with_account_locked(self, locked: bool) -> 'CitizenUserBuilder': + self._account_locked = locked + return self + + def with_father_or_husband_name(self, name: str) -> 'CitizenUserBuilder': + self._father_or_husband_name = name + return self + + def with_blood_group(self, blood_group: str) -> 'CitizenUserBuilder': + self._blood_group = blood_group + return self + + def with_identification_mark(self, mark: str) -> 'CitizenUserBuilder': + self._identification_mark = mark + return self + + def with_photo(self, photo: str) -> 'CitizenUserBuilder': + self._photo = photo + return self + + def with_otp_reference(self, otp_reference: str) -> 'CitizenUserBuilder': + self._otp_reference = otp_reference + return self + + def build(self) -> CitizenUser: + """Build and validate the CitizenUser object""" + # Validate required fields + required_fields = { + 'user_name': self._user_name, + 'password': self._password, + 'name': self._name, + 'gender': self._gender, + 'mobile_number': self._mobile_number, + 'tenant_id': self._tenant_id + } + + missing_fields = [field for field, value in required_fields.items() if value is None] + if missing_fields: + raise ValueError(f"Missing required fields: {', '.join(missing_fields)}") + + if not self._roles: + # Add default CITIZEN role if none provided + self._roles.append(Role( + code="CITIZEN", + name="Citizen", + tenant_id=self._tenant_id + )) + + return CitizenUser( + user_name=self._user_name, + password=self._password, + salutation=self._salutation, + name=self._name, + gender=self._gender, + mobile_number=self._mobile_number, + email_id=self._email_id, + tenant_id=self._tenant_id, + roles=self._roles, + alt_contact_number=self._alt_contact_number, + pan=self._pan, + aadhaar_number=self._aadhaar_number, + permanent_address=self._permanent_address, + permanent_city=self._permanent_city, + permanent_pincode=self._permanent_pincode, + correspondence_city=self._correspondence_city, + correspondence_pincode=self._correspondence_pincode, + correspondence_address=self._correspondence_address, + active=self._active, + dob=self._dob, + pwd_expiry_date=self._pwd_expiry_date, + locale=self._locale, + type=self._type, + signature=self._signature, + account_locked=self._account_locked, + father_or_husband_name=self._father_or_husband_name, + blood_group=self._blood_group, + identification_mark=self._identification_mark, + photo=self._photo, + otp_reference=self._otp_reference + ) \ No newline at end of file diff --git a/accelerators/digit_client/build/lib/digit_client/models/search_models.py b/accelerators/digit_client/build/lib/digit_client/models/search_models.py new file mode 100644 index 00000000000..592641f58da --- /dev/null +++ b/accelerators/digit_client/build/lib/digit_client/models/search_models.py @@ -0,0 +1,70 @@ +from typing import List, Optional +from dataclasses import dataclass, field + +@dataclass +class UserSearchModel: + """Model for user search criteria""" + tenantId: str # Using exact field names from API + userType: Optional[str] = None # Changed from type to userType + active: Optional[bool] = None + uuid: Optional[List[str]] = None + userName: Optional[str] = None # Changed from user_name to userName + + def to_dict(self) -> dict: + """Convert the search model to a dictionary for API request""" + search_dict = { + "tenantId": self.tenantId + } + + if self.userType is not None: + search_dict["userType"] = self.userType + + if self.active is not None: + search_dict["active"] = str(self.active).lower() # Convert to "true" or "false" string + + if self.uuid: + search_dict["uuid"] = self.uuid + + if self.userName: + search_dict["userName"] = self.userName + + return search_dict + +class UserSearchModelBuilder: + def __init__(self): + self._tenant_id = None + self._user_type = None + self._active = None + self._uuid = None + self._user_name = None + + def with_tenant_id(self, tenant_id: str) -> 'UserSearchModelBuilder': + self._tenant_id = tenant_id + return self + + def with_user_type(self, user_type: str) -> 'UserSearchModelBuilder': + self._user_type = user_type + return self + + def with_active(self, active: bool) -> 'UserSearchModelBuilder': + self._active = active + return self + + def with_uuid(self, uuid: List[str]) -> 'UserSearchModelBuilder': + self._uuid = uuid + return self + + def with_user_name(self, user_name: str) -> 'UserSearchModelBuilder': + self._user_name = user_name + return self + + def build(self) -> UserSearchModel: + return UserSearchModel( + tenantId=self._tenant_id, + userType=self._user_type, + active=self._active, + uuid=self._uuid, + userName=self._user_name + ) + + diff --git a/accelerators/digit_client/build/lib/digit_client/models/user.py b/accelerators/digit_client/build/lib/digit_client/models/user.py index a535f2ed7c1..646bfb038c7 100644 --- a/accelerators/digit_client/build/lib/digit_client/models/user.py +++ b/accelerators/digit_client/build/lib/digit_client/models/user.py @@ -1,12 +1,113 @@ +from typing import List, Optional +from dataclasses import dataclass +from copy import deepcopy +from .AuthorizationRequest import Role + +# @dataclass +# class Role: +# name: str +# code: str +# tenant_id: str + +# def to_dict(self) -> dict: +# return { +# "name": self.name, +# "code": self.code, +# "tenantId": self.tenant_id +# } + +@dataclass class User: - def __init__(self, name, mobile_number, email): - self.name = name - self.mobile_number = mobile_number - self.email = email + id: int + uuid: str + user_name: str + name: str + mobile_number: str + email_id: str + type: str + roles: List[Role] + tenant_id: str - def to_dict(self): + def to_dict(self) -> dict: return { + "id": self.id, + "uuid": self.uuid, + "userName": self.user_name, "name": self.name, - "mobile_number": self.mobile_number, - "email": self.email - } \ No newline at end of file + "mobileNumber": self.mobile_number, + "emailId": self.email_id, + "type": self.type, + "roles": [role.to_dict() for role in self.roles], + "tenantId": self.tenant_id + } + +class UserBuilder: + """Builder class for creating UserProfileUpdate objects""" + + def __init__(self): + self._roles: List[Role] = [] + self._id: Optional[int] = None + self._uuid: Optional[str] = None + self._user_name: Optional[str] = None + self._name: Optional[str] = None + self._mobile_number: Optional[str] = None + self._email_id: Optional[str] = None + self._type: str = "CITIZEN" + self._tenant_id: Optional[str] = None + + def with_id(self, id: int) -> 'UserProfileUpdateBuilder': + self._id = id + return self + + def with_uuid(self, uuid: str) -> 'UserProfileUpdateBuilder': + self._uuid = uuid + return self + + def with_user_name(self, user_name: str) -> 'UserProfileUpdateBuilder': + self._user_name = user_name + return self + + def with_name(self, name: str) -> 'UserProfileUpdateBuilder': + self._name = name + return self + + def with_mobile_number(self, mobile_number: str) -> 'UserProfileUpdateBuilder': + self._mobile_number = mobile_number + return self + + def with_email(self, email_id: str) -> 'UserProfileUpdateBuilder': + self._email_id = email_id + return self + + + def with_type(self, type: str) -> 'UserProfileUpdateBuilder': + self._type = type + return self + + def with_role(self, role: Role) -> 'UserProfileUpdateBuilder': + self._roles.append(deepcopy(role)) + return self + + def with_roles(self, roles: List[Role]) -> 'UserProfileUpdateBuilder': + self._roles.extend(deepcopy(roles)) + return self + + def with_tenant_id(self, tenant_id: str) -> 'UserProfileUpdateBuilder': + self._tenant_id = tenant_id + return self + + + def build(self) -> User: + """Build and validate the UserProfileUpdate object""" + + return User( + id=self._id, + uuid=self._uuid, + user_name=self._user_name, + name=self._name, + mobile_number=self._mobile_number, + email_id=self._email_id, + type=self._type, + roles=self._roles, + tenant_id=self._tenant_id + ) \ No newline at end of file diff --git a/accelerators/digit_client/build/lib/digit_client/models/user_profile.py b/accelerators/digit_client/build/lib/digit_client/models/user_profile.py new file mode 100644 index 00000000000..88b2207e952 --- /dev/null +++ b/accelerators/digit_client/build/lib/digit_client/models/user_profile.py @@ -0,0 +1,167 @@ +from typing import List, Optional +from dataclasses import dataclass +from copy import deepcopy + +@dataclass +class UserRole: + name: str + code: str + tenant_id: str + + def to_dict(self) -> dict: + return { + "name": self.name, + "code": self.code, + "tenantId": self.tenant_id + } + +@dataclass +class UserProfileUpdate: + id: int + uuid: str + user_name: str + name: str + mobile_number: str + email_id: str + locale: str + type: str + roles: List[UserRole] + active: bool + tenant_id: str + permanent_city: Optional[str] = None + gender: Optional[str] = None + photo: Optional[str] = None + + def to_dict(self) -> dict: + return { + "id": self.id, + "uuid": self.uuid, + "userName": self.user_name, + "name": self.name, + "mobileNumber": self.mobile_number, + "emailId": self.email_id, + "locale": self.locale, + "type": self.type, + "roles": [role.to_dict() for role in self.roles], + "active": self.active, + "tenantId": self.tenant_id, + "permanentCity": self.permanent_city, + "gender": self.gender, + "photo": self.photo + } + +class UserProfileUpdateBuilder: + """Builder class for creating UserProfileUpdate objects""" + + def __init__(self): + self._roles: List[UserRole] = [] + self._id: Optional[int] = None + self._uuid: Optional[str] = None + self._user_name: Optional[str] = None + self._name: Optional[str] = None + self._mobile_number: Optional[str] = None + self._email_id: Optional[str] = None + self._locale: str = "en_IN" + self._type: str = "CITIZEN" + self._active: bool = True + self._tenant_id: Optional[str] = None + self._permanent_city: Optional[str] = None + self._gender: Optional[str] = None + self._photo: Optional[str] = None + + def with_id(self, id: int) -> 'UserProfileUpdateBuilder': + self._id = id + return self + + def with_uuid(self, uuid: str) -> 'UserProfileUpdateBuilder': + self._uuid = uuid + return self + + def with_user_name(self, user_name: str) -> 'UserProfileUpdateBuilder': + self._user_name = user_name + return self + + def with_name(self, name: str) -> 'UserProfileUpdateBuilder': + self._name = name + return self + + def with_mobile_number(self, mobile_number: str) -> 'UserProfileUpdateBuilder': + self._mobile_number = mobile_number + return self + + def with_email(self, email_id: str) -> 'UserProfileUpdateBuilder': + self._email_id = email_id + return self + + def with_locale(self, locale: str) -> 'UserProfileUpdateBuilder': + self._locale = locale + return self + + def with_type(self, type: str) -> 'UserProfileUpdateBuilder': + self._type = type + return self + + def with_role(self, role: UserRole) -> 'UserProfileUpdateBuilder': + self._roles.append(deepcopy(role)) + return self + + def with_roles(self, roles: List[UserRole]) -> 'UserProfileUpdateBuilder': + self._roles.extend(deepcopy(roles)) + return self + + def with_active(self, active: bool) -> 'UserProfileUpdateBuilder': + self._active = active + return self + + def with_tenant_id(self, tenant_id: str) -> 'UserProfileUpdateBuilder': + self._tenant_id = tenant_id + return self + + def with_permanent_city(self, permanent_city: str) -> 'UserProfileUpdateBuilder': + self._permanent_city = permanent_city + return self + + def with_gender(self, gender: str) -> 'UserProfileUpdateBuilder': + self._gender = gender + return self + + def with_photo(self, photo: str) -> 'UserProfileUpdateBuilder': + self._photo = photo + return self + + def build(self) -> UserProfileUpdate: + """Build and validate the UserProfileUpdate object""" + # Validate required fields + required_fields = { + 'id': self._id, + 'uuid': self._uuid, + 'user_name': self._user_name, + 'name': self._name, + 'mobile_number': self._mobile_number, + 'email_id': self._email_id, + 'tenant_id': self._tenant_id + } + + missing_fields = [field for field, value in required_fields.items() if value is None] + if missing_fields: + raise ValueError(f"Missing required fields: {', '.join(missing_fields)}") + + if not self._roles: + raise ValueError("At least one role is required") + + return UserProfileUpdate( + id=self._id, + uuid=self._uuid, + user_name=self._user_name, + name=self._name, + mobile_number=self._mobile_number, + email_id=self._email_id, + locale=self._locale, + type=self._type, + roles=self._roles, + active=self._active, + tenant_id=self._tenant_id, + permanent_city=self._permanent_city, + gender=self._gender, + photo=self._photo + ) \ No newline at end of file diff --git a/accelerators/digit_client/build/lib/digit_client/request_config.py b/accelerators/digit_client/build/lib/digit_client/request_config.py new file mode 100644 index 00000000000..f283c002b82 --- /dev/null +++ b/accelerators/digit_client/build/lib/digit_client/request_config.py @@ -0,0 +1,458 @@ +from typing import Optional, Dict +import time +import uuid + +class RequestInfo: + def __init__(self, api_id: str, ver: str, ts: int, action: str, + did: str = None, key: str = None, msg_id: str = None, + requester_id: str = None, auth_token: str = None, + user_info: dict = None, correlation_id: str = None): + self.api_id = api_id + self.ver = ver + self.ts = ts + self.action = action + self.did = did + self.key = key + self.msg_id = msg_id or str(uuid.uuid4()) + self.requester_id = requester_id + self.auth_token = auth_token + self.user_info = user_info + self.correlation_id = correlation_id or str(uuid.uuid4()) + + def to_dict(self) -> Dict: + return { + "apiId": self.api_id, + "ver": self.ver, + "ts": self.ts, + "action": self.action, + "did": self.did, + "key": self.key, + "msgId": self.msg_id, + "requesterId": self.requester_id, + "authToken": self.auth_token, + "userInfo": self.user_info, + "correlationId": self.correlation_id + } + + def with_auth_token(self, temp_auth_token: str) -> 'RequestInfo': + """Create a new RequestInfo instance with a temporary auth token""" + request_info_dict = self.to_dict() + request_info_dict["authToken"] = temp_auth_token + return RequestInfo( + api_id=request_info_dict["apiId"], + ver=request_info_dict["ver"], + ts=int(time.time() * 1000), # Update timestamp + action=request_info_dict["action"], + did=request_info_dict["did"], + key=request_info_dict["key"], + msg_id=str(uuid.uuid4()), # New message ID + requester_id=request_info_dict["requesterId"], + auth_token=temp_auth_token, + user_info=request_info_dict["userInfo"], + correlation_id=str(uuid.uuid4()) # New correlation ID + ) + +class RequestInfoBuilder: + """Builder class for creating RequestInfo objects""" + + _default_config = None + + def __init__(self): + # Required fields + self._api_id: Optional[str] = None + self._ver: Optional[str] = None + self._action: Optional[str] = None + + # Optional fields with defaults + self._ts: int = int(time.time() * 1000) + self._did: Optional[str] = None + self._key: Optional[str] = None + self._msg_id: Optional[str] = None + self._requester_id: Optional[str] = None + self._auth_token: Optional[str] = None + self._user_info: Optional[dict] = None + self._correlation_id: Optional[str] = None + + @classmethod + def initialize(cls, + api_id: str = "DIGIT-CLIENT", + version: str = "1.0.0", + auth_token: Optional[str] = None, + user_info: Optional[dict] = None) -> None: + """ + Initialize the default request configuration. + + Args: + api_id (str): The API ID for the application + version (str): API version + auth_token (str, optional): Default authentication token + user_info (dict, optional): User information dictionary + """ + cls._default_config = { + "api_id": api_id, + "ver": version, + "auth_token": auth_token, + "user_info": user_info + } + + @classmethod + def get_request_info(cls, action: str, temp_auth_token: Optional[str] = None, **kwargs) -> 'RequestInfo': + """ + Get a new RequestInfo instance with default values and specified overrides. + + Args: + action (str): The action being performed + temp_auth_token (str, optional): Temporary auth token to use instead of default + **kwargs: Additional parameters to override default values + + Returns: + RequestInfo: A new RequestInfo instance + """ + if cls._default_config is None: + raise RuntimeError("RequestInfoBuilder not initialized. Call RequestInfoBuilder.initialize() first.") + + builder = cls() + + # Apply default configuration + builder.with_api_id(cls._default_config["api_id"]) + builder.with_version(cls._default_config["ver"]) + if cls._default_config["auth_token"]: + builder.with_auth_token(cls._default_config["auth_token"]) + if cls._default_config["user_info"]: + builder.with_user_info(cls._default_config["user_info"]) + + # Apply action and any overrides + builder.with_action(action) + if temp_auth_token: + builder.with_auth_token(temp_auth_token) + + # Apply any additional kwargs + for key, value in kwargs.items(): + method_name = f"with_{key}" + if hasattr(builder, method_name): + getattr(builder, method_name)(value) + + return builder.build() + + def with_api_id(self, api_id: str) -> 'RequestInfoBuilder': + self._api_id = api_id + return self + + def with_version(self, ver: str) -> 'RequestInfoBuilder': + self._ver = ver + return self + + def with_action(self, action: str) -> 'RequestInfoBuilder': + self._action = action + return self + + def with_timestamp(self, ts: int) -> 'RequestInfoBuilder': + self._ts = ts + return self + + def with_did(self, did: str) -> 'RequestInfoBuilder': + self._did = did + return self + + def with_key(self, key: str) -> 'RequestInfoBuilder': + self._key = key + return self + + def with_msg_id(self, msg_id: str) -> 'RequestInfoBuilder': + self._msg_id = msg_id + return self + + def with_requester_id(self, requester_id: str) -> 'RequestInfoBuilder': + self._requester_id = requester_id + return self + + def with_auth_token(self, auth_token: str) -> 'RequestInfoBuilder': + self._auth_token = auth_token + return self + + def with_user_info(self, user_info: dict) -> 'RequestInfoBuilder': + self._user_info = user_info + return self + + def with_correlation_id(self, correlation_id: str) -> 'RequestInfoBuilder': + self._correlation_id = correlation_id + return self + + def build(self) -> RequestInfo: + """Build and validate the RequestInfo object""" + # Validate required fields + required_fields = { + 'api_id': self._api_id, + 'ver': self._ver, + 'action': self._action + } + + missing_fields = [field for field, value in required_fields.items() if value is None] + if missing_fields: + raise ValueError(f"Missing required fields: {', '.join(missing_fields)}") + + return RequestInfo( + api_id=self._api_id, + ver=self._ver, + ts=self._ts, + action=self._action, + did=self._did, + key=self._key, + msg_id=self._msg_id, + requester_id=self._requester_id, + auth_token=self._auth_token, + user_info=self._user_info, + correlation_id=self._correlation_id + ) + +class RequestConfig: + _instance = None + _default_request_info = None + + def __new__(cls): + if cls._instance is None: + cls._instance = super(RequestConfig, cls).__new__(cls) + return cls._instance + + @classmethod + def initialize(cls, + api_id: str = "DIGIT-CLIENT", + version: str = "1.0.0", + auth_token: Optional[str] = None, + user_info: Optional[dict] = None, + did: Optional[str] = None, + key: Optional[str] = None, + msg_id: Optional[str] = None, + requester_id: Optional[str] = None, + correlation_id: Optional[str] = None, + action: str = "", + ts: Optional[int] = None) -> None: + """ + Initialize the default request configuration. + + Args: + api_id (str): The API ID for the application + version (str): API version + auth_token (str, optional): Default authentication token + user_info (dict, optional): User information dictionary + did (str, optional): Device ID + key (str, optional): Key for the request + msg_id (str, optional): Message ID + requester_id (str, optional): ID of the requester + correlation_id (str, optional): Correlation ID for request tracking + action (str, optional): Action being performed + """ + cls._default_request_info = RequestInfo( + api_id=api_id, + ver=version, + ts=ts, + action=action, + auth_token=auth_token, + user_info=user_info, + did=did, + key=key, + msg_id=msg_id, + requester_id=requester_id, + correlation_id=correlation_id + ) + + @classmethod + def update_api_id(cls, api_id: str) -> None: + """Update the API ID in the existing configuration.""" + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + request_info_dict = cls._default_request_info.to_dict() + request_info_dict["apiId"] = api_id + + cls._default_request_info = RequestInfo( + api_id=api_id, + ver=request_info_dict["ver"], + ts=int(time.time() * 1000), + action=request_info_dict["action"], + did=request_info_dict["did"], + key=request_info_dict["key"], + msg_id=str(uuid.uuid4()), + requester_id=request_info_dict["requesterId"], + auth_token=request_info_dict["authToken"], + user_info=request_info_dict["userInfo"], + correlation_id=str(uuid.uuid4()) + ) + + @classmethod + def update_version(cls, version: str) -> None: + """Update the version in the existing configuration.""" + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + request_info_dict = cls._default_request_info.to_dict() + request_info_dict["ver"] = version + + cls._default_request_info = RequestInfo( + api_id=request_info_dict["apiId"], + ver=version, + ts=int(time.time() * 1000), + action=request_info_dict["action"], + did=request_info_dict["did"], + key=request_info_dict["key"], + msg_id=str(uuid.uuid4()), + requester_id=request_info_dict["requesterId"], + auth_token=request_info_dict["authToken"], + user_info=request_info_dict["userInfo"], + correlation_id=str(uuid.uuid4()) + ) + + @classmethod + def update_auth_token(cls, auth_token: str) -> None: + """Update the auth token in the existing configuration.""" + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + request_info_dict = cls._default_request_info.to_dict() + request_info_dict["authToken"] = auth_token + + cls._default_request_info = RequestInfo( + api_id=request_info_dict["apiId"], + ver=request_info_dict["ver"], + ts=int(time.time() * 1000), + action=request_info_dict["action"], + did=request_info_dict["did"], + key=request_info_dict["key"], + msg_id=str(uuid.uuid4()), + requester_id=request_info_dict["requesterId"], + auth_token=auth_token, + user_info=request_info_dict["userInfo"], + correlation_id=str(uuid.uuid4()) + ) + + @classmethod + def update_user_info(cls, user_info: dict) -> None: + """Update the user info in the existing configuration.""" + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + request_info_dict = cls._default_request_info.to_dict() + request_info_dict["userInfo"] = user_info + + cls._default_request_info = RequestInfo( + api_id=request_info_dict["apiId"], + ver=request_info_dict["ver"], + ts=int(time.time() * 1000), + action=request_info_dict["action"], + did=request_info_dict["did"], + key=request_info_dict["key"], + msg_id=str(uuid.uuid4()), + requester_id=request_info_dict["requesterId"], + auth_token=request_info_dict["authToken"], + user_info=user_info, + correlation_id=str(uuid.uuid4()) + ) + + @classmethod + def update_did(cls, did: str) -> None: + """Update the device ID in the existing configuration.""" + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + request_info_dict = cls._default_request_info.to_dict() + request_info_dict["did"] = did + + cls._default_request_info = RequestInfo( + api_id=request_info_dict["apiId"], + ver=request_info_dict["ver"], + ts=int(time.time() * 1000), + action=request_info_dict["action"], + did=did, + key=request_info_dict["key"], + msg_id=str(uuid.uuid4()), + requester_id=request_info_dict["requesterId"], + auth_token=request_info_dict["authToken"], + user_info=request_info_dict["userInfo"], + correlation_id=str(uuid.uuid4()) + ) + + @classmethod + def update_key(cls, key: str) -> None: + """Update the key in the existing configuration.""" + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + request_info_dict = cls._default_request_info.to_dict() + request_info_dict["key"] = key + + cls._default_request_info = RequestInfo( + api_id=request_info_dict["apiId"], + ver=request_info_dict["ver"], + ts=int(time.time() * 1000), + action=request_info_dict["action"], + did=request_info_dict["did"], + key=key, + msg_id=str(uuid.uuid4()), + requester_id=request_info_dict["requesterId"], + auth_token=request_info_dict["authToken"], + user_info=request_info_dict["userInfo"], + correlation_id=str(uuid.uuid4()) + ) + + @classmethod + def update_requester_id(cls, requester_id: str) -> None: + """Update the requester ID in the existing configuration.""" + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + request_info_dict = cls._default_request_info.to_dict() + request_info_dict["requesterId"] = requester_id + + cls._default_request_info = RequestInfo( + api_id=request_info_dict["apiId"], + ver=request_info_dict["ver"], + ts=int(time.time() * 1000), + action=request_info_dict["action"], + did=request_info_dict["did"], + key=request_info_dict["key"], + msg_id=str(uuid.uuid4()), + requester_id=requester_id, + auth_token=request_info_dict["authToken"], + user_info=request_info_dict["userInfo"], + correlation_id=str(uuid.uuid4()) + ) + + @classmethod + def get_request_info(cls,temp_auth_token: Optional[str] = None, **kwargs) -> RequestInfo: + """ + Get a new RequestInfo instance with default values and specified overrides. + + Args: + action (str): The action being performed + temp_auth_token (str, optional): Temporary auth token to use instead of default + **kwargs: Additional parameters to override default values + + Returns: + RequestInfo: A new RequestInfo instance + """ + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + # Create new request info with current timestamp + request_info_dict = cls._default_request_info.to_dict() + request_info_dict.update({ + **kwargs + }) + + # Override auth token if temporary one is provided + if temp_auth_token: + request_info_dict["authToken"] = temp_auth_token + + return RequestInfo( + api_id=request_info_dict["apiId"], + ver=request_info_dict["ver"], + ts=request_info_dict["ts"], + action=request_info_dict["action"], + did=request_info_dict["did"], + key=request_info_dict["key"], + msg_id=request_info_dict["msgId"], + requester_id=request_info_dict["requesterId"], + auth_token=request_info_dict["authToken"], + user_info=request_info_dict["userInfo"], + correlation_id=request_info_dict["correlationId"] + ) \ No newline at end of file diff --git a/accelerators/digit_client/build/lib/digit_client/services/__init__.py b/accelerators/digit_client/build/lib/digit_client/services/__init__.py index b232f9a6b5c..37864849c20 100644 --- a/accelerators/digit_client/build/lib/digit_client/services/__init__.py +++ b/accelerators/digit_client/build/lib/digit_client/services/__init__.py @@ -1 +1,8 @@ -# __init__.py for services package \ No newline at end of file +# __init__.py for services package +from .authenticate import AuthenticationService +from .user_service import UserService +from .master_data_v1 import MDMSService +from .mdms_v2 import MDMSV2Service +from .authorize import AuthorizeService +from .workflow import WorkflowV2Service +__all__ = ['AuthenticationService', 'UserService', 'MDMSService', 'MDMSV2Service', 'AuthorizeService', 'WorkflowV2Service'] \ No newline at end of file diff --git a/accelerators/digit_client/build/lib/digit_client/services/tenant_service.py b/accelerators/digit_client/build/lib/digit_client/services/tenant_service.py new file mode 100644 index 00000000000..4195702a0b7 --- /dev/null +++ b/accelerators/digit_client/build/lib/digit_client/services/tenant_service.py @@ -0,0 +1,44 @@ +from typing import Dict, Optional + +class TenantService: + def __init__(self, api_client): + self.api_client = api_client + self.base_url = "tenant-management/tenant" + + def create_tenant(self, name: str, email: str, auth_token: Optional[str] = None) -> Dict: + """ + Create a new tenant in the DIGIT platform. + + Args: + name (str): Name of the tenant + email (str): Email address for the tenant + auth_token (Optional[str]): Authentication token. If not provided, uses the default from APIClient + + Returns: + Dict: Response from the tenant creation API + """ + # Use provided auth token or get from api client + token = auth_token or self.api_client.auth_token + + payload = { + "tenant": { + "name": name, + "email": email + }, + "RequestInfo": { + "apiId": "Rainmaker", + "authToken": token, + "userInfo": None, + "msgId": "1742362722972|en_IN", + "plainAccessRequest": {} + } + } + + headers = { + 'accept': 'application/json, text/plain, */*', + 'content-type': 'application/json;charset=UTF-8', + 'Authorization': f'Bearer {token}' + } + + endpoint = f"{self.base_url}/_create" + return self.api_client.post(endpoint, json_data=payload, additional_headers=headers) \ No newline at end of file diff --git a/accelerators/digit_client/build/lib/digit_client/services/user_service.py b/accelerators/digit_client/build/lib/digit_client/services/user_service.py index 205ed1e55a0..73f86eb0770 100644 --- a/accelerators/digit_client/build/lib/digit_client/services/user_service.py +++ b/accelerators/digit_client/build/lib/digit_client/services/user_service.py @@ -1,13 +1,167 @@ +from typing import Dict, List, Optional from ..api_client import APIClient +from ..models.citizen_user import CitizenUser +from ..models.search_models import UserSearchModel +from ..models.user import User +from ..request_config import RequestConfig, RequestInfo class UserService: - def __init__(self): - self.api_client = APIClient() + def __init__(self, api_client: Optional[APIClient] = None): + self.api_client = api_client or APIClient() + self.base_url = "user" - def create_user(self, user_data): - return self.api_client.post("user/create", user_data) + def create_citizen(self, citizen_user: CitizenUser, request_info: Optional[RequestInfo] = None) -> Dict: + """ + Create a new citizen user in the DIGIT platform. + + Args: + citizen_user (CitizenUser): The citizen user data + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig + + Returns: + Dict: Response from the user creation API + """ + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info() - def get_user(self, user_id): - return self.api_client.get(f"user/{user_id}") + payload = { + "RequestInfo": request_info.to_dict(), + "user": citizen_user.to_dict() + } - # Add more user related methods as needed \ No newline at end of file + endpoint = f"{self.base_url}/citizen/_create" + return self.api_client.post(endpoint, json_data=payload) + + def get_user_details(self, tenant_id: str, request_info: Optional[RequestInfo] = None) -> Dict: + """ + Get user details using access token. + + Args: + tenant_id (str): Tenant ID + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig + + Returns: + Dict: User details from the API + """ + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict() + } + + # Add query parameters + params = { + "tenantId": tenant_id + } + + # Use temp_auth_token from parameter if provided, otherwise use auth token from RequestInfo + if temp_auth_token: + params["access_token"] = temp_auth_token + else: + # Get auth token from RequestInfo + request_info_dict = request_info.to_dict() + if request_info_dict.get("authToken"): + params["access_token"] = request_info_dict["authToken"] + else: + raise ValueError("No access token provided and no auth token found in RequestInfo") + + endpoint = f"{self.base_url}/_details" + return self.api_client.post(endpoint, json_data=payload, params=params) + + def update_profile(self, user_profile: User, request_info: Optional[RequestInfo] = None) -> Dict: + """ + Update user profile. + + Args: + user_profile (User): The updated user profile data + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig + + Returns: + Dict: Response from the user profile update API + """ + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict(), + "user": user_profile.to_dict() + } + + endpoint = f"{self.base_url}/profile/_update" + return self.api_client.post(endpoint, json_data=payload) + + def search_users(self, search_criteria: UserSearchModel, request_info: Optional[RequestInfo] = None) -> Dict: + """ + Search for users based on given criteria + + Args: + search_criteria (UserSearchModel): The search criteria + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig + + Returns: + Dict: Search results from the API + """ + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info() + + # Combine RequestInfo with search criteria at root level + payload = { + "RequestInfo": request_info.to_dict(), + **search_criteria.to_dict() # Spread search criteria at root level + } + + endpoint = f"{self.base_url}/_search" + return self.api_client.post(endpoint, json_data=payload) + + def create_user_no_validate(self, citizen_user: CitizenUser, request_info: Optional[RequestInfo] = None) -> Dict: + """ + Create a new user without validation in the DIGIT platform. + + Args: + citizen_user (CitizenUser): The citizen user data + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig + + Returns: + Dict: Response from the user creation API + """ + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict(), + "user": citizen_user.to_dict() + } + + endpoint = f"{self.base_url}/users/_createnovalidate" + return self.api_client.post(endpoint, json_data=payload) + + def update_user_no_validate(self, user_profile: User, request_info: Optional[RequestInfo] = None) -> Dict: + """ + Update a user without validation in the DIGIT platform. + + Args: + user_profile (User): The updated user profile data + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig + + Returns: + Dict: Response from the user update API + """ + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict(), + "user": user_profile.to_dict() + } + + endpoint = f"{self.base_url}/users/_updatenovalidate" + return self.api_client.post(endpoint, json_data=payload) + + \ No newline at end of file diff --git a/accelerators/digit_client/digit_client.egg-info/PKG-INFO b/accelerators/digit_client/digit_client.egg-info/PKG-INFO index fd42a3c5792..76c56f239a5 100644 --- a/accelerators/digit_client/digit_client.egg-info/PKG-INFO +++ b/accelerators/digit_client/digit_client.egg-info/PKG-INFO @@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Requires-Python: >=3.6 Requires-Dist: requests>=2.25.1 +Requires-Dist: jsonschema>=4.0.0 Dynamic: author Dynamic: author-email Dynamic: classifier diff --git a/accelerators/digit_client/digit_client.egg-info/SOURCES.txt b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt index bb7f70ac1e9..3d1c0fcc3eb 100644 --- a/accelerators/digit_client/digit_client.egg-info/SOURCES.txt +++ b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt @@ -2,20 +2,31 @@ README.md setup.py digit_client/__init__.py digit_client/api_client.py -digit_client/auth.py digit_client/config.py digit_client/exceptions.py -digit_client/pre_install.py +digit_client/request_config.py digit_client/utils.py digit_client.egg-info/PKG-INFO digit_client.egg-info/SOURCES.txt digit_client.egg-info/dependency_links.txt digit_client.egg-info/requires.txt digit_client.egg-info/top_level.txt +digit_client/models/ActionRequest.py +digit_client/models/AuthorizationRequest.py +digit_client/models/Workflow.py digit_client/models/__init__.py -digit_client/models/boundary.py -digit_client/models/service.py -digit_client/models/service_definition.py +digit_client/models/auth.py +digit_client/models/citizen_user.py +digit_client/models/mdms.py +digit_client/models/mdms_v2.py +digit_client/models/search_models.py digit_client/models/user.py +digit_client/models/workflow.py digit_client/services/__init__.py -digit_client/services/user_service.py \ No newline at end of file +digit_client/services/authenticate.py +digit_client/services/authorize.py +digit_client/services/master_data_v1.py +digit_client/services/mdms_v2.py +digit_client/services/user_service.py +digit_client/services/workflow.py +tests/test_user_service.py \ No newline at end of file diff --git a/accelerators/digit_client/digit_client.egg-info/requires.txt b/accelerators/digit_client/digit_client.egg-info/requires.txt index 65d7ffc5c62..b8fa00b94fb 100644 --- a/accelerators/digit_client/digit_client.egg-info/requires.txt +++ b/accelerators/digit_client/digit_client.egg-info/requires.txt @@ -1 +1,2 @@ requests>=2.25.1 +jsonschema>=4.0.0 diff --git a/accelerators/digit_client/digit_client/__init__.py b/accelerators/digit_client/digit_client/__init__.py index 2345eeff1d3..4328e62888a 100644 --- a/accelerators/digit_client/digit_client/__init__.py +++ b/accelerators/digit_client/digit_client/__init__.py @@ -1,52 +1,107 @@ -# __init__.py for digit_client package -import os -import sys -from pathlib import Path +""" +DIGIT Client Library for Python +""" -# Store auth file in the package directory instead of home -PACKAGE_DIR = os.path.dirname(os.path.abspath(__file__)) -AUTH_FILE = os.path.join(PACKAGE_DIR, '.digit_client_auth') +__version__ = "0.1.0" -def check_auth(): - """Check if authentication has been done""" - try: - return os.path.exists(AUTH_FILE) - except Exception: - return False - -def perform_auth(): - """Perform authentication""" - try: - # Ensure we import from the correct path - # sys.path.insert(0, os.path.dirname(PACKAGE_DIR)) - from digit_client.pre_install import main as auth_main - - success, access_token = auth_main() - if success and access_token: - # Create auth file after successful authentication - try: - # with open(AUTH_FILE, 'w') as f: - # f.write(access_token) - return True - except Exception as e: - print(f"Failed to create auth file: {e}") - return False - return False - except Exception as e: - print(f"Authentication error: {e}") - return False - -print("Initializing DIGIT Client...") -# Check authentication on import -if not check_auth(): - print("\nAuthentication required for DIGIT Client") - if not perform_auth(): - print("Authentication failed. Package cannot be used.") - sys.exit(1) - print("Authentication successful!") - -# Only import DigitAuth after successful authentication -from .auth import DigitAuth - -__version__ = '0.1' -__all__ = ['DigitAuth'] \ No newline at end of file +from .api_client import APIClient +from .config import Config +from .services import AuthenticationService, UserService, MDMSService, MDMSV2Service, AuthorizeService, WorkflowV2Service +from .request_config import RequestConfig, RequestInfo, RequestInfoBuilder +from .models.citizen_user import CitizenUser, Role, CitizenUserBuilder +from .models.search_models import UserSearchModel, UserSearchModelBuilder +from .models.user import User,UserBuilder +from .models.auth import AuthenticationRequest, AuthenticationRequestBuilder +from .models.mdms import ( + MdmsCriteriaReq, + MdmsCriteriaReqBuilder, + MdmsCriteria, + MdmsCriteriaBuilder, + ModuleDetail, + ModuleDetailBuilder, + MasterDetail, + MasterDetailBuilder +) +from .models.mdms_v2 import ( + SchemaDefinition, + SchemaDefinitionBuilder, + SchemaDefCriteria, + SchemaDefCriteriaBuilder, + AuditDetails, + AuditDetailsBuilder, + Mdms, + MdmsBuilder, + MdmsCriteriaV2, + MdmsCriteriaV2Builder, +) +from .models.workflow import ( + Document, + DocumentBuilder, + WorkflowAction, + WorkflowActionBuilder, + State, + StateBuilder, + ProcessInstance, + ProcessInstanceBuilder, + ProcessInstanceSearchCriteria, + ProcessInstanceSearchCriteriaBuilder, +) +from .models.AuthorizationRequest import AuthorizationRequest, AuthorizationRequestBuilder, Role, RoleBuilder +from .models.ActionRequest import ActionRequest, ActionBuilder, ActionRequestBuilder, Action +__all__ = [ + 'APIClient', + 'Config', + 'AuthenticationService', + 'UserService', + 'MDMSService', + 'MDMSV2Service', + 'AuthorizeService', + 'WorkflowV2Service', + 'RequestConfig', + 'RequestInfo', + 'RequestInfoBuilder', + 'CitizenUser', + 'Role', + 'UserSearchModel', + 'UserSearchModelBuilder', + 'User', + 'UserBuilder', + 'CitizenUserBuilder', + 'AuthenticationRequest', + 'AuthenticationRequestBuilder', + 'MdmsCriteriaReq', + 'MdmsCriteriaReqBuilder', + 'MdmsCriteria', + 'MdmsCriteriaBuilder', + 'ModuleDetail', + 'ModuleDetailBuilder', + 'MasterDetail', + 'MasterDetailBuilder', + 'SchemaDefinition', + 'SchemaDefinitionBuilder', + 'SchemaDefCriteria', + 'SchemaDefCriteriaBuilder', + 'AuditDetails', + 'AuditDetailsBuilder', + 'Mdms', + 'MdmsBuilder', + 'MdmsCriteriaV2', + 'MdmsCriteriaV2Builder', + 'AuthorizationRequest', + 'AuthorizationRequestBuilder', + 'Role', + 'RoleBuilder', + 'ActionRequest', + 'ActionBuilder', + 'ActionRequestBuilder', + 'WorkflowAction', + 'WorkflowActionBuilder', + 'Document', + 'DocumentBuilder', + 'State', + 'StateBuilder', + 'ProcessInstance', + 'ProcessInstanceBuilder', + 'ProcessInstanceSearchCriteria', + 'ProcessInstanceSearchCriteriaBuilder', +] \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/__pycache__/__init__.cpython-313.pyc b/accelerators/digit_client/digit_client/__pycache__/__init__.cpython-313.pyc index 4f0b89956a2..e5de5ddadfc 100644 Binary files a/accelerators/digit_client/digit_client/__pycache__/__init__.cpython-313.pyc and b/accelerators/digit_client/digit_client/__pycache__/__init__.cpython-313.pyc differ diff --git a/accelerators/digit_client/digit_client/api_client.py b/accelerators/digit_client/digit_client/api_client.py index a28a964c9db..3824e7b0931 100644 --- a/accelerators/digit_client/digit_client/api_client.py +++ b/accelerators/digit_client/digit_client/api_client.py @@ -11,9 +11,36 @@ def get(self, endpoint, params=None): response = requests.get(f"{self.base_url}/{endpoint}", headers=headers, params=params) return response.json() - def post(self, endpoint, json_data): - headers = {'Authorization': f'Bearer {self.auth_token}'} - response = requests.post(f"{self.base_url}/{endpoint}", headers=headers, json=json_data) + def post(self, endpoint, json_data=None, data=None, additional_headers=None, params=None): + """ + Make a POST request + + Args: + endpoint (str): API endpoint + json_data (dict, optional): JSON data to send + data (dict, optional): Form data to send + additional_headers (dict, optional): Additional headers to include + params (dict, optional): Query parameters to include in the URL + + Returns: + dict: Response JSON + """ + # headers = {'Authorization': f'Bearer {self.auth_token}'} + headers = {'Content-Type': 'application/json'} + if additional_headers: + headers.update(additional_headers) + print(headers) + print(json_data) + print(data) + print(params) + + response = requests.post( + f"{self.base_url}/{endpoint}", + headers=headers, + json=json_data if json_data is not None else None, + data=data if data is not None else None, + params=params if params is not None else None + ) return response.json() # Add more HTTP methods as needed \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/auth.py b/accelerators/digit_client/digit_client/auth.py deleted file mode 100644 index 87067ba5aa8..00000000000 --- a/accelerators/digit_client/digit_client/auth.py +++ /dev/null @@ -1,73 +0,0 @@ -import requests -import json - -class DigitAuth: - BASE_URL = "https://sandbox.digit.org" - - @staticmethod - def register_user(email, tenant_name): - url = f"{DigitAuth.BASE_URL}/tenant-management/tenant/_create" - payload = { - "tenant": { - "name": tenant_name, - "email": email - }, - "RequestInfo": { - "apiId": "Rainmaker", - "authToken": None, - "userInfo": None, - "msgId": "registration", - "plainAccessRequest": {} - } - } - response = requests.post(url, json=payload) - return response.json() - - @staticmethod - def send_otp(email, tenant_id): - url = f"{DigitAuth.BASE_URL}/user-otp/v1/_send" - params = {"tenantId": tenant_id} - payload = { - "otp": { - "userName": email, - "type": "login", - "tenantId": tenant_id, - "userType": "EMPLOYEE" - }, - "RequestInfo": { - "apiId": "Rainmaker", - "authToken": None, - "userInfo": None, - "msgId": "otp_request", - "plainAccessRequest": {} - } - } - response = requests.post(url, params=params, json=payload) - return response.json() - - @staticmethod - def validate_otp(email, otp, tenant_id): - url = f"{DigitAuth.BASE_URL}/user/oauth/token" - - # Form encoded payload - payload = { - 'username': email, - 'password': otp, - 'tenantId': tenant_id, - 'userType': 'EMPLOYEE', - 'scope': 'read', - 'grant_type': 'password' - } - - headers = { - 'accept': 'application/json, text/plain, */*', - 'accept-language': 'en-US,en;q=0.9', - 'authorization': 'Basic ZWdvdi11c2VyLWNsaWVudDo=', - 'content-type': 'application/x-www-form-urlencoded', - 'origin': 'https://sandbox.digit.org', - 'referer': f'https://sandbox.digit.org/sandbox-ui/{tenant_id}/employee/user/login/otp', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36' - } - - response = requests.post(url, headers=headers, data=payload) - return response.json() \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/config.py b/accelerators/digit_client/digit_client/config.py index 561d416ea85..29fe688f287 100644 --- a/accelerators/digit_client/digit_client/config.py +++ b/accelerators/digit_client/digit_client/config.py @@ -1,3 +1,13 @@ class Config: - API_ENDPOINT = "https://example.com/api" # Set your API endpoint - AUTH_TOKEN = "your_auth_token" # Set your authentication token \ No newline at end of file + API_ENDPOINT = "https://sandbox.digit.org" # Sandbox API endpoint + AUTH_TOKEN = None # Will be set during runtime + + @classmethod + def set_auth_token(cls, token: str): + """Set the authentication token for API requests""" + cls.AUTH_TOKEN = token + + @classmethod + def set_api_endpoint(cls, endpoint: str): + """Set the API endpoint (useful for switching between environments)""" + cls.API_ENDPOINT = endpoint \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/models/ActionRequest.py b/accelerators/digit_client/digit_client/models/ActionRequest.py new file mode 100644 index 00000000000..bdeed01ba19 --- /dev/null +++ b/accelerators/digit_client/digit_client/models/ActionRequest.py @@ -0,0 +1,352 @@ +from dataclasses import dataclass, field +from typing import List, Optional, Dict, Any +from datetime import datetime +from ..request_config import RequestInfo + +@dataclass +class Action: + """ + Model representing an action + """ + id: Optional[int] = None + name: Optional[str] = None + url: Optional[str] = None + display_name: Optional[str] = None + order_number: Optional[int] = None + query_params: Optional[str] = None + parent_module: Optional[str] = None + enabled: bool = False + service_code: Optional[str] = None + tenant_id: Optional[str] = None + created_date: Optional[datetime] = None + created_by: Optional[int] = None + last_modified_date: Optional[datetime] = None + last_modified_by: Optional[int] = None + path: Optional[str] = None + navigation_url: Optional[str] = None + left_icon: Optional[str] = None + right_icon: Optional[str] = None + + def __post_init__(self): + if self.name and len(self.name) > 100: + raise ValueError("name must be at most 100 characters") + if self.url and len(self.url) > 100: + raise ValueError("url must be at most 100 characters") + if self.display_name and len(self.display_name) > 100: + raise ValueError("display_name must be at most 100 characters") + if self.query_params and len(self.query_params) > 100: + raise ValueError("query_params must be at most 100 characters") + if self.parent_module and len(self.parent_module) > 50: + raise ValueError("parent_module must be at most 50 characters") + if self.service_code and len(self.service_code) > 50: + raise ValueError("service_code must be at most 50 characters") + if self.tenant_id and len(self.tenant_id) > 50: + raise ValueError("tenant_id must be at most 50 characters") + + def to_dict(self) -> Dict[str, Any]: + result = {} + if self.id is not None: + result["id"] = self.id + if self.name: + result["name"] = self.name + if self.url: + result["url"] = self.url + if self.display_name: + result["displayName"] = self.display_name + if self.order_number is not None: + result["orderNumber"] = self.order_number + if self.query_params: + result["queryParams"] = self.query_params + if self.parent_module: + result["parentModule"] = self.parent_module + result["enabled"] = self.enabled + if self.service_code: + result["serviceCode"] = self.service_code + if self.tenant_id: + result["tenantId"] = self.tenant_id + if self.created_date: + result["createdDate"] = self.created_date.isoformat() + if self.created_by is not None: + result["createdBy"] = self.created_by + if self.last_modified_date: + result["lastModifiedDate"] = self.last_modified_date.isoformat() + if self.last_modified_by is not None: + result["lastModifiedBy"] = self.last_modified_by + if self.path: + result["path"] = self.path + if self.navigation_url: + result["navigationURL"] = self.navigation_url + if self.left_icon: + result["leftIcon"] = self.left_icon + if self.right_icon: + result["rightIcon"] = self.right_icon + return result + +@dataclass +class ActionSearchCriteria: + """ + Model representing action search criteria + """ + role_codes: Optional[List[str]] = field(default_factory=list) + feature_ids: Optional[List[int]] = field(default_factory=list) + tenant_id: Optional[str] = None + + def to_dict(self) -> Dict[str, Any]: + result = {} + if self.role_codes: + result["roleCodes"] = self.role_codes + if self.feature_ids: + result["featureIds"] = self.feature_ids + if self.tenant_id: + result["tenantId"] = self.tenant_id + return result + +@dataclass +class ActionRequest: + """ + Model representing an action request + """ + request_info: Optional[RequestInfo] = None + role_codes: Optional[List[str]] = field(default_factory=list) + feature_ids: Optional[List[int]] = field(default_factory=list) + tenant_id: Optional[str] = None + enabled: Optional[bool] = None + actions: Optional[List[Action]] = field(default_factory=list) + action_master: Optional[str] = None + navigation_url: Optional[str] = None + left_icon: Optional[str] = None + right_icon: Optional[str] = None + + def __post_init__(self): + if self.tenant_id and len(self.tenant_id) > 50: + raise ValueError("tenant_id must be at most 50 characters") + + def to_domain(self) -> ActionSearchCriteria: + """ + Convert to ActionSearchCriteria domain object + """ + return ActionSearchCriteria( + role_codes=self.role_codes, + feature_ids=self.feature_ids, + tenant_id=self.tenant_id + ) + + def to_dict(self) -> Dict[str, Any]: + result = {} + if self.request_info: + result["RequestInfo"] = self.request_info.to_dict() + if self.role_codes: + result["roleCodes"] = self.role_codes + if self.feature_ids: + result["featureIds"] = self.feature_ids + if self.tenant_id: + result["tenantId"] = self.tenant_id + if self.enabled is not None: + result["enabled"] = self.enabled + if self.actions: + result["actions"] = [action.to_dict() for action in self.actions] + if self.action_master: + result["actionMaster"] = self.action_master + if self.navigation_url: + result["navigationURL"] = self.navigation_url + if self.left_icon: + result["leftIcon"] = self.left_icon + if self.right_icon: + result["rightIcon"] = self.right_icon + return result + +class ActionBuilder: + """Builder for creating Action objects""" + def __init__(self): + self._id: Optional[int] = None + self._name: Optional[str] = None + self._url: Optional[str] = None + self._display_name: Optional[str] = None + self._order_number: Optional[int] = None + self._query_params: Optional[str] = None + self._parent_module: Optional[str] = None + self._enabled: bool = False + self._service_code: Optional[str] = None + self._tenant_id: Optional[str] = None + self._created_date: Optional[datetime] = None + self._created_by: Optional[int] = None + self._last_modified_date: Optional[datetime] = None + self._last_modified_by: Optional[int] = None + self._path: Optional[str] = None + self._navigation_url: Optional[str] = None + self._left_icon: Optional[str] = None + self._right_icon: Optional[str] = None + + def with_id(self, id: int) -> 'ActionBuilder': + self._id = id + return self + + def with_name(self, name: str) -> 'ActionBuilder': + self._name = name + return self + + def with_url(self, url: str) -> 'ActionBuilder': + self._url = url + return self + + def with_display_name(self, display_name: str) -> 'ActionBuilder': + self._display_name = display_name + return self + + def with_order_number(self, order_number: int) -> 'ActionBuilder': + self._order_number = order_number + return self + + def with_query_params(self, query_params: str) -> 'ActionBuilder': + self._query_params = query_params + return self + + def with_parent_module(self, parent_module: str) -> 'ActionBuilder': + self._parent_module = parent_module + return self + + def with_enabled(self, enabled: bool) -> 'ActionBuilder': + self._enabled = enabled + return self + + def with_service_code(self, service_code: str) -> 'ActionBuilder': + self._service_code = service_code + return self + + def with_tenant_id(self, tenant_id: str) -> 'ActionBuilder': + self._tenant_id = tenant_id + return self + + def with_created_date(self, created_date: datetime) -> 'ActionBuilder': + self._created_date = created_date + return self + + def with_created_by(self, created_by: int) -> 'ActionBuilder': + self._created_by = created_by + return self + + def with_last_modified_date(self, last_modified_date: datetime) -> 'ActionBuilder': + self._last_modified_date = last_modified_date + return self + + def with_last_modified_by(self, last_modified_by: int) -> 'ActionBuilder': + self._last_modified_by = last_modified_by + return self + + def with_path(self, path: str) -> 'ActionBuilder': + self._path = path + return self + + def with_navigation_url(self, navigation_url: str) -> 'ActionBuilder': + self._navigation_url = navigation_url + return self + + def with_left_icon(self, left_icon: str) -> 'ActionBuilder': + self._left_icon = left_icon + return self + + def with_right_icon(self, right_icon: str) -> 'ActionBuilder': + self._right_icon = right_icon + return self + + def build(self) -> Action: + return Action( + id=self._id, + name=self._name, + url=self._url, + display_name=self._display_name, + order_number=self._order_number, + query_params=self._query_params, + parent_module=self._parent_module, + enabled=self._enabled, + service_code=self._service_code, + tenant_id=self._tenant_id, + created_date=self._created_date, + created_by=self._created_by, + last_modified_date=self._last_modified_date, + last_modified_by=self._last_modified_by, + path=self._path, + navigation_url=self._navigation_url, + left_icon=self._left_icon, + right_icon=self._right_icon + ) + +class ActionRequestBuilder: + """Builder for creating ActionRequest objects""" + def __init__(self): + self._request_info: Optional[RequestInfo] = None + self._role_codes: List[str] = [] + self._feature_ids: List[int] = [] + self._tenant_id: Optional[str] = None + self._enabled: Optional[bool] = None + self._actions: List[Action] = [] + self._action_master: Optional[str] = None + self._navigation_url: Optional[str] = None + self._left_icon: Optional[str] = None + self._right_icon: Optional[str] = None + + def with_request_info(self, request_info: RequestInfo) -> 'ActionRequestBuilder': + self._request_info = request_info + return self + + def with_role_codes(self, role_codes: List[str]) -> 'ActionRequestBuilder': + self._role_codes = role_codes + return self + + def add_role_code(self, role_code: str) -> 'ActionRequestBuilder': + self._role_codes.append(role_code) + return self + + def with_feature_ids(self, feature_ids: List[int]) -> 'ActionRequestBuilder': + self._feature_ids = feature_ids + return self + + def add_feature_id(self, feature_id: int) -> 'ActionRequestBuilder': + self._feature_ids.append(feature_id) + return self + + def with_tenant_id(self, tenant_id: str) -> 'ActionRequestBuilder': + self._tenant_id = tenant_id + return self + + def with_enabled(self, enabled: bool) -> 'ActionRequestBuilder': + self._enabled = enabled + return self + + def with_actions(self, actions: List[Action]) -> 'ActionRequestBuilder': + self._actions = actions + return self + + def add_action(self, action: Action) -> 'ActionRequestBuilder': + self._actions.append(action) + return self + + def with_action_master(self, action_master: str) -> 'ActionRequestBuilder': + self._action_master = action_master + return self + + def with_navigation_url(self, navigation_url: str) -> 'ActionRequestBuilder': + self._navigation_url = navigation_url + return self + + def with_left_icon(self, left_icon: str) -> 'ActionRequestBuilder': + self._left_icon = left_icon + return self + + def with_right_icon(self, right_icon: str) -> 'ActionRequestBuilder': + self._right_icon = right_icon + return self + + def build(self) -> ActionRequest: + return ActionRequest( + request_info=self._request_info, + role_codes=self._role_codes, + feature_ids=self._feature_ids, + tenant_id=self._tenant_id, + enabled=self._enabled, + actions=self._actions, + action_master=self._action_master, + navigation_url=self._navigation_url, + left_icon=self._left_icon, + right_icon=self._right_icon + ) diff --git a/accelerators/digit_client/digit_client/models/AuthorizationRequest.py b/accelerators/digit_client/digit_client/models/AuthorizationRequest.py new file mode 100644 index 00000000000..7b36561c0a1 --- /dev/null +++ b/accelerators/digit_client/digit_client/models/AuthorizationRequest.py @@ -0,0 +1,127 @@ +from dataclasses import dataclass, field +from typing import Set, Optional, Dict, Any, List + +@dataclass(frozen=True) +class Role: + """ + Model representing a user role + """ + id: Optional[int] = None + name: Optional[str] = None + code: Optional[str] = None + tenant_id: Optional[str] = None + + def __post_init__(self): + if self.name and len(self.name) > 32: + raise ValueError("name must be at most 32 characters") + if self.code and len(self.code) > 50: + raise ValueError("code must be at most 50 characters") + if self.tenant_id and len(self.tenant_id) > 50: + raise ValueError("tenant_id must be at most 50 characters") + + def to_dict(self) -> Dict[str, Any]: + result = {} + if self.id is not None: + result["id"] = self.id + if self.name is not None: + result["name"] = self.name + if self.code is not None: + result["code"] = self.code + if self.tenant_id is not None: + result["tenantId"] = self.tenant_id + return result + + # def __setattr__(self, name, value): + # if name in ['id', 'name', 'code', 'tenant_id']: + # super().__setattr__(name, value) + # else: + # raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") + +@dataclass +class AuthorizationRequest: + """ + Model representing an authorization request + """ + roles: Set[Role] + uri: str + tenant_ids: Set[str] + + def __post_init__(self): + if not self.roles: + raise ValueError("At least one role is required") + if not self.uri: + raise ValueError("URI is required") + if not self.tenant_ids: + raise ValueError("At least one tenant ID is required") + + def to_dict(self) -> Dict[str, Any]: + return { + "roles": [role.to_dict() for role in self.roles], + "uri": self.uri, + "tenantIds": list(self.tenant_ids) + } + +class RoleBuilder: + """Builder for creating Role objects""" + def __init__(self): + self._id: Optional[int] = None + self._name: Optional[str] = None + self._code: Optional[str] = None + self._tenant_id: Optional[str] = None + + def with_id(self, role_id: int) -> 'RoleBuilder': + self._id = role_id + return self + + def with_name(self, name: str) -> 'RoleBuilder': + self._name = name + return self + + def with_code(self, code: str) -> 'RoleBuilder': + self._code = code + return self + + def with_tenant_id(self, tenant_id: str) -> 'RoleBuilder': + self._tenant_id = tenant_id + return self + + def build(self) -> Role: + return Role( + id=self._id, + name=self._name, + code=self._code, + tenant_id=self._tenant_id + ) + +class AuthorizationRequestBuilder: + """Builder for creating AuthorizationRequest objects""" + def __init__(self): + self._roles: Set[Role] = set() + self._uri: Optional[str] = None + self._tenant_ids: Set[str] = set() + + def with_uri(self, uri: str) -> 'AuthorizationRequestBuilder': + self._uri = uri + return self + + def add_role(self, role: Role) -> 'AuthorizationRequestBuilder': + self._roles.add(role) + return self + + def add_tenant_id(self, tenant_id: str) -> 'AuthorizationRequestBuilder': + self._tenant_ids.add(tenant_id) + return self + + def build(self) -> AuthorizationRequest: + if not self._uri: + raise ValueError("URI is required") + if not self._roles: + raise ValueError("At least one role is required") + if not self._tenant_ids: + raise ValueError("At least one tenant ID is required") + + return AuthorizationRequest( + roles=self._roles, + uri=self._uri, + tenant_ids=self._tenant_ids + ) diff --git a/accelerators/digit_client/digit_client/models/__init__.py b/accelerators/digit_client/digit_client/models/__init__.py index 9db2b1183d3..06c33192c2c 100644 --- a/accelerators/digit_client/digit_client/models/__init__.py +++ b/accelerators/digit_client/digit_client/models/__init__.py @@ -1 +1,66 @@ -# __init__.py for models package \ No newline at end of file +# __init__.py for models package +from .user import User, UserBuilder +from .AuthorizationRequest import Role, RoleBuilder, AuthorizationRequest, AuthorizationRequestBuilder +from .ActionRequest import ActionRequest, ActionBuilder, ActionRequestBuilder, Action +from .citizen_user import CitizenUser, CitizenUserBuilder +from .mdms import MdmsCriteria, MdmsCriteriaBuilder, MdmsCriteriaReq, MdmsCriteriaReqBuilder, MasterDetailBuilder, ModuleDetailBuilder, ModuleDetail, MasterDetail +from .search_models import UserSearchModel, UserSearchModelBuilder +from .mdms_v2 import ( + SchemaDefinition, + SchemaDefinitionBuilder, + SchemaDefCriteria, + SchemaDefCriteriaBuilder, + AuditDetails, + AuditDetailsBuilder, + Mdms, + MdmsBuilder, + MdmsCriteriaV2, + MdmsCriteriaV2Builder, +) +from .workflow import Document, DocumentBuilder, WorkflowAction, WorkflowActionBuilder, State, StateBuilder, ProcessInstance, ProcessInstanceBuilder, ProcessInstanceSearchCriteria, ProcessInstanceSearchCriteriaBuilder + + +__all__ = ['Role', + 'RoleBuilder', + 'AuthorizationRequest', + 'AuthorizationRequestBuilder', + 'ActionRequest', + 'ActionBuilder', + 'ActionRequestBuilder', + 'Action', + 'User', + 'UserBuilder', + 'CitizenUser', + 'CitizenUserBuilder', + 'MdmsCriteria', + 'MdmsCriteriaBuilder', + 'UserSearchModel', + 'UserSearchModelBuilder', + 'AuditDetails', + 'SchemaDefinition', + 'SchemaDefinitionRequest', + 'SchemaDefCriteria', + 'Mdms', + 'MdmsCriteriaV2', + 'MdmsBuilder', + 'MdmsCriteriaV2Builder', + 'SchemaDefinitionBuilder', + 'SchemaDefinitionRequestBuilder', + 'SchemaDefCriteriaBuilder', + 'AuditDetailsBuilder', + 'MasterDetail', + 'MasterDetailBuilder', + 'ModuleDetail', + 'ModuleDetailBuilder', + 'MdmsCriteriaReq', + 'MdmsCriteriaReqBuilder', + 'Document', + 'DocumentBuilder', + 'WorkflowAction', + 'WorkflowActionBuilder', + 'State', + 'StateBuilder', + 'ProcessInstance', + 'ProcessInstanceBuilder', + 'ProcessInstanceSearchCriteria', + 'ProcessInstanceSearchCriteriaBuilder'] \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/models/auth.py b/accelerators/digit_client/digit_client/models/auth.py new file mode 100644 index 00000000000..a4e6e6c47b2 --- /dev/null +++ b/accelerators/digit_client/digit_client/models/auth.py @@ -0,0 +1,84 @@ +from dataclasses import dataclass +from typing import Optional + +@dataclass +class AuthenticationRequest: + """ + Model for authentication request parameters + """ + username: str + password: str + tenant_id: str + grant_type: str = 'password' + scope: str = 'read' + user_type: str = 'EMPLOYEE' + + def to_dict(self) -> dict: + return { + 'grant_type': self.grant_type, + 'scope': self.scope, + 'username': self.username, + 'password': self.password, + 'tenantId': self.tenant_id, + 'userType': self.user_type + } + +class AuthenticationRequestBuilder: + """Builder class for creating AuthenticationRequest objects""" + + def __init__(self): + # Required fields + self._username: Optional[str] = None + self._password: Optional[str] = None + self._tenant_id: Optional[str] = None + + # Optional fields with defaults + self._grant_type: str = 'password' + self._scope: str = 'read' + self._user_type: str = 'EMPLOYEE' + + def with_username(self, username: str) -> 'AuthenticationRequestBuilder': + self._username = username + return self + + def with_password(self, password: str) -> 'AuthenticationRequestBuilder': + self._password = password + return self + + def with_tenant_id(self, tenant_id: str) -> 'AuthenticationRequestBuilder': + self._tenant_id = tenant_id + return self + + def with_grant_type(self, grant_type: str) -> 'AuthenticationRequestBuilder': + self._grant_type = grant_type + return self + + def with_scope(self, scope: str) -> 'AuthenticationRequestBuilder': + self._scope = scope + return self + + def with_user_type(self, user_type: str) -> 'AuthenticationRequestBuilder': + self._user_type = user_type + return self + + def build(self) -> AuthenticationRequest: + """Build and validate the AuthenticationRequest object""" + # Validate required fields + required_fields = { + 'username': self._username, + 'password': self._password, + 'tenant_id': self._tenant_id + } + + missing_fields = [field for field, value in required_fields.items() if value is None] + if missing_fields: + raise ValueError(f"Missing required fields: {', '.join(missing_fields)}") + + return AuthenticationRequest( + username=self._username, + password=self._password, + tenant_id=self._tenant_id, + grant_type=self._grant_type, + scope=self._scope, + user_type=self._user_type + ) \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/models/boundary.py b/accelerators/digit_client/digit_client/models/boundary.py deleted file mode 100644 index 6b32faf4db0..00000000000 --- a/accelerators/digit_client/digit_client/models/boundary.py +++ /dev/null @@ -1,10 +0,0 @@ -class Boundary: - def __init__(self, boundary_id, boundary_name): - self.boundary_id = boundary_id - self.boundary_name = boundary_name - - def to_dict(self): - return { - "boundary_id": self.boundary_id, - "boundary_name": self.boundary_name - } \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/models/citizen_user.py b/accelerators/digit_client/digit_client/models/citizen_user.py new file mode 100644 index 00000000000..8df9481d42b --- /dev/null +++ b/accelerators/digit_client/digit_client/models/citizen_user.py @@ -0,0 +1,305 @@ +from typing import List, Optional +from dataclasses import dataclass +from copy import deepcopy +from datetime import datetime + +@dataclass +class Role: + code: str + name: str + tenant_id: str + + def to_dict(self) -> dict: + return { + "code": self.code, + "name": self.name, + "tenantId": self.tenant_id + } + +@dataclass +class CitizenUser: + user_name: str + password: str + salutation: str + name: str + gender: str + mobile_number: str + email_id: str + tenant_id: str + roles: List[Role] + alt_contact_number: Optional[str] = "" + pan: Optional[str] = None + aadhaar_number: Optional[str] = None + permanent_address: Optional[str] = None + permanent_city: Optional[str] = None + permanent_pincode: Optional[str] = None + correspondence_city: Optional[str] = None + correspondence_pincode: Optional[str] = None + correspondence_address: Optional[str] = None + active: bool = True + dob: Optional[str] = None + pwd_expiry_date: Optional[str] = None + locale: str = "en_IN" + type: str = "CITIZEN" + signature: Optional[str] = None + account_locked: bool = False + father_or_husband_name: Optional[str] = None + blood_group: Optional[str] = None + identification_mark: Optional[str] = None + photo: Optional[str] = None + otp_reference: Optional[str] = None + + def to_dict(self) -> dict: + return { + "userName": self.user_name, + "password": self.password, + "salutation": self.salutation, + "name": self.name, + "gender": self.gender, + "mobileNumber": self.mobile_number, + "emailId": self.email_id, + "altContactNumber": self.alt_contact_number, + "pan": self.pan, + "aadhaarNumber": self.aadhaar_number, + "permanentAddress": self.permanent_address, + "permanentCity": self.permanent_city, + "permanentPincode": self.permanent_pincode, + "correspondenceCity": self.correspondence_city, + "correspondencePincode": self.correspondence_pincode, + "correspondenceAddress": self.correspondence_address, + "active": self.active, + "dob": self.dob, + "pwdExpiryDate": self.pwd_expiry_date, + "locale": self.locale, + "type": self.type, + "signature": self.signature, + "accountLocked": self.account_locked, + "fatherOrHusbandName": self.father_or_husband_name, + "bloodGroup": self.blood_group, + "identificationMark": self.identification_mark, + "photo": self.photo, + "otpReference": self.otp_reference, + "tenantId": self.tenant_id, + "roles": [role.to_dict() for role in self.roles] + } + +class CitizenUserBuilder: + """Builder class for creating CitizenUser objects""" + + def __init__(self): + # Required fields + self._user_name: Optional[str] = None + self._password: Optional[str] = None + self._name: Optional[str] = None + self._gender: Optional[str] = None + self._mobile_number: Optional[str] = None + self._tenant_id: Optional[str] = None + self._roles: List[Role] = [] + + # Optional fields with defaults + self._active: bool = True + self._locale: str = "en_IN" + self._type: str = "CITIZEN" + self._account_locked: bool = False + + # Optional fields + self._salutation: Optional[str] = None + self._email_id: Optional[str] = None + self._alt_contact_number: Optional[str] = None + self._pan: Optional[str] = None + self._aadhaar_number: Optional[str] = None + self._permanent_address: Optional[str] = None + self._permanent_city: Optional[str] = None + self._permanent_pincode: Optional[str] = None + self._correspondence_city: Optional[str] = None + self._correspondence_pincode: Optional[str] = None + self._correspondence_address: Optional[str] = None + self._dob: Optional[str] = None + self._pwd_expiry_date: Optional[str] = None + self._signature: Optional[str] = None + self._father_or_husband_name: Optional[str] = None + self._blood_group: Optional[str] = None + self._identification_mark: Optional[str] = None + self._photo: Optional[str] = None + self._otp_reference: Optional[str] = None + + def with_user_name(self, user_name: str) -> 'CitizenUserBuilder': + self._user_name = user_name + return self + + def with_password(self, password: str) -> 'CitizenUserBuilder': + self._password = password + return self + + def with_name(self, name: str) -> 'CitizenUserBuilder': + self._name = name + return self + + def with_gender(self, gender: str) -> 'CitizenUserBuilder': + self._gender = gender + return self + + def with_mobile_number(self, mobile_number: str) -> 'CitizenUserBuilder': + self._mobile_number = mobile_number + return self + + def with_tenant_id(self, tenant_id: str) -> 'CitizenUserBuilder': + self._tenant_id = tenant_id + return self + + def with_role(self, role: Role) -> 'CitizenUserBuilder': + self._roles.append(deepcopy(role)) + return self + + def with_roles(self, roles: List[Role]) -> 'CitizenUserBuilder': + self._roles.extend(deepcopy(roles)) + return self + + def with_salutation(self, salutation: str) -> 'CitizenUserBuilder': + self._salutation = salutation + return self + + def with_email(self, email_id: str) -> 'CitizenUserBuilder': + self._email_id = email_id + return self + + def with_alt_contact_number(self, alt_contact_number: str) -> 'CitizenUserBuilder': + self._alt_contact_number = alt_contact_number + return self + + def with_pan(self, pan: str) -> 'CitizenUserBuilder': + self._pan = pan + return self + + def with_aadhaar(self, aadhaar_number: str) -> 'CitizenUserBuilder': + self._aadhaar_number = aadhaar_number + return self + + def with_permanent_address(self, address: str) -> 'CitizenUserBuilder': + self._permanent_address = address + return self + + def with_permanent_city(self, city: str) -> 'CitizenUserBuilder': + self._permanent_city = city + return self + + def with_permanent_pincode(self, pincode: str) -> 'CitizenUserBuilder': + self._permanent_pincode = pincode + return self + + def with_correspondence_city(self, city: str) -> 'CitizenUserBuilder': + self._correspondence_city = city + return self + + def with_correspondence_pincode(self, pincode: str) -> 'CitizenUserBuilder': + self._correspondence_pincode = pincode + return self + + def with_correspondence_address(self, address: str) -> 'CitizenUserBuilder': + self._correspondence_address = address + return self + + def with_active(self, active: bool) -> 'CitizenUserBuilder': + self._active = active + return self + + def with_dob(self, dob: str) -> 'CitizenUserBuilder': + self._dob = dob + return self + + def with_pwd_expiry_date(self, expiry_date: str) -> 'CitizenUserBuilder': + self._pwd_expiry_date = expiry_date + return self + + def with_locale(self, locale: str) -> 'CitizenUserBuilder': + self._locale = locale + return self + + def with_type(self, type: str) -> 'CitizenUserBuilder': + self._type = type + return self + + def with_signature(self, signature: str) -> 'CitizenUserBuilder': + self._signature = signature + return self + + def with_account_locked(self, locked: bool) -> 'CitizenUserBuilder': + self._account_locked = locked + return self + + def with_father_or_husband_name(self, name: str) -> 'CitizenUserBuilder': + self._father_or_husband_name = name + return self + + def with_blood_group(self, blood_group: str) -> 'CitizenUserBuilder': + self._blood_group = blood_group + return self + + def with_identification_mark(self, mark: str) -> 'CitizenUserBuilder': + self._identification_mark = mark + return self + + def with_photo(self, photo: str) -> 'CitizenUserBuilder': + self._photo = photo + return self + + def with_otp_reference(self, otp_reference: str) -> 'CitizenUserBuilder': + self._otp_reference = otp_reference + return self + + def build(self) -> CitizenUser: + """Build and validate the CitizenUser object""" + # Validate required fields + required_fields = { + 'user_name': self._user_name, + 'password': self._password, + 'name': self._name, + 'gender': self._gender, + 'mobile_number': self._mobile_number, + 'tenant_id': self._tenant_id + } + + missing_fields = [field for field, value in required_fields.items() if value is None] + if missing_fields: + raise ValueError(f"Missing required fields: {', '.join(missing_fields)}") + + if not self._roles: + # Add default CITIZEN role if none provided + self._roles.append(Role( + code="CITIZEN", + name="Citizen", + tenant_id=self._tenant_id + )) + + return CitizenUser( + user_name=self._user_name, + password=self._password, + salutation=self._salutation, + name=self._name, + gender=self._gender, + mobile_number=self._mobile_number, + email_id=self._email_id, + tenant_id=self._tenant_id, + roles=self._roles, + alt_contact_number=self._alt_contact_number, + pan=self._pan, + aadhaar_number=self._aadhaar_number, + permanent_address=self._permanent_address, + permanent_city=self._permanent_city, + permanent_pincode=self._permanent_pincode, + correspondence_city=self._correspondence_city, + correspondence_pincode=self._correspondence_pincode, + correspondence_address=self._correspondence_address, + active=self._active, + dob=self._dob, + pwd_expiry_date=self._pwd_expiry_date, + locale=self._locale, + type=self._type, + signature=self._signature, + account_locked=self._account_locked, + father_or_husband_name=self._father_or_husband_name, + blood_group=self._blood_group, + identification_mark=self._identification_mark, + photo=self._photo, + otp_reference=self._otp_reference + ) \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/models/mdms.py b/accelerators/digit_client/digit_client/models/mdms.py new file mode 100644 index 00000000000..04321a9bd0b --- /dev/null +++ b/accelerators/digit_client/digit_client/models/mdms.py @@ -0,0 +1,221 @@ +from dataclasses import dataclass, field +from typing import List, Optional, Set, Dict +from ..request_config import RequestInfo + +@dataclass +class MasterDetail: + """ + Model for master detail parameters + """ + name: str + filter: Optional[str] = None + + def __post_init__(self): + if not self.name or len(self.name) > 100: + raise ValueError("name must be between 1 and 100 characters") + if self.filter and len(self.filter) > 500: + raise ValueError("filter must be between 1 and 500 characters") + + def to_dict(self) -> dict: + result = {'name': self.name} + if self.filter: + result['filter'] = self.filter + return result + +@dataclass +class ModuleDetail: + """ + Model for module detail parameters + """ + module_name: str + master_details: List[MasterDetail] = field(default_factory=list) + + def __post_init__(self): + if not self.module_name or len(self.module_name) > 100: + raise ValueError("module_name must be between 1 and 100 characters") + + def add_master_details_item(self, master_details_item: MasterDetail) -> 'ModuleDetail': + self.master_details.append(master_details_item) + return self + + def to_dict(self) -> dict: + return { + 'moduleName': self.module_name, + 'masterDetails': [master.to_dict() for master in self.master_details] + } + +@dataclass +class MdmsCriteria: + """ + Model for MDMS criteria parameters + """ + tenant_id: str + module_details: List[ModuleDetail] + ids: Optional[Set[str]] = None + unique_identifier: Optional[str] = None + schema_code_filter_map: Optional[Dict[str, str]] = field(default=None, repr=False) + is_active: bool = field(default=True, repr=False) + + def __post_init__(self): + if not self.tenant_id or len(self.tenant_id) > 100: + raise ValueError("tenant_id must be between 1 and 100 characters") + if self.unique_identifier and len(self.unique_identifier) > 64: + raise ValueError("unique_identifier must be between 1 and 64 characters") + + def add_module_details_item(self, module_details_item: ModuleDetail) -> 'MdmsCriteria': + if not self.module_details: + self.module_details = [] + self.module_details.append(module_details_item) + return self + + def to_dict(self) -> dict: + result = { + 'tenantId': self.tenant_id, + 'moduleDetails': [module.to_dict() for module in self.module_details] + } + + if self.ids: + result['ids'] = list(self.ids) + if self.unique_identifier: + result['uniqueIdentifier'] = self.unique_identifier + + return result + +class MdmsCriteriaBuilder: + """Builder class for creating MdmsCriteria objects""" + + def __init__(self): + self._tenant_id: Optional[str] = None + self._module_details: List[ModuleDetail] = [] + self._ids: Optional[Set[str]] = None + self._unique_identifier: Optional[str] = None + self._schema_code_filter_map: Optional[Dict[str, str]] = None + self._is_active: bool = True + + def with_tenant_id(self, tenant_id: str) -> 'MdmsCriteriaBuilder': + self._tenant_id = tenant_id + return self + + def with_module_details(self, module_details: List[ModuleDetail]) -> 'MdmsCriteriaBuilder': + self._module_details = module_details + return self + + def add_module_detail(self, module_detail: ModuleDetail) -> 'MdmsCriteriaBuilder': + self._module_details.append(module_detail) + return self + + def with_ids(self, ids: Set[str]) -> 'MdmsCriteriaBuilder': + self._ids = ids + return self + + def with_unique_identifier(self, unique_identifier: str) -> 'MdmsCriteriaBuilder': + self._unique_identifier = unique_identifier + return self + + def with_schema_code_filter_map(self, schema_code_filter_map: Dict[str, str]) -> 'MdmsCriteriaBuilder': + self._schema_code_filter_map = schema_code_filter_map + return self + + def with_is_active(self, is_active: bool) -> 'MdmsCriteriaBuilder': + self._is_active = is_active + return self + + def build(self) -> MdmsCriteria: + if not self._tenant_id: + raise ValueError("tenant_id is required") + if not self._module_details: + raise ValueError("module_details is required") + + return MdmsCriteria( + tenant_id=self._tenant_id, + module_details=self._module_details, + ids=self._ids, + unique_identifier=self._unique_identifier, + schema_code_filter_map=self._schema_code_filter_map, + is_active=self._is_active + ) + +@dataclass +class MdmsCriteriaReq: + """ + Model for MDMS criteria request parameters + """ + request_info: Optional[RequestInfo] = None + mdms_criteria: Optional[MdmsCriteria] = None + + def to_dict(self) -> dict: + return { + 'RequestInfo': self.request_info.to_dict() if self.request_info else None, + 'MdmsCriteria': self.mdms_criteria.to_dict() if self.mdms_criteria else None + } + +class MdmsCriteriaReqBuilder: + """Builder class for creating MdmsCriteriaReq objects""" + + def __init__(self): + self._request_info: Optional[RequestInfo] = None + self._mdms_criteria: Optional[MdmsCriteria] = None + + def with_request_info(self, request_info: RequestInfo) -> 'MdmsCriteriaReqBuilder': + self._request_info = request_info + return self + + def with_mdms_criteria(self, mdms_criteria: MdmsCriteria) -> 'MdmsCriteriaReqBuilder': + self._mdms_criteria = mdms_criteria + return self + + def build(self) -> MdmsCriteriaReq: + return MdmsCriteriaReq( + request_info=self._request_info, + mdms_criteria=self._mdms_criteria + ) + +class MasterDetailBuilder: + """Builder class for creating MasterDetail objects""" + + def __init__(self): + self._name: Optional[str] = None + self._filter: Optional[str] = None + + def with_name(self, name: str) -> 'MasterDetailBuilder': + self._name = name + return self + + def with_filter(self, filter: str) -> 'MasterDetailBuilder': + self._filter = filter + return self + + def build(self) -> MasterDetail: + if not self._name: + raise ValueError("name is required") + return MasterDetail( + name=self._name, + filter=self._filter + ) + +class ModuleDetailBuilder: + """Builder class for creating ModuleDetail objects""" + + def __init__(self): + self._module_name: Optional[str] = None + self._master_details: List[MasterDetail] = [] + + def with_module_name(self, module_name: str) -> 'ModuleDetailBuilder': + self._module_name = module_name + return self + + def with_master_details(self, master_details: List[MasterDetail]) -> 'ModuleDetailBuilder': + self._master_details = master_details + return self + + def add_master_detail(self, master_detail: MasterDetail) -> 'ModuleDetailBuilder': + self._master_details.append(master_detail) + return self + + def build(self) -> ModuleDetail: + if not self._module_name: + raise ValueError("module_name is required") + return ModuleDetail( + module_name=self._module_name, + master_details=self._master_details + ) \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/models/mdms_v2.py b/accelerators/digit_client/digit_client/models/mdms_v2.py new file mode 100644 index 00000000000..a6702b0c037 --- /dev/null +++ b/accelerators/digit_client/digit_client/models/mdms_v2.py @@ -0,0 +1,467 @@ +from dataclasses import dataclass, field +from typing import Optional, Dict, Any, List, Set +from ..request_config import RequestInfo + +@dataclass +class AuditDetails: + """ + Model for audit details + """ + created_by: Optional[str] = None + last_modified_by: Optional[str] = None + created_time: Optional[int] = None + last_modified_time: Optional[int] = None + + def to_dict(self) -> Dict[str, Any]: + result = {} + if self.created_by: + result['createdBy'] = self.created_by + if self.last_modified_by: + result['lastModifiedBy'] = self.last_modified_by + if self.created_time: + result['createdTime'] = self.created_time + if self.last_modified_time: + result['lastModifiedTime'] = self.last_modified_time + return result + +@dataclass +class SchemaDefinition: + """ + Model for schema definition + """ + tenant_id: str + code: str + definition: Dict[str, Any] + id: Optional[str] = None + description: Optional[str] = None + is_active: bool = True + audit_details: Optional[AuditDetails] = None + + def __post_init__(self): + if self.id and (len(self.id) < 2 or len(self.id) > 128): + raise ValueError("id must be between 2 and 128 characters") + if len(self.tenant_id) < 2 or len(self.tenant_id) > 128: + raise ValueError("tenant_id must be between 2 and 128 characters") + if len(self.code) < 2 or len(self.code) > 128: + raise ValueError("code must be between 2 and 128 characters") + if self.description and (len(self.description) < 2 or len(self.description) > 512): + raise ValueError("description must be between 2 and 512 characters") + + def to_dict(self) -> Dict[str, Any]: + result = { + 'tenantId': self.tenant_id, + 'code': self.code, + 'definition': self.definition, + 'isActive': self.is_active + } + if self.id: + result['id'] = self.id + if self.description: + result['description'] = self.description + if self.audit_details: + result['auditDetails'] = self.audit_details.to_dict() + return result + +@dataclass +class SchemaDefinitionRequest: + """ + Model for schema definition request + """ + request_info: RequestInfo + schema_definition: SchemaDefinition + + def to_dict(self) -> Dict[str, Any]: + return { + 'RequestInfo': self.request_info.to_dict(), + 'SchemaDefinition': self.schema_definition.to_dict() + } + +@dataclass +class SchemaDefCriteria: + """ + Model for schema definition search criteria + """ + tenant_id: str + codes: Optional[List[str]] = field(default_factory=list) + offset: Optional[int] = None + limit: Optional[int] = None + + def __post_init__(self): + if not self.tenant_id or len(self.tenant_id) < 1 or len(self.tenant_id) > 100: + raise ValueError("tenant_id is required and must be between 1 and 100 characters") + + def add_codes_item(self, code: str) -> 'SchemaDefCriteria': + """ + Add a code to the codes list + """ + if not self.codes: + self.codes = [] + self.codes.append(code) + return self + + def to_dict(self) -> Dict[str, Any]: + result = { + 'tenantId': self.tenant_id + } + if self.codes: + result['codes'] = self.codes + if self.offset is not None: + result['offset'] = self.offset + if self.limit is not None: + result['limit'] = self.limit + return result + +class AuditDetailsBuilder: + """Builder class for creating AuditDetails objects""" + def __init__(self): + self._created_by: Optional[str] = None + self._last_modified_by: Optional[str] = None + self._created_time: Optional[int] = None + self._last_modified_time: Optional[int] = None + + def with_created_by(self, created_by: str) -> 'AuditDetailsBuilder': + self._created_by = created_by + return self + + def with_last_modified_by(self, last_modified_by: str) -> 'AuditDetailsBuilder': + self._last_modified_by = last_modified_by + return self + + def with_created_time(self, created_time: int) -> 'AuditDetailsBuilder': + self._created_time = created_time + return self + + def with_last_modified_time(self, last_modified_time: int) -> 'AuditDetailsBuilder': + self._last_modified_time = last_modified_time + return self + + def build(self) -> AuditDetails: + return AuditDetails( + created_by=self._created_by, + last_modified_by=self._last_modified_by, + created_time=self._created_time, + last_modified_time=self._last_modified_time + ) + +class SchemaDefinitionBuilder: + """Builder class for creating SchemaDefinition objects""" + def __init__(self): + self._id: Optional[str] = None + self._tenant_id: Optional[str] = None + self._code: Optional[str] = None + self._description: Optional[str] = None + self._definition: Optional[Dict[str, Any]] = None + self._is_active: bool = True + self._audit_details: Optional[AuditDetails] = None + + def with_id(self, id: str) -> 'SchemaDefinitionBuilder': + self._id = id + return self + + def with_tenant_id(self, tenant_id: str) -> 'SchemaDefinitionBuilder': + self._tenant_id = tenant_id + return self + + def with_code(self, code: str) -> 'SchemaDefinitionBuilder': + self._code = code + return self + + def with_description(self, description: str) -> 'SchemaDefinitionBuilder': + self._description = description + return self + + def with_definition(self, definition: Dict[str, Any]) -> 'SchemaDefinitionBuilder': + self._definition = definition + return self + + def with_is_active(self, is_active: bool) -> 'SchemaDefinitionBuilder': + self._is_active = is_active + return self + + def with_audit_details(self, audit_details: AuditDetails) -> 'SchemaDefinitionBuilder': + self._audit_details = audit_details + return self + + def build(self) -> SchemaDefinition: + if not self._tenant_id: + raise ValueError("tenant_id is required") + if not self._code: + raise ValueError("code is required") + if not self._definition: + raise ValueError("definition is required") + + return SchemaDefinition( + id=self._id, + tenant_id=self._tenant_id, + code=self._code, + description=self._description, + definition=self._definition, + is_active=self._is_active, + audit_details=self._audit_details + ) + +class SchemaDefinitionRequestBuilder: + """Builder class for creating SchemaDefinitionRequest objects""" + def __init__(self): + self._request_info: Optional[RequestInfo] = None + self._schema_definition: Optional[SchemaDefinition] = None + + def with_request_info(self, request_info: RequestInfo) -> 'SchemaDefinitionRequestBuilder': + self._request_info = request_info + return self + + def with_schema_definition(self, schema_definition: SchemaDefinition) -> 'SchemaDefinitionRequestBuilder': + self._schema_definition = schema_definition + return self + + def build(self) -> SchemaDefinitionRequest: + if not self._request_info: + raise ValueError("request_info is required") + if not self._schema_definition: + raise ValueError("schema_definition is required") + + return SchemaDefinitionRequest( + request_info=self._request_info, + schema_definition=self._schema_definition + ) + +class SchemaDefCriteriaBuilder: + """Builder class for creating SchemaDefCriteria objects""" + def __init__(self): + self._tenant_id: Optional[str] = None + self._codes: List[str] = [] + self._offset: Optional[int] = None + self._limit: Optional[int] = None + + def with_tenant_id(self, tenant_id: str) -> 'SchemaDefCriteriaBuilder': + self._tenant_id = tenant_id + return self + + def with_codes(self, codes: List[str]) -> 'SchemaDefCriteriaBuilder': + self._codes = codes + return self + + def add_code(self, code: str) -> 'SchemaDefCriteriaBuilder': + self._codes.append(code) + return self + + def with_offset(self, offset: int) -> 'SchemaDefCriteriaBuilder': + self._offset = offset + return self + + def with_limit(self, limit: int) -> 'SchemaDefCriteriaBuilder': + self._limit = limit + return self + + def build(self) -> SchemaDefCriteria: + if not self._tenant_id: + raise ValueError("tenant_id is required") + + return SchemaDefCriteria( + tenant_id=self._tenant_id, + codes=self._codes, + offset=self._offset, + limit=self._limit + ) + +@dataclass +class Mdms: + tenant_id: str + schema_code: str + data: Dict[str, Any] + id: Optional[str] = None + unique_identifier: Optional[str] = None + is_active: bool = True + audit_details: Optional[AuditDetails] = None + + def __post_init__(self): + if self.id and (len(self.id) < 2 or len(self.id) > 64): + raise ValueError("id must be between 2 and 64 characters") + if len(self.tenant_id) < 2 or len(self.tenant_id) > 128: + raise ValueError("tenant_id must be between 2 and 128 characters") + if len(self.schema_code) < 2 or len(self.schema_code) > 128: + raise ValueError("schema_code must be between 2 and 128 characters") + if self.unique_identifier and (len(self.unique_identifier) < 1 or len(self.unique_identifier) > 128): + raise ValueError("unique_identifier must be between 1 and 128 characters") + + def to_dict(self) -> Dict[str, Any]: + result = { + 'tenantId': self.tenant_id, + 'schemaCode': self.schema_code, + 'data': self.data, + 'isActive': self.is_active + } + if self.id: + result['id'] = self.id + if self.unique_identifier: + result['uniqueIdentifier'] = self.unique_identifier + if self.audit_details: + result['auditDetails'] = self.audit_details.to_dict() + return result + +@dataclass +class MdmsCriteriaV2: + tenant_id: str + ids: Optional[Set[str]] = field(default_factory=set) + unique_identifiers: Optional[Set[str]] = field(default_factory=set) + schema_code: Optional[str] = None + filter_map: Optional[Dict[str, str]] = field(default_factory=dict) + is_active: Optional[bool] = None + schema_code_filter_map: Optional[Dict[str, str]] = field(default_factory=dict) + unique_identifiers_for_ref_verification: Optional[Set[str]] = field(default_factory=set) + offset: Optional[int] = None + limit: Optional[int] = None + + def __post_init__(self): + if not self.tenant_id or len(self.tenant_id) < 1 or len(self.tenant_id) > 100: + raise ValueError("tenant_id is required and must be between 1 and 100 characters") + if self.unique_identifiers and any(len(uid) < 1 or len(uid) > 64 for uid in self.unique_identifiers): + raise ValueError("unique_identifiers must be between 1 and 64 characters") + + def to_dict(self) -> Dict[str, Any]: + result = { + 'tenantId': self.tenant_id + } + if self.ids: + result['ids'] = list(self.ids) + if self.unique_identifiers: + result['uniqueIdentifiers'] = list(self.unique_identifiers) + if self.schema_code: + result['schemaCode'] = self.schema_code + if self.filter_map: + result['filters'] = self.filter_map + if self.is_active is not None: + result['isActive'] = self.is_active + if self.offset is not None: + result['offset'] = self.offset + if self.limit is not None: + result['limit'] = self.limit + return result + +class MdmsBuilder: + """Builder class for creating Mdms objects""" + def __init__(self): + self._id: Optional[str] = None + self._tenant_id: Optional[str] = None + self._schema_code: Optional[str] = None + self._unique_identifier: Optional[str] = None + self._data: Optional[Dict[str, Any]] = None + self._is_active: bool = True + self._audit_details: Optional[AuditDetails] = None + + def with_id(self, id: str) -> 'MdmsBuilder': + self._id = id + return self + + def with_tenant_id(self, tenant_id: str) -> 'MdmsBuilder': + self._tenant_id = tenant_id + return self + + def with_schema_code(self, schema_code: str) -> 'MdmsBuilder': + self._schema_code = schema_code + return self + + def with_unique_identifier(self, unique_identifier: str) -> 'MdmsBuilder': + self._unique_identifier = unique_identifier + return self + + def with_data(self, data: Dict[str, Any]) -> 'MdmsBuilder': + self._data = data + return self + + def with_is_active(self, is_active: bool) -> 'MdmsBuilder': + self._is_active = is_active + return self + + def with_audit_details(self, audit_details: AuditDetails) -> 'MdmsBuilder': + self._audit_details = audit_details + return self + + def build(self) -> Mdms: + if not self._tenant_id: + raise ValueError("tenant_id is required") + if not self._schema_code: + raise ValueError("schema_code is required") + if not self._data: + raise ValueError("data is required") + + return Mdms( + id=self._id, + tenant_id=self._tenant_id, + schema_code=self._schema_code, + unique_identifier=self._unique_identifier, + data=self._data, + is_active=self._is_active, + audit_details=self._audit_details + ) + +class MdmsCriteriaV2Builder: + """Builder class for creating MdmsCriteriaV2 objects""" + def __init__(self): + self._tenant_id: Optional[str] = None + self._ids: Set[str] = set() + self._unique_identifiers: Set[str] = set() + self._schema_code: Optional[str] = None + self._filter_map: Dict[str, str] = {} + self._is_active: Optional[bool] = None + self._schema_code_filter_map: Dict[str, str] = {} + self._unique_identifiers_for_ref_verification: Set[str] = set() + self._offset: Optional[int] = None + self._limit: Optional[int] = None + + def with_tenant_id(self, tenant_id: str) -> 'MdmsCriteriaV2Builder': + self._tenant_id = tenant_id + return self + + def with_ids(self, ids: Set[str]) -> 'MdmsCriteriaV2Builder': + self._ids = ids + return self + + def with_unique_identifiers(self, unique_identifiers: Set[str]) -> 'MdmsCriteriaV2Builder': + self._unique_identifiers = unique_identifiers + return self + + def with_schema_code(self, schema_code: str) -> 'MdmsCriteriaV2Builder': + self._schema_code = schema_code + return self + + def with_filter_map(self, filter_map: Dict[str, str]) -> 'MdmsCriteriaV2Builder': + self._filter_map = filter_map + return self + + def with_is_active(self, is_active: bool) -> 'MdmsCriteriaV2Builder': + self._is_active = is_active + return self + + def with_schema_code_filter_map(self, schema_code_filter_map: Dict[str, str]) -> 'MdmsCriteriaV2Builder': + self._schema_code_filter_map = schema_code_filter_map + return self + + def with_unique_identifiers_for_ref_verification(self, unique_identifiers_for_ref_verification: Set[str]) -> 'MdmsCriteriaV2Builder': + self._unique_identifiers_for_ref_verification = unique_identifiers_for_ref_verification + return self + + def with_offset(self, offset: int) -> 'MdmsCriteriaV2Builder': + self._offset = offset + return self + + def with_limit(self, limit: int) -> 'MdmsCriteriaV2Builder': + self._limit = limit + return self + + def build(self) -> MdmsCriteriaV2: + if not self._tenant_id: + raise ValueError("tenant_id is required") + + return MdmsCriteriaV2( + tenant_id=self._tenant_id, + ids=self._ids, + unique_identifiers=self._unique_identifiers, + schema_code=self._schema_code, + filter_map=self._filter_map, + is_active=self._is_active, + schema_code_filter_map=self._schema_code_filter_map, + unique_identifiers_for_ref_verification=self._unique_identifiers_for_ref_verification, + offset=self._offset, + limit=self._limit + ) \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/models/search_models.py b/accelerators/digit_client/digit_client/models/search_models.py new file mode 100644 index 00000000000..592641f58da --- /dev/null +++ b/accelerators/digit_client/digit_client/models/search_models.py @@ -0,0 +1,70 @@ +from typing import List, Optional +from dataclasses import dataclass, field + +@dataclass +class UserSearchModel: + """Model for user search criteria""" + tenantId: str # Using exact field names from API + userType: Optional[str] = None # Changed from type to userType + active: Optional[bool] = None + uuid: Optional[List[str]] = None + userName: Optional[str] = None # Changed from user_name to userName + + def to_dict(self) -> dict: + """Convert the search model to a dictionary for API request""" + search_dict = { + "tenantId": self.tenantId + } + + if self.userType is not None: + search_dict["userType"] = self.userType + + if self.active is not None: + search_dict["active"] = str(self.active).lower() # Convert to "true" or "false" string + + if self.uuid: + search_dict["uuid"] = self.uuid + + if self.userName: + search_dict["userName"] = self.userName + + return search_dict + +class UserSearchModelBuilder: + def __init__(self): + self._tenant_id = None + self._user_type = None + self._active = None + self._uuid = None + self._user_name = None + + def with_tenant_id(self, tenant_id: str) -> 'UserSearchModelBuilder': + self._tenant_id = tenant_id + return self + + def with_user_type(self, user_type: str) -> 'UserSearchModelBuilder': + self._user_type = user_type + return self + + def with_active(self, active: bool) -> 'UserSearchModelBuilder': + self._active = active + return self + + def with_uuid(self, uuid: List[str]) -> 'UserSearchModelBuilder': + self._uuid = uuid + return self + + def with_user_name(self, user_name: str) -> 'UserSearchModelBuilder': + self._user_name = user_name + return self + + def build(self) -> UserSearchModel: + return UserSearchModel( + tenantId=self._tenant_id, + userType=self._user_type, + active=self._active, + uuid=self._uuid, + userName=self._user_name + ) + + diff --git a/accelerators/digit_client/digit_client/models/service.py b/accelerators/digit_client/digit_client/models/service.py deleted file mode 100644 index 11427709d30..00000000000 --- a/accelerators/digit_client/digit_client/models/service.py +++ /dev/null @@ -1,10 +0,0 @@ -class Service: - def __init__(self, service_id, service_name): - self.service_id = service_id - self.service_name = service_name - - def to_dict(self): - return { - "service_id": self.service_id, - "service_name": self.service_name - } \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/models/service_definition.py b/accelerators/digit_client/digit_client/models/service_definition.py deleted file mode 100644 index 940741bd554..00000000000 --- a/accelerators/digit_client/digit_client/models/service_definition.py +++ /dev/null @@ -1,10 +0,0 @@ -class ServiceDefinition: - def __init__(self, definition_id, definition_details): - self.definition_id = definition_id - self.definition_details = definition_details - - def to_dict(self): - return { - "definition_id": self.definition_id, - "definition_details": self.definition_details - } \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/models/user.py b/accelerators/digit_client/digit_client/models/user.py index a535f2ed7c1..646bfb038c7 100644 --- a/accelerators/digit_client/digit_client/models/user.py +++ b/accelerators/digit_client/digit_client/models/user.py @@ -1,12 +1,113 @@ +from typing import List, Optional +from dataclasses import dataclass +from copy import deepcopy +from .AuthorizationRequest import Role + +# @dataclass +# class Role: +# name: str +# code: str +# tenant_id: str + +# def to_dict(self) -> dict: +# return { +# "name": self.name, +# "code": self.code, +# "tenantId": self.tenant_id +# } + +@dataclass class User: - def __init__(self, name, mobile_number, email): - self.name = name - self.mobile_number = mobile_number - self.email = email + id: int + uuid: str + user_name: str + name: str + mobile_number: str + email_id: str + type: str + roles: List[Role] + tenant_id: str - def to_dict(self): + def to_dict(self) -> dict: return { + "id": self.id, + "uuid": self.uuid, + "userName": self.user_name, "name": self.name, - "mobile_number": self.mobile_number, - "email": self.email - } \ No newline at end of file + "mobileNumber": self.mobile_number, + "emailId": self.email_id, + "type": self.type, + "roles": [role.to_dict() for role in self.roles], + "tenantId": self.tenant_id + } + +class UserBuilder: + """Builder class for creating UserProfileUpdate objects""" + + def __init__(self): + self._roles: List[Role] = [] + self._id: Optional[int] = None + self._uuid: Optional[str] = None + self._user_name: Optional[str] = None + self._name: Optional[str] = None + self._mobile_number: Optional[str] = None + self._email_id: Optional[str] = None + self._type: str = "CITIZEN" + self._tenant_id: Optional[str] = None + + def with_id(self, id: int) -> 'UserProfileUpdateBuilder': + self._id = id + return self + + def with_uuid(self, uuid: str) -> 'UserProfileUpdateBuilder': + self._uuid = uuid + return self + + def with_user_name(self, user_name: str) -> 'UserProfileUpdateBuilder': + self._user_name = user_name + return self + + def with_name(self, name: str) -> 'UserProfileUpdateBuilder': + self._name = name + return self + + def with_mobile_number(self, mobile_number: str) -> 'UserProfileUpdateBuilder': + self._mobile_number = mobile_number + return self + + def with_email(self, email_id: str) -> 'UserProfileUpdateBuilder': + self._email_id = email_id + return self + + + def with_type(self, type: str) -> 'UserProfileUpdateBuilder': + self._type = type + return self + + def with_role(self, role: Role) -> 'UserProfileUpdateBuilder': + self._roles.append(deepcopy(role)) + return self + + def with_roles(self, roles: List[Role]) -> 'UserProfileUpdateBuilder': + self._roles.extend(deepcopy(roles)) + return self + + def with_tenant_id(self, tenant_id: str) -> 'UserProfileUpdateBuilder': + self._tenant_id = tenant_id + return self + + + def build(self) -> User: + """Build and validate the UserProfileUpdate object""" + + return User( + id=self._id, + uuid=self._uuid, + user_name=self._user_name, + name=self._name, + mobile_number=self._mobile_number, + email_id=self._email_id, + type=self._type, + roles=self._roles, + tenant_id=self._tenant_id + ) \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/models/workflow.py b/accelerators/digit_client/digit_client/models/workflow.py new file mode 100644 index 00000000000..3e3480aa85f --- /dev/null +++ b/accelerators/digit_client/digit_client/models/workflow.py @@ -0,0 +1,796 @@ +from dataclasses import dataclass, field +from typing import List, Optional, Any, Dict +from ..request_config import RequestInfo +from .user import User +from .mdms_v2 import AuditDetails + +@dataclass +class Document: + """ + Model for document details + """ + id: Optional[str] = None + tenant_id: Optional[str] = None + document_type: Optional[str] = None + file_store_id: Optional[str] = None + document_uid: Optional[str] = None + audit_details: Optional[AuditDetails] = None + + def __post_init__(self): + if self.id and len(self.id) > 64: + raise ValueError("id must be at most 64 characters") + if self.tenant_id and len(self.tenant_id) > 64: + raise ValueError("tenant_id must be at most 64 characters") + if self.document_type and len(self.document_type) > 64: + raise ValueError("document_type must be at most 64 characters") + if self.file_store_id and len(self.file_store_id) > 64: + raise ValueError("file_store_id must be at most 64 characters") + if self.document_uid and len(self.document_uid) > 64: + raise ValueError("document_uid must be at most 64 characters") + + def to_dict(self) -> Dict[str, Any]: + result = {} + if self.id: + result['id'] = self.id + if self.tenant_id: + result['tenantId'] = self.tenant_id + if self.document_type: + result['documentType'] = self.document_type + if self.file_store_id: + result['fileStoreId'] = self.file_store_id + if self.document_uid: + result['documentUid'] = self.document_uid + if self.audit_details: + result['auditDetails'] = self.audit_details.to_dict() + return result + +@dataclass +class WorkflowAction: + """ + Model for action details + """ + uuid: Optional[str] = None + tenant_id: Optional[str] = None + current_state: Optional[str] = None + action: str = field(default="") + next_state: str = field(default="") + roles: List[str] = field(default_factory=list) + audit_details: Optional[AuditDetails] = None + active: Optional[bool] = None + + def __post_init__(self): + if self.uuid and len(self.uuid) > 256: + raise ValueError("uuid must be at most 256 characters") + if self.tenant_id and len(self.tenant_id) > 256: + raise ValueError("tenant_id must be at most 256 characters") + if self.current_state and len(self.current_state) > 256: + raise ValueError("current_state must be at most 256 characters") + if not self.action: + raise ValueError("action is required") + if len(self.action) > 256: + raise ValueError("action must be at most 256 characters") + if not self.next_state: + raise ValueError("next_state is required") + if len(self.next_state) > 256: + raise ValueError("next_state must be at most 256 characters") + if not self.roles: + raise ValueError("roles is required") + if len(self.roles) > 1024: + raise ValueError("roles must be at most 1024 items") + + def add_roles_item(self, roles_item: str) -> 'Action': + if self.roles is None: + self.roles = [] + self.roles.append(roles_item) + return self + + def to_dict(self) -> Dict[str, Any]: + result = { + 'action': self.action, + 'nextState': self.next_state, + 'roles': self.roles + } + if self.uuid: + result['uuid'] = self.uuid + if self.tenant_id: + result['tenantId'] = self.tenant_id + if self.current_state: + result['currentState'] = self.current_state + if self.audit_details: + result['auditDetails'] = self.audit_details.to_dict() + if self.active is not None: + result['active'] = self.active + return result + +@dataclass +class State: + """ + Model for state details + """ + uuid: Optional[str] = None + tenant_id: Optional[str] = None + business_service_id: Optional[str] = None + sla: Optional[int] = None + state: Optional[str] = None + application_status: Optional[str] = None + doc_upload_required: Optional[bool] = None + is_start_state: Optional[bool] = None + is_terminate_state: Optional[bool] = None + is_state_updatable: Optional[bool] = None + actions: List[WorkflowAction] = field(default_factory=list) + audit_details: Optional[AuditDetails] = None + + def __post_init__(self): + if self.uuid and len(self.uuid) > 256: + raise ValueError("uuid must be at most 256 characters") + if self.tenant_id and len(self.tenant_id) > 256: + raise ValueError("tenant_id must be at most 256 characters") + if self.business_service_id and len(self.business_service_id) > 256: + raise ValueError("business_service_id must be at most 256 characters") + if self.state and len(self.state) > 256: + raise ValueError("state must be at most 256 characters") + if self.application_status and len(self.application_status) > 256: + raise ValueError("application_status must be at most 256 characters") + + def add_actions_item(self, actions_item: WorkflowAction) -> 'State': + if self.actions is None: + self.actions = [] + self.actions.append(actions_item) + return self + + def to_dict(self) -> Dict[str, Any]: + result = {} + if self.uuid: + result['uuid'] = self.uuid + if self.tenant_id: + result['tenantId'] = self.tenant_id + if self.business_service_id: + result['businessServiceId'] = self.business_service_id + if self.sla is not None: + result['sla'] = self.sla + if self.state: + result['state'] = self.state + if self.application_status: + result['applicationStatus'] = self.application_status + if self.doc_upload_required is not None: + result['docUploadRequired'] = self.doc_upload_required + if self.is_start_state is not None: + result['isStartState'] = self.is_start_state + if self.is_terminate_state is not None: + result['isTerminateState'] = self.is_terminate_state + if self.is_state_updatable is not None: + result['isStateUpdatable'] = self.is_state_updatable + if self.actions: + result['actions'] = [action.to_dict() for action in self.actions] + if self.audit_details: + result['auditDetails'] = self.audit_details.to_dict() + return result + +@dataclass +class ProcessInstance: + """ + Model for process instance + """ + tenant_id: str + business_service: str + business_id: str + action: str + module_name: str + id: Optional[str] = None + state: Optional[State] = None + comment: Optional[str] = None + documents: List[Document] = field(default_factory=list) + assigner: Optional[User] = None + assignes: List[User] = field(default_factory=list) + next_actions: List[WorkflowAction] = field(default_factory=list) + state_sla: Optional[int] = None + business_service_sla: Optional[int] = None + previous_status: Optional[str] = None + entity: Optional[Any] = None + audit_details: Optional[AuditDetails] = None + rating: Optional[int] = None + escalated: bool = False + + def __post_init__(self): + if self.id and len(self.id) > 64: + raise ValueError("id must be at most 64 characters") + if not self.tenant_id: + raise ValueError("tenant_id is required") + if len(self.tenant_id) > 128: + raise ValueError("tenant_id must be at most 128 characters") + if not self.business_service: + raise ValueError("business_service is required") + if len(self.business_service) > 128: + raise ValueError("business_service must be at most 128 characters") + if not self.business_id: + raise ValueError("business_id is required") + if len(self.business_id) > 128: + raise ValueError("business_id must be at most 128 characters") + if not self.action: + raise ValueError("action is required") + if len(self.action) > 128: + raise ValueError("action must be at most 128 characters") + if not self.module_name: + raise ValueError("module_name is required") + if len(self.module_name) > 64: + raise ValueError("module_name must be at most 64 characters") + if self.comment and len(self.comment) > 1024: + raise ValueError("comment must be at most 1024 characters") + if self.previous_status and len(self.previous_status) > 128: + raise ValueError("previous_status must be at most 128 characters") + + def add_documents_item(self, documents_item: Document) -> 'ProcessInstance': + if self.documents is None: + self.documents = [] + if documents_item not in self.documents: + self.documents.append(documents_item) + return self + + def add_next_actions_item(self, next_actions_item: WorkflowAction) -> 'ProcessInstance': + if self.next_actions is None: + self.next_actions = [] + self.next_actions.append(next_actions_item) + return self + + def add_users_item(self, users_item: User) -> 'ProcessInstance': + if self.assignes is None: + self.assignes = [] + if users_item not in self.assignes: + self.assignes.append(users_item) + return self + + def to_dict(self) -> Dict[str, Any]: + result = { + 'tenantId': self.tenant_id, + 'businessService': self.business_service, + 'businessId': self.business_id, + 'action': self.action, + 'moduleName': self.module_name + } + if self.id: + result['id'] = self.id + if self.state: + result['state'] = self.state.to_dict() + if self.comment: + result['comment'] = self.comment + if self.documents: + result['documents'] = [doc.to_dict() for doc in self.documents] + if self.assigner: + result['assigner'] = self.assigner.to_dict() + if self.assignes: + result['assignes'] = [user.to_dict() for user in self.assignes] + if self.next_actions: + result['nextActions'] = [action.to_dict() for action in self.next_actions] + if self.state_sla is not None: + result['stateSla'] = self.state_sla + if self.business_service_sla is not None: + result['businesssServiceSla'] = self.business_service_sla + if self.previous_status: + result['previousStatus'] = self.previous_status + if self.entity: + result['entity'] = self.entity + if self.audit_details: + result['auditDetails'] = self.audit_details.to_dict() + if self.rating is not None: + result['rating'] = self.rating + result['escalated'] = self.escalated + return result + +# Builder classes +class DocumentBuilder: + """Builder for creating Document objects""" + def __init__(self): + self._id: Optional[str] = None + self._tenant_id: Optional[str] = None + self._document_type: Optional[str] = None + self._file_store_id: Optional[str] = None + self._document_uid: Optional[str] = None + self._audit_details: Optional[AuditDetails] = None + + def with_id(self, id: str) -> 'DocumentBuilder': + self._id = id + return self + + def with_tenant_id(self, tenant_id: str) -> 'DocumentBuilder': + self._tenant_id = tenant_id + return self + + def with_document_type(self, document_type: str) -> 'DocumentBuilder': + self._document_type = document_type + return self + + def with_file_store_id(self, file_store_id: str) -> 'DocumentBuilder': + self._file_store_id = file_store_id + return self + + def with_document_uid(self, document_uid: str) -> 'DocumentBuilder': + self._document_uid = document_uid + return self + + def with_audit_details(self, audit_details: AuditDetails) -> 'DocumentBuilder': + self._audit_details = audit_details + return self + + def build(self) -> Document: + return Document( + id=self._id, + tenant_id=self._tenant_id, + document_type=self._document_type, + file_store_id=self._file_store_id, + document_uid=self._document_uid, + audit_details=self._audit_details + ) + +class WorkflowActionBuilder: + """Builder for creating Action objects""" + def __init__(self): + self._uuid: Optional[str] = None + self._tenant_id: Optional[str] = None + self._current_state: Optional[str] = None + self._action: Optional[str] = None + self._next_state: Optional[str] = None + self._roles: List[str] = [] + self._audit_details: Optional[AuditDetails] = None + self._active: Optional[bool] = None + + def with_uuid(self, uuid: str) -> 'ActionBuilder': + self._uuid = uuid + return self + + def with_tenant_id(self, tenant_id: str) -> 'ActionBuilder': + self._tenant_id = tenant_id + return self + + def with_current_state(self, current_state: str) -> 'ActionBuilder': + self._current_state = current_state + return self + + def with_action(self, action: str) -> 'ActionBuilder': + self._action = action + return self + + def with_next_state(self, next_state: str) -> 'ActionBuilder': + self._next_state = next_state + return self + + def with_roles(self, roles: List[str]) -> 'ActionBuilder': + self._roles = roles + return self + + def add_role(self, role: str) -> 'ActionBuilder': + self._roles.append(role) + return self + + def with_audit_details(self, audit_details: AuditDetails) -> 'ActionBuilder': + self._audit_details = audit_details + return self + + def with_active(self, active: bool) -> 'ActionBuilder': + self._active = active + return self + + def build(self) -> WorkflowAction: + if not self._action: + raise ValueError("action is required") + if not self._next_state: + raise ValueError("next_state is required") + if not self._roles: + raise ValueError("roles is required") + + return WorkflowAction( + uuid=self._uuid, + tenant_id=self._tenant_id, + current_state=self._current_state, + action=self._action, + next_state=self._next_state, + roles=self._roles, + audit_details=self._audit_details, + active=self._active + ) + +class StateBuilder: + """Builder for creating State objects""" + def __init__(self): + self._uuid: Optional[str] = None + self._tenant_id: Optional[str] = None + self._business_service_id: Optional[str] = None + self._sla: Optional[int] = None + self._state: Optional[str] = None + self._application_status: Optional[str] = None + self._doc_upload_required: Optional[bool] = None + self._is_start_state: Optional[bool] = None + self._is_terminate_state: Optional[bool] = None + self._is_state_updatable: Optional[bool] = None + self._actions: List[WorkflowAction] = [] + self._audit_details: Optional[AuditDetails] = None + + def with_uuid(self, uuid: str) -> 'StateBuilder': + self._uuid = uuid + return self + + def with_tenant_id(self, tenant_id: str) -> 'StateBuilder': + self._tenant_id = tenant_id + return self + + def with_business_service_id(self, business_service_id: str) -> 'StateBuilder': + self._business_service_id = business_service_id + return self + + def with_sla(self, sla: int) -> 'StateBuilder': + self._sla = sla + return self + + def with_state(self, state: str) -> 'StateBuilder': + self._state = state + return self + + def with_application_status(self, application_status: str) -> 'StateBuilder': + self._application_status = application_status + return self + + def with_doc_upload_required(self, doc_upload_required: bool) -> 'StateBuilder': + self._doc_upload_required = doc_upload_required + return self + + def with_is_start_state(self, is_start_state: bool) -> 'StateBuilder': + self._is_start_state = is_start_state + return self + + def with_is_terminate_state(self, is_terminate_state: bool) -> 'StateBuilder': + self._is_terminate_state = is_terminate_state + return self + + def with_is_state_updatable(self, is_state_updatable: bool) -> 'StateBuilder': + self._is_state_updatable = is_state_updatable + return self + + def with_actions(self, actions: List[WorkflowAction]) -> 'StateBuilder': + self._actions = actions + return self + + def with_audit_details(self, audit_details: AuditDetails) -> 'StateBuilder': + self._audit_details = audit_details + return self + + def build(self) -> State: + return State( + uuid=self._uuid, + tenant_id=self._tenant_id, + business_service_id=self._business_service_id, + sla=self._sla, + state=self._state, + application_status=self._application_status, + doc_upload_required=self._doc_upload_required, + is_start_state=self._is_start_state, + is_terminate_state=self._is_terminate_state, + is_state_updatable=self._is_state_updatable, + actions=self._actions, + audit_details=self._audit_details + ) + +class ProcessInstanceBuilder: + """Builder for creating ProcessInstance objects""" + def __init__(self): + self._tenant_id: Optional[str] = None + self._business_service: Optional[str] = None + self._business_id: Optional[str] = None + self._action: Optional[str] = None + self._module_name: Optional[str] = None + self._id: Optional[str] = None + self._state: Optional[State] = None + self._comment: Optional[str] = None + self._documents: List[Document] = [] + self._assigner: Optional[User] = None + self._assignes: List[User] = [] + self._next_actions: List[WorkflowAction] = [] + self._state_sla: Optional[int] = None + self._business_service_sla: Optional[int] = None + self._previous_status: Optional[str] = None + self._entity: Optional[Any] = None + self._audit_details: Optional[AuditDetails] = None + self._rating: Optional[int] = None + self._escalated: Optional[bool] = None + + def with_tenant_id(self, tenant_id: str) -> 'ProcessInstanceBuilder': + self._tenant_id = tenant_id + return self + + def with_business_service(self, business_service: str) -> 'ProcessInstanceBuilder': + self._business_service = business_service + return self + + def with_business_id(self, business_id: str) -> 'ProcessInstanceBuilder': + self._business_id = business_id + return self + + def with_action(self, action: str) -> 'ProcessInstanceBuilder': + self._action = action + return self + + def with_module_name(self, module_name: str) -> 'ProcessInstanceBuilder': + self._module_name = module_name + return self + + def with_id(self, id: str) -> 'ProcessInstanceBuilder': + self._id = id + return self + + def with_state(self, state: State) -> 'ProcessInstanceBuilder': + self._state = state + return self + + def with_comment(self, comment: str) -> 'ProcessInstanceBuilder': + self._comment = comment + return self + + def with_documents(self, documents: List[Document]) -> 'ProcessInstanceBuilder': + self._documents = documents + return self + + def with_assigner(self, assigner: User) -> 'ProcessInstanceBuilder': + self._assigner = assigner + return self + + def with_assignes(self, assignes: List[User]) -> 'ProcessInstanceBuilder': + self._assignes = assignes + return self + + def with_next_actions(self, next_actions: List[WorkflowAction]) -> 'ProcessInstanceBuilder': + self._next_actions = next_actions + return self + + def with_state_sla(self, state_sla: int) -> 'ProcessInstanceBuilder': + self._state_sla = state_sla + return self + + def with_business_service_sla(self, business_service_sla: int) -> 'ProcessInstanceBuilder': + self._business_service_sla = business_service_sla + return self + + def with_previous_status(self, previous_status: str) -> 'ProcessInstanceBuilder': + self._previous_status = previous_status + return self + + def with_entity(self, entity: Any) -> 'ProcessInstanceBuilder': + self._entity = entity + return self + + def with_audit_details(self, audit_details: AuditDetails) -> 'ProcessInstanceBuilder': + self._audit_details = audit_details + return self + + def with_rating(self, rating: int) -> 'ProcessInstanceBuilder': + self._rating = rating + return self + + def with_escalated(self, escalated: bool) -> 'ProcessInstanceBuilder': + self._escalated = escalated + return self + + def build(self) -> ProcessInstance: + return ProcessInstance( + tenant_id=self._tenant_id, + business_service=self._business_service, + business_id=self._business_id, + action=self._action, + module_name=self._module_name, + id=self._id, + state=self._state, + comment=self._comment, + documents=self._documents, + assigner=self._assigner, + assignes=self._assignes, + next_actions=self._next_actions, + state_sla=self._state_sla, + business_service_sla=self._business_service_sla, + previous_status=self._previous_status, + entity=self._entity, + audit_details=self._audit_details, + rating=self._rating, + escalated=self._escalated + ) + +@dataclass +class ProcessInstanceSearchCriteria: + """ + Model for process instance search criteria + """ + tenant_id: str + status: Optional[List[str]] = None + business_ids: Optional[List[str]] = None + assignee: Optional[str] = None + ids: Optional[List[str]] = None + history: bool = False + from_date: Optional[int] = None + to_date: Optional[int] = None + offset: Optional[int] = None + limit: Optional[int] = None + business_service: Optional[str] = None + module_name: Optional[str] = None + is_nearing_sla_count: Optional[bool] = field(default=None, repr=False) + tenant_specific_status: Optional[List[str]] = field(default=None, repr=False) + multiple_assignees: Optional[List[str]] = field(default=None, repr=False) + states_to_ignore: Optional[List[str]] = field(default=None, repr=False) + is_escalated_count: Optional[bool] = field(default=None, repr=False) + is_assigned_to_me_count: Optional[bool] = field(default=None, repr=False) + statuses_irrespective_of_tenant: Optional[List[str]] = field(default=None, repr=False) + slot_percentage_sla_limit: Optional[int] = field(default=None, repr=False) + + def __post_init__(self): + if not self.tenant_id: + raise ValueError("tenant_id is required") + if self.business_ids: + for business_id in self.business_ids: + if len(business_id) < 4: + raise ValueError("business_id must be at least 4 characters") + + def is_null(self) -> bool: + """ + Check if the main search criteria are null + """ + return (self.business_ids is None and self.ids is None and + self.assignee is None and self.status is None) + + def to_dict(self) -> Dict[str, Any]: + result = { + 'tenantId': self.tenant_id + } + if self.status: + result['status'] = self.status + if self.business_ids: + result['businessIds'] = self.business_ids + if self.assignee: + result['assignee'] = self.assignee + if self.ids: + result['ids'] = self.ids + if self.history is not None: + result['history'] = self.history + if self.from_date is not None: + result['fromDate'] = self.from_date + if self.to_date is not None: + result['toDate'] = self.to_date + if self.offset is not None: + result['offset'] = self.offset + if self.limit is not None: + result['limit'] = self.limit + if self.business_service: + result['businessService'] = self.business_service + if self.module_name: + result['moduleName'] = self.module_name + return result + +class ProcessInstanceSearchCriteriaBuilder: + """Builder for creating ProcessInstanceSearchCriteria objects""" + def __init__(self): + self._tenant_id: Optional[str] = None + self._status: Optional[List[str]] = None + self._business_ids: Optional[List[str]] = None + self._assignee: Optional[str] = None + self._ids: Optional[List[str]] = None + self._history: bool = False + self._from_date: Optional[int] = None + self._to_date: Optional[int] = None + self._offset: Optional[int] = None + self._limit: Optional[int] = None + self._business_service: Optional[str] = None + self._module_name: Optional[str] = None + self._is_nearing_sla_count: Optional[bool] = None + self._tenant_specific_status: Optional[List[str]] = None + self._multiple_assignees: Optional[List[str]] = None + self._states_to_ignore: Optional[List[str]] = None + self._is_escalated_count: Optional[bool] = None + self._is_assigned_to_me_count: Optional[bool] = None + self._statuses_irrespective_of_tenant: Optional[List[str]] = None + self._slot_percentage_sla_limit: Optional[int] = None + + def with_tenant_id(self, tenant_id: str) -> 'ProcessInstanceSearchCriteriaBuilder': + self._tenant_id = tenant_id + return self + + def with_status(self, status: List[str]) -> 'ProcessInstanceSearchCriteriaBuilder': + self._status = status + return self + + def with_business_ids(self, business_ids: List[str]) -> 'ProcessInstanceSearchCriteriaBuilder': + self._business_ids = business_ids + return self + + def with_assignee(self, assignee: str) -> 'ProcessInstanceSearchCriteriaBuilder': + self._assignee = assignee + return self + + def with_ids(self, ids: List[str]) -> 'ProcessInstanceSearchCriteriaBuilder': + self._ids = ids + return self + + def with_history(self, history: bool) -> 'ProcessInstanceSearchCriteriaBuilder': + self._history = history + return self + + def with_from_date(self, from_date: int) -> 'ProcessInstanceSearchCriteriaBuilder': + self._from_date = from_date + return self + + def with_to_date(self, to_date: int) -> 'ProcessInstanceSearchCriteriaBuilder': + self._to_date = to_date + return self + + def with_offset(self, offset: int) -> 'ProcessInstanceSearchCriteriaBuilder': + self._offset = offset + return self + + def with_limit(self, limit: int) -> 'ProcessInstanceSearchCriteriaBuilder': + self._limit = limit + return self + + def with_business_service(self, business_service: str) -> 'ProcessInstanceSearchCriteriaBuilder': + self._business_service = business_service + return self + + def with_module_name(self, module_name: str) -> 'ProcessInstanceSearchCriteriaBuilder': + self._module_name = module_name + return self + + def with_is_nearing_sla_count(self, is_nearing_sla_count: bool) -> 'ProcessInstanceSearchCriteriaBuilder': + self._is_nearing_sla_count = is_nearing_sla_count + return self + + def with_tenant_specific_status(self, tenant_specific_status: List[str]) -> 'ProcessInstanceSearchCriteriaBuilder': + self._tenant_specific_status = tenant_specific_status + return self + + def with_multiple_assignees(self, multiple_assignees: List[str]) -> 'ProcessInstanceSearchCriteriaBuilder': + self._multiple_assignees = multiple_assignees + return self + + def with_states_to_ignore(self, states_to_ignore: List[str]) -> 'ProcessInstanceSearchCriteriaBuilder': + self._states_to_ignore = states_to_ignore + return self + + def with_is_escalated_count(self, is_escalated_count: bool) -> 'ProcessInstanceSearchCriteriaBuilder': + self._is_escalated_count = is_escalated_count + return self + + def with_is_assigned_to_me_count(self, is_assigned_to_me_count: bool) -> 'ProcessInstanceSearchCriteriaBuilder': + self._is_assigned_to_me_count = is_assigned_to_me_count + return self + + def with_statuses_irrespective_of_tenant(self, statuses_irrespective_of_tenant: List[str]) -> 'ProcessInstanceSearchCriteriaBuilder': + self._statuses_irrespective_of_tenant = statuses_irrespective_of_tenant + return self + + def with_slot_percentage_sla_limit(self, slot_percentage_sla_limit: int) -> 'ProcessInstanceSearchCriteriaBuilder': + self._slot_percentage_sla_limit = slot_percentage_sla_limit + return self + + def build(self) -> ProcessInstanceSearchCriteria: + if not self._tenant_id: + raise ValueError("tenant_id is required") + + return ProcessInstanceSearchCriteria( + tenant_id=self._tenant_id, + status=self._status, + business_ids=self._business_ids, + assignee=self._assignee, + ids=self._ids, + history=self._history, + from_date=self._from_date, + to_date=self._to_date, + offset=self._offset, + limit=self._limit, + business_service=self._business_service, + module_name=self._module_name, + is_nearing_sla_count=self._is_nearing_sla_count, + tenant_specific_status=self._tenant_specific_status, + multiple_assignees=self._multiple_assignees, + states_to_ignore=self._states_to_ignore, + is_escalated_count=self._is_escalated_count, + is_assigned_to_me_count=self._is_assigned_to_me_count, + statuses_irrespective_of_tenant=self._statuses_irrespective_of_tenant, + slot_percentage_sla_limit=self._slot_percentage_sla_limit + ) + + + + + diff --git a/accelerators/digit_client/digit_client/pre_install.py b/accelerators/digit_client/digit_client/pre_install.py deleted file mode 100644 index 3d151116a85..00000000000 --- a/accelerators/digit_client/digit_client/pre_install.py +++ /dev/null @@ -1,119 +0,0 @@ -import sys -from getpass import getpass -import os - -# Add parent directory to path to ensure auth.py can be imported -current_dir = os.path.dirname(os.path.abspath(__file__)) -if current_dir not in sys.path: - sys.path.insert(0, current_dir) - -from .auth import DigitAuth - -def get_user_choice(): - print("\n=== DIGIT Client Authentication ===") - print("Before using the package, you need to authenticate.") - while True: - choice = input("\nDo you want to (1) Register or (2) Login? Enter 1 or 2: ").strip() - if choice in ['1', '2']: - return choice - print("Invalid choice. Please enter 1 for Register or 2 for Login.") - -def handle_registration(): - print("\n=== Registration ===") - email = input("Enter your email: ").strip() - tenant_id = input("Enter your tenant ID: ").strip() - - try: - print("\nRegistering user...") - register_response = DigitAuth.register_user(email, tenant_id) - - # Check if registration was successful - response_info = register_response.get('ResponseInfo', {}) - tenants = register_response.get('Tenants', []) - - if not response_info.get('status') == 'successful' or not tenants: - print("\nRegistration failed:", register_response) - return False, None - - # Store the tenant code for future use - tenant_code = tenants[0].get('code') - if not tenant_code: - print("\nFailed to get tenant code from response") - return False, None - - print("\nRegistration successful! Sending OTP...") - - # Send OTP - otp_response = DigitAuth.send_otp(email, tenant_code) - if not otp_response: - print("\nFailed to send OTP") - return False, None - - print("\nOTP has been sent to your email.") - - # Validate OTP - otp = getpass("Enter the OTP sent to your email: ").strip() - print("\nValidating OTP...") - validate_response = DigitAuth.validate_otp(email, otp, tenant_code) - - access_token = validate_response.get('access_token') - if not access_token: - print("\nOTP validation failed") - return False, None - - print("\nAuthentication successful! the access token is: ", access_token) - return True, access_token - - except Exception as e: - print(f"\nAn error occurred: {str(e)}") - return False, None - -def handle_login(): - print("\n=== Login ===") - email = input("Enter your email: ").strip() - tenant_id = input("Enter your tenant ID: ").strip() - - try: - print("\nSending OTP...") - # Send OTP - otp_response = DigitAuth.send_otp(email, tenant_id) - if not otp_response: - print("\nFailed to send OTP") - return False, None - - print("\nOTP has been sent to your email.") - - # Validate OTP - otp = getpass("Enter the OTP sent to your email: ").strip() - print("\nValidating OTP...") - validate_response = DigitAuth.validate_otp(email, otp, tenant_id) - - access_token = validate_response.get('access_token') - if not access_token: - print("\nLogin failed") - return False, None - - print("\nAuthentication successful! the access token is: ", access_token) - return True, access_token - - except Exception as e: - print(f"\nAn error occurred: {str(e)}") - return False, None - -def main(): - try: - choice = get_user_choice() - - if choice == '1': - return handle_registration() - else: - return handle_login() - except KeyboardInterrupt: - print("\n\nAuthentication cancelled by user.") - return False, None - except Exception as e: - print(f"\nAn unexpected error occurred: {str(e)}") - return False, None - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/request_config.py b/accelerators/digit_client/digit_client/request_config.py new file mode 100644 index 00000000000..f283c002b82 --- /dev/null +++ b/accelerators/digit_client/digit_client/request_config.py @@ -0,0 +1,458 @@ +from typing import Optional, Dict +import time +import uuid + +class RequestInfo: + def __init__(self, api_id: str, ver: str, ts: int, action: str, + did: str = None, key: str = None, msg_id: str = None, + requester_id: str = None, auth_token: str = None, + user_info: dict = None, correlation_id: str = None): + self.api_id = api_id + self.ver = ver + self.ts = ts + self.action = action + self.did = did + self.key = key + self.msg_id = msg_id or str(uuid.uuid4()) + self.requester_id = requester_id + self.auth_token = auth_token + self.user_info = user_info + self.correlation_id = correlation_id or str(uuid.uuid4()) + + def to_dict(self) -> Dict: + return { + "apiId": self.api_id, + "ver": self.ver, + "ts": self.ts, + "action": self.action, + "did": self.did, + "key": self.key, + "msgId": self.msg_id, + "requesterId": self.requester_id, + "authToken": self.auth_token, + "userInfo": self.user_info, + "correlationId": self.correlation_id + } + + def with_auth_token(self, temp_auth_token: str) -> 'RequestInfo': + """Create a new RequestInfo instance with a temporary auth token""" + request_info_dict = self.to_dict() + request_info_dict["authToken"] = temp_auth_token + return RequestInfo( + api_id=request_info_dict["apiId"], + ver=request_info_dict["ver"], + ts=int(time.time() * 1000), # Update timestamp + action=request_info_dict["action"], + did=request_info_dict["did"], + key=request_info_dict["key"], + msg_id=str(uuid.uuid4()), # New message ID + requester_id=request_info_dict["requesterId"], + auth_token=temp_auth_token, + user_info=request_info_dict["userInfo"], + correlation_id=str(uuid.uuid4()) # New correlation ID + ) + +class RequestInfoBuilder: + """Builder class for creating RequestInfo objects""" + + _default_config = None + + def __init__(self): + # Required fields + self._api_id: Optional[str] = None + self._ver: Optional[str] = None + self._action: Optional[str] = None + + # Optional fields with defaults + self._ts: int = int(time.time() * 1000) + self._did: Optional[str] = None + self._key: Optional[str] = None + self._msg_id: Optional[str] = None + self._requester_id: Optional[str] = None + self._auth_token: Optional[str] = None + self._user_info: Optional[dict] = None + self._correlation_id: Optional[str] = None + + @classmethod + def initialize(cls, + api_id: str = "DIGIT-CLIENT", + version: str = "1.0.0", + auth_token: Optional[str] = None, + user_info: Optional[dict] = None) -> None: + """ + Initialize the default request configuration. + + Args: + api_id (str): The API ID for the application + version (str): API version + auth_token (str, optional): Default authentication token + user_info (dict, optional): User information dictionary + """ + cls._default_config = { + "api_id": api_id, + "ver": version, + "auth_token": auth_token, + "user_info": user_info + } + + @classmethod + def get_request_info(cls, action: str, temp_auth_token: Optional[str] = None, **kwargs) -> 'RequestInfo': + """ + Get a new RequestInfo instance with default values and specified overrides. + + Args: + action (str): The action being performed + temp_auth_token (str, optional): Temporary auth token to use instead of default + **kwargs: Additional parameters to override default values + + Returns: + RequestInfo: A new RequestInfo instance + """ + if cls._default_config is None: + raise RuntimeError("RequestInfoBuilder not initialized. Call RequestInfoBuilder.initialize() first.") + + builder = cls() + + # Apply default configuration + builder.with_api_id(cls._default_config["api_id"]) + builder.with_version(cls._default_config["ver"]) + if cls._default_config["auth_token"]: + builder.with_auth_token(cls._default_config["auth_token"]) + if cls._default_config["user_info"]: + builder.with_user_info(cls._default_config["user_info"]) + + # Apply action and any overrides + builder.with_action(action) + if temp_auth_token: + builder.with_auth_token(temp_auth_token) + + # Apply any additional kwargs + for key, value in kwargs.items(): + method_name = f"with_{key}" + if hasattr(builder, method_name): + getattr(builder, method_name)(value) + + return builder.build() + + def with_api_id(self, api_id: str) -> 'RequestInfoBuilder': + self._api_id = api_id + return self + + def with_version(self, ver: str) -> 'RequestInfoBuilder': + self._ver = ver + return self + + def with_action(self, action: str) -> 'RequestInfoBuilder': + self._action = action + return self + + def with_timestamp(self, ts: int) -> 'RequestInfoBuilder': + self._ts = ts + return self + + def with_did(self, did: str) -> 'RequestInfoBuilder': + self._did = did + return self + + def with_key(self, key: str) -> 'RequestInfoBuilder': + self._key = key + return self + + def with_msg_id(self, msg_id: str) -> 'RequestInfoBuilder': + self._msg_id = msg_id + return self + + def with_requester_id(self, requester_id: str) -> 'RequestInfoBuilder': + self._requester_id = requester_id + return self + + def with_auth_token(self, auth_token: str) -> 'RequestInfoBuilder': + self._auth_token = auth_token + return self + + def with_user_info(self, user_info: dict) -> 'RequestInfoBuilder': + self._user_info = user_info + return self + + def with_correlation_id(self, correlation_id: str) -> 'RequestInfoBuilder': + self._correlation_id = correlation_id + return self + + def build(self) -> RequestInfo: + """Build and validate the RequestInfo object""" + # Validate required fields + required_fields = { + 'api_id': self._api_id, + 'ver': self._ver, + 'action': self._action + } + + missing_fields = [field for field, value in required_fields.items() if value is None] + if missing_fields: + raise ValueError(f"Missing required fields: {', '.join(missing_fields)}") + + return RequestInfo( + api_id=self._api_id, + ver=self._ver, + ts=self._ts, + action=self._action, + did=self._did, + key=self._key, + msg_id=self._msg_id, + requester_id=self._requester_id, + auth_token=self._auth_token, + user_info=self._user_info, + correlation_id=self._correlation_id + ) + +class RequestConfig: + _instance = None + _default_request_info = None + + def __new__(cls): + if cls._instance is None: + cls._instance = super(RequestConfig, cls).__new__(cls) + return cls._instance + + @classmethod + def initialize(cls, + api_id: str = "DIGIT-CLIENT", + version: str = "1.0.0", + auth_token: Optional[str] = None, + user_info: Optional[dict] = None, + did: Optional[str] = None, + key: Optional[str] = None, + msg_id: Optional[str] = None, + requester_id: Optional[str] = None, + correlation_id: Optional[str] = None, + action: str = "", + ts: Optional[int] = None) -> None: + """ + Initialize the default request configuration. + + Args: + api_id (str): The API ID for the application + version (str): API version + auth_token (str, optional): Default authentication token + user_info (dict, optional): User information dictionary + did (str, optional): Device ID + key (str, optional): Key for the request + msg_id (str, optional): Message ID + requester_id (str, optional): ID of the requester + correlation_id (str, optional): Correlation ID for request tracking + action (str, optional): Action being performed + """ + cls._default_request_info = RequestInfo( + api_id=api_id, + ver=version, + ts=ts, + action=action, + auth_token=auth_token, + user_info=user_info, + did=did, + key=key, + msg_id=msg_id, + requester_id=requester_id, + correlation_id=correlation_id + ) + + @classmethod + def update_api_id(cls, api_id: str) -> None: + """Update the API ID in the existing configuration.""" + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + request_info_dict = cls._default_request_info.to_dict() + request_info_dict["apiId"] = api_id + + cls._default_request_info = RequestInfo( + api_id=api_id, + ver=request_info_dict["ver"], + ts=int(time.time() * 1000), + action=request_info_dict["action"], + did=request_info_dict["did"], + key=request_info_dict["key"], + msg_id=str(uuid.uuid4()), + requester_id=request_info_dict["requesterId"], + auth_token=request_info_dict["authToken"], + user_info=request_info_dict["userInfo"], + correlation_id=str(uuid.uuid4()) + ) + + @classmethod + def update_version(cls, version: str) -> None: + """Update the version in the existing configuration.""" + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + request_info_dict = cls._default_request_info.to_dict() + request_info_dict["ver"] = version + + cls._default_request_info = RequestInfo( + api_id=request_info_dict["apiId"], + ver=version, + ts=int(time.time() * 1000), + action=request_info_dict["action"], + did=request_info_dict["did"], + key=request_info_dict["key"], + msg_id=str(uuid.uuid4()), + requester_id=request_info_dict["requesterId"], + auth_token=request_info_dict["authToken"], + user_info=request_info_dict["userInfo"], + correlation_id=str(uuid.uuid4()) + ) + + @classmethod + def update_auth_token(cls, auth_token: str) -> None: + """Update the auth token in the existing configuration.""" + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + request_info_dict = cls._default_request_info.to_dict() + request_info_dict["authToken"] = auth_token + + cls._default_request_info = RequestInfo( + api_id=request_info_dict["apiId"], + ver=request_info_dict["ver"], + ts=int(time.time() * 1000), + action=request_info_dict["action"], + did=request_info_dict["did"], + key=request_info_dict["key"], + msg_id=str(uuid.uuid4()), + requester_id=request_info_dict["requesterId"], + auth_token=auth_token, + user_info=request_info_dict["userInfo"], + correlation_id=str(uuid.uuid4()) + ) + + @classmethod + def update_user_info(cls, user_info: dict) -> None: + """Update the user info in the existing configuration.""" + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + request_info_dict = cls._default_request_info.to_dict() + request_info_dict["userInfo"] = user_info + + cls._default_request_info = RequestInfo( + api_id=request_info_dict["apiId"], + ver=request_info_dict["ver"], + ts=int(time.time() * 1000), + action=request_info_dict["action"], + did=request_info_dict["did"], + key=request_info_dict["key"], + msg_id=str(uuid.uuid4()), + requester_id=request_info_dict["requesterId"], + auth_token=request_info_dict["authToken"], + user_info=user_info, + correlation_id=str(uuid.uuid4()) + ) + + @classmethod + def update_did(cls, did: str) -> None: + """Update the device ID in the existing configuration.""" + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + request_info_dict = cls._default_request_info.to_dict() + request_info_dict["did"] = did + + cls._default_request_info = RequestInfo( + api_id=request_info_dict["apiId"], + ver=request_info_dict["ver"], + ts=int(time.time() * 1000), + action=request_info_dict["action"], + did=did, + key=request_info_dict["key"], + msg_id=str(uuid.uuid4()), + requester_id=request_info_dict["requesterId"], + auth_token=request_info_dict["authToken"], + user_info=request_info_dict["userInfo"], + correlation_id=str(uuid.uuid4()) + ) + + @classmethod + def update_key(cls, key: str) -> None: + """Update the key in the existing configuration.""" + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + request_info_dict = cls._default_request_info.to_dict() + request_info_dict["key"] = key + + cls._default_request_info = RequestInfo( + api_id=request_info_dict["apiId"], + ver=request_info_dict["ver"], + ts=int(time.time() * 1000), + action=request_info_dict["action"], + did=request_info_dict["did"], + key=key, + msg_id=str(uuid.uuid4()), + requester_id=request_info_dict["requesterId"], + auth_token=request_info_dict["authToken"], + user_info=request_info_dict["userInfo"], + correlation_id=str(uuid.uuid4()) + ) + + @classmethod + def update_requester_id(cls, requester_id: str) -> None: + """Update the requester ID in the existing configuration.""" + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + request_info_dict = cls._default_request_info.to_dict() + request_info_dict["requesterId"] = requester_id + + cls._default_request_info = RequestInfo( + api_id=request_info_dict["apiId"], + ver=request_info_dict["ver"], + ts=int(time.time() * 1000), + action=request_info_dict["action"], + did=request_info_dict["did"], + key=request_info_dict["key"], + msg_id=str(uuid.uuid4()), + requester_id=requester_id, + auth_token=request_info_dict["authToken"], + user_info=request_info_dict["userInfo"], + correlation_id=str(uuid.uuid4()) + ) + + @classmethod + def get_request_info(cls,temp_auth_token: Optional[str] = None, **kwargs) -> RequestInfo: + """ + Get a new RequestInfo instance with default values and specified overrides. + + Args: + action (str): The action being performed + temp_auth_token (str, optional): Temporary auth token to use instead of default + **kwargs: Additional parameters to override default values + + Returns: + RequestInfo: A new RequestInfo instance + """ + if cls._default_request_info is None: + raise RuntimeError("RequestConfig not initialized. Call RequestConfig.initialize() first.") + + # Create new request info with current timestamp + request_info_dict = cls._default_request_info.to_dict() + request_info_dict.update({ + **kwargs + }) + + # Override auth token if temporary one is provided + if temp_auth_token: + request_info_dict["authToken"] = temp_auth_token + + return RequestInfo( + api_id=request_info_dict["apiId"], + ver=request_info_dict["ver"], + ts=request_info_dict["ts"], + action=request_info_dict["action"], + did=request_info_dict["did"], + key=request_info_dict["key"], + msg_id=request_info_dict["msgId"], + requester_id=request_info_dict["requesterId"], + auth_token=request_info_dict["authToken"], + user_info=request_info_dict["userInfo"], + correlation_id=request_info_dict["correlationId"] + ) \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/services/__init__.py b/accelerators/digit_client/digit_client/services/__init__.py index b232f9a6b5c..37864849c20 100644 --- a/accelerators/digit_client/digit_client/services/__init__.py +++ b/accelerators/digit_client/digit_client/services/__init__.py @@ -1 +1,8 @@ -# __init__.py for services package \ No newline at end of file +# __init__.py for services package +from .authenticate import AuthenticationService +from .user_service import UserService +from .master_data_v1 import MDMSService +from .mdms_v2 import MDMSV2Service +from .authorize import AuthorizeService +from .workflow import WorkflowV2Service +__all__ = ['AuthenticationService', 'UserService', 'MDMSService', 'MDMSV2Service', 'AuthorizeService', 'WorkflowV2Service'] \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/services/authenticate.py b/accelerators/digit_client/digit_client/services/authenticate.py new file mode 100644 index 00000000000..f652355f046 --- /dev/null +++ b/accelerators/digit_client/digit_client/services/authenticate.py @@ -0,0 +1,90 @@ +from typing import Dict, Optional +from ..api_client import APIClient +from ..models.auth import AuthenticationRequest +from ..request_config import RequestConfig, RequestInfo + +class AuthenticationService: + def __init__(self, api_client: Optional[APIClient] = None): + self.api_client = api_client or APIClient() + self.base_url = "user" + + def get_auth_token(self, auth_request: AuthenticationRequest) -> Dict: + """ + Get authentication token using password grant type + + Args: + auth_request (AuthenticationRequest): Authentication request model containing credentials + + Returns: + Dict: Response containing the auth token + """ + headers = { + 'Authorization': 'Basic ZWdvdi11c2VyLWNsaWVudDo=', + 'Content-Type': 'application/x-www-form-urlencoded' + } + + return self.api_client.post( + "oauth/token", + data=auth_request.to_dict(), + additional_headers=headers + ) + + def update_password_no_login(self, request_info: Optional[RequestInfo] = None) -> Dict: + """ + Update password without requiring login + + Args: + request_info (Optional[RequestInfo]): Request information containing auth details + + Returns: + Dict: Response from the password update API + """ + # Get request info if not provided + if request_info is None: + request_info = RequestConfig.get_request_info( + action="POST", + ) + headers = { + 'Authorization': 'Basic ZWdvdi11c2VyLWNsaWVudDo=', + 'Content-Type': 'application/x-www-form-urlencoded' + } + payload = { + "RequestInfo": request_info.to_dict() + } + + endpoint = f"{self.base_url}/password/nologin/_update" + return self.api_client.post( + endpoint, + json_data=payload, + additional_headers=headers + ) + + def logout(self, request_info: Optional[RequestInfo] = None) -> Dict: + """ + Logout the user using their access token + + Args: + request_info (Optional[RequestInfo]): Request information containing auth details + + Returns: + Dict: Response from the logout API + """ + # Get request info for logout action if not provided + if request_info is None: + request_info = RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict() + } + + # Add access token as query parameter + params = { + "access_token": temp_auth_token or request_info.auth_token + } + + endpoint = f"{self.base_url}/_logout" + return self.api_client.post( + endpoint, + json_data=payload, + params=params + ) \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/services/authorize.py b/accelerators/digit_client/digit_client/services/authorize.py new file mode 100644 index 00000000000..45136ea4acf --- /dev/null +++ b/accelerators/digit_client/digit_client/services/authorize.py @@ -0,0 +1,66 @@ +from typing import Dict, List, Optional +from ..api_client import APIClient +from ..models.AuthorizationRequest import AuthorizationRequest # New model +from ..request_config import RequestConfig, RequestInfo +from ..models.ActionRequest import ActionRequest + +class AuthorizeService: + def __init__(self, api_client: Optional[APIClient] = None): + self.api_client = api_client or APIClient() + self.base_url = "access/v1" + + def authorize_action(self, + authorization_request: AuthorizationRequest, + request_info: Optional[RequestInfo] = None, + headers: Optional[Dict] = None) -> Dict: + """ + Authorize an action based on roles and tenant permissions + + Args: + authorization_request: Authorization parameters including roles and tenant IDs + request_info: Authentication and request metadata + + Returns: + Dict: Authorization response with access decision + """ + request_info = request_info or RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict(), + "AuthorizationRequest": authorization_request.to_dict() + } + + endpoint = f"{self.base_url}/actions/_authorize" + return self.api_client.post( + endpoint, + json_data=payload, + additional_headers=headers + ) + + def get_mdms_action(self, + action_request: ActionRequest, + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Get MDMS action details + + Args: + action_request: Parameters for the MDMS action request + request_info: Authentication and request metadata. If provided, will override the request_info in action_request + + Returns: + Dict: MDMS action response data + """ + # If request_info is provided, update the action_request's request_info + if request_info: + action_request.request_info = request_info + else: + # If no request_info provided, use the global one + action_request.request_info = RequestConfig.get_request_info() + + payload = action_request.to_dict() + + endpoint = f"{self.base_url}/actions/mdms/_get" + return self.api_client.post( + endpoint, + json_data=payload + ) diff --git a/accelerators/digit_client/digit_client/services/master_data_v1.py b/accelerators/digit_client/digit_client/services/master_data_v1.py new file mode 100644 index 00000000000..a20eaef5b7f --- /dev/null +++ b/accelerators/digit_client/digit_client/services/master_data_v1.py @@ -0,0 +1,87 @@ +from typing import Dict, Optional +from ..api_client import APIClient +from ..models.mdms import MdmsCriteriaReq, MdmsCriteria +from ..request_config import RequestConfig, RequestInfo + +class MDMSService: + def __init__(self, api_client: Optional[APIClient] = None): + self.api_client = api_client or APIClient() + self.base_url = "egov-mdms-service/v1" + + def search(self, + mdms_criteria: MdmsCriteria, + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Search for MDMS data based on criteria + + Args: + mdms_criteria_req (MdmsCriteriaReq): MDMS criteria request containing search parameters + request_info (Optional[RequestInfo]): Request information containing auth details + + Returns: + Dict: Response containing the MDMS data + """ + # Get request info if not provided + if request_info is None: + request_info = RequestConfig.get_request_info() + + # Build complete request body + payload = { + "RequestInfo": request_info.to_dict(), + "MdmsCriteria": mdms_criteria.to_dict() + } + + endpoint = f"{self.base_url}/_search" + return self.api_client.post( + endpoint, + json_data=payload + ) + + def get(self, + module_name: str, + master_name: str, + mdms_criteria: MdmsCriteria, + tenant_id: str = "POR", + filter: Optional[str] = None, + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Get specific MDMS data based on module and master name + + Args: + module_name (str): Name of the module + master_name (str): Name of the master + mdms_criteria (MdmsCriteria): MDMS criteria for the request + tenant_id (str): Tenant ID (default: "POR") + filter (Optional[str]): Filter criteria + request_info (Optional[RequestInfo]): Request information containing auth details + + Returns: + Dict: Response containing the MDMS data + """ + # Get request info if not provided + if request_info is None: + request_info = RequestConfig.get_request_info() + + # Build query parameters + params = { + "moduleName": module_name, + "masterName": master_name, + "tenantId": tenant_id + } + if filter: + params["filter"] = filter + + # Build request body + mdms_criteria = mdms_criteria.to_dict() + + payload = { + "RequestInfo": request_info.to_dict(), + "MdmsCriteria": mdms_criteria + } + + endpoint = f"{self.base_url}/_get" + return self.api_client.post( + endpoint, + json_data=payload, + params=params + ) \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/services/mdms_v2.py b/accelerators/digit_client/digit_client/services/mdms_v2.py new file mode 100644 index 00000000000..abd1c690893 --- /dev/null +++ b/accelerators/digit_client/digit_client/services/mdms_v2.py @@ -0,0 +1,142 @@ +from typing import Dict, Optional, List + +from ..api_client import APIClient +from ..models.mdms_v2 import SchemaDefCriteria, SchemaDefinition, Mdms, MdmsCriteriaV2 +from ..request_config import RequestConfig, RequestInfo + +class MDMSV2Service: + def __init__(self, api_client: Optional[APIClient] = None): + self.api_client = api_client or APIClient() + self.base_url = "mdms-v2/schema/v1" + self.mdms_base_url = "mdms-v2/mdms/v1" + + def schema_create(self, + schema_definition: SchemaDefinition, + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Create a new schema definition + + Args: + schema_definition: Schema definition to create + request_info (Optional[RequestInfo]): Request information containing auth details + + Returns: + Dict: Response containing the created schema definition + """ + # Get request info if not provided + if request_info is None: + request_info = RequestConfig.get_request_info() + + # Build complete request body + payload = { + "RequestInfo": request_info.to_dict(), + "SchemaDefinition": schema_definition.to_dict() + } + + endpoint = f"{self.base_url}/_create" + return self.api_client.post( + endpoint, + json_data=payload + ) + + def schema_search(self, + criteria: SchemaDefCriteria, + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Search for schema definitions based on criteria + + Args: + criteria (SchemaDefCriteria): Search criteria for schema definitions + request_info (Optional[RequestInfo]): Request information containing auth details + + Returns: + Dict: Response containing the matching schema definitions + """ + # Get request info if not provided + if request_info is None: + request_info = RequestConfig.get_request_info() + + # Build complete request body + payload = { + "RequestInfo": request_info.to_dict(), + "SchemaDefCriteria": criteria.to_dict() + } + + endpoint = f"{self.base_url}/_search" + return self.api_client.post( + endpoint, + json_data=payload + ) + + def mdms_create(self, + schema_code: str, + mdms_data: Mdms, + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Create new MDMS data for a specific schema + + Args: + schema_code: Schema code from URL path (e.g., "Owner.car.engine1") + mdms_data: MDMS data payload + request_info: Authentication and request metadata + + Returns: + Dict: Created MDMS record + """ + request_info = request_info or RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict(), + "Mdms": mdms_data.to_dict() + } + + endpoint = f"{self.mdms_base_url}/_create/{schema_code}" + return self.api_client.post(endpoint, json_data=payload) + + def mdms_search(self, + criteria: MdmsCriteriaV2, + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Search MDMS records + + Args: + criteria: Search criteria filters + request_info: Authentication and request metadata + + Returns: + Dict: Search results + """ + request_info = request_info or RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict(), + "MdmsCriteria": criteria.to_dict() + } + + endpoint = f"{self.mdms_base_url}/_search" + return self.api_client.post(endpoint, json_data=payload) + + def mdms_update(self, + schema_code: str, + mdms_data: Mdms, + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Update existing MDMS data + + Args: + schema_code: Schema code from URL path (e.g., "TradeLicense.Usage") + mdms_data: Updated MDMS data with ID + request_info: Authentication and request metadata + + Returns: + Dict: Updated MDMS record + """ + request_info = request_info or RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict(), + "Mdms": mdms_data.to_dict() + } + + endpoint = f"{self.mdms_base_url}/_update/{schema_code}" + return self.api_client.post(endpoint, json_data=payload) \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/services/user_service.py b/accelerators/digit_client/digit_client/services/user_service.py index 205ed1e55a0..73f86eb0770 100644 --- a/accelerators/digit_client/digit_client/services/user_service.py +++ b/accelerators/digit_client/digit_client/services/user_service.py @@ -1,13 +1,167 @@ +from typing import Dict, List, Optional from ..api_client import APIClient +from ..models.citizen_user import CitizenUser +from ..models.search_models import UserSearchModel +from ..models.user import User +from ..request_config import RequestConfig, RequestInfo class UserService: - def __init__(self): - self.api_client = APIClient() + def __init__(self, api_client: Optional[APIClient] = None): + self.api_client = api_client or APIClient() + self.base_url = "user" - def create_user(self, user_data): - return self.api_client.post("user/create", user_data) + def create_citizen(self, citizen_user: CitizenUser, request_info: Optional[RequestInfo] = None) -> Dict: + """ + Create a new citizen user in the DIGIT platform. + + Args: + citizen_user (CitizenUser): The citizen user data + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig + + Returns: + Dict: Response from the user creation API + """ + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info() - def get_user(self, user_id): - return self.api_client.get(f"user/{user_id}") + payload = { + "RequestInfo": request_info.to_dict(), + "user": citizen_user.to_dict() + } - # Add more user related methods as needed \ No newline at end of file + endpoint = f"{self.base_url}/citizen/_create" + return self.api_client.post(endpoint, json_data=payload) + + def get_user_details(self, tenant_id: str, request_info: Optional[RequestInfo] = None) -> Dict: + """ + Get user details using access token. + + Args: + tenant_id (str): Tenant ID + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig + + Returns: + Dict: User details from the API + """ + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict() + } + + # Add query parameters + params = { + "tenantId": tenant_id + } + + # Use temp_auth_token from parameter if provided, otherwise use auth token from RequestInfo + if temp_auth_token: + params["access_token"] = temp_auth_token + else: + # Get auth token from RequestInfo + request_info_dict = request_info.to_dict() + if request_info_dict.get("authToken"): + params["access_token"] = request_info_dict["authToken"] + else: + raise ValueError("No access token provided and no auth token found in RequestInfo") + + endpoint = f"{self.base_url}/_details" + return self.api_client.post(endpoint, json_data=payload, params=params) + + def update_profile(self, user_profile: User, request_info: Optional[RequestInfo] = None) -> Dict: + """ + Update user profile. + + Args: + user_profile (User): The updated user profile data + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig + + Returns: + Dict: Response from the user profile update API + """ + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict(), + "user": user_profile.to_dict() + } + + endpoint = f"{self.base_url}/profile/_update" + return self.api_client.post(endpoint, json_data=payload) + + def search_users(self, search_criteria: UserSearchModel, request_info: Optional[RequestInfo] = None) -> Dict: + """ + Search for users based on given criteria + + Args: + search_criteria (UserSearchModel): The search criteria + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig + + Returns: + Dict: Search results from the API + """ + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info() + + # Combine RequestInfo with search criteria at root level + payload = { + "RequestInfo": request_info.to_dict(), + **search_criteria.to_dict() # Spread search criteria at root level + } + + endpoint = f"{self.base_url}/_search" + return self.api_client.post(endpoint, json_data=payload) + + def create_user_no_validate(self, citizen_user: CitizenUser, request_info: Optional[RequestInfo] = None) -> Dict: + """ + Create a new user without validation in the DIGIT platform. + + Args: + citizen_user (CitizenUser): The citizen user data + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig + + Returns: + Dict: Response from the user creation API + """ + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict(), + "user": citizen_user.to_dict() + } + + endpoint = f"{self.base_url}/users/_createnovalidate" + return self.api_client.post(endpoint, json_data=payload) + + def update_user_no_validate(self, user_profile: User, request_info: Optional[RequestInfo] = None) -> Dict: + """ + Update a user without validation in the DIGIT platform. + + Args: + user_profile (User): The updated user profile data + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig + + Returns: + Dict: Response from the user update API + """ + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict(), + "user": user_profile.to_dict() + } + + endpoint = f"{self.base_url}/users/_updatenovalidate" + return self.api_client.post(endpoint, json_data=payload) + + \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/services/workflow.py b/accelerators/digit_client/digit_client/services/workflow.py new file mode 100644 index 00000000000..59b125c0ab4 --- /dev/null +++ b/accelerators/digit_client/digit_client/services/workflow.py @@ -0,0 +1,220 @@ +from typing import Dict, List, Optional, Any +from ..api_client import APIClient +from ..request_config import RequestConfig, RequestInfo +from ..models.workflow import WorkflowAction, WorkflowActionBuilder, State, StateBuilder, ProcessInstance, ProcessInstanceBuilder, ProcessInstanceSearchCriteria, ProcessInstanceSearchCriteriaBuilder + +class WorkflowV2Service: + def __init__(self, api_client: Optional[APIClient] = None): + self.api_client = api_client or APIClient() + self.base_url = "egov-workflow-v2/egov-wf/process" + self.url = "egov-workflow-v2/egov-wf" + + def transition_process(self, + process_instances: List[ProcessInstance], + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Transition a workflow process to a new state + + Args: + process_instances: List of process instance objects with transition details + request_info: Authentication and request metadata + + Returns: + Dict: Response containing process instances and response info + """ + request_info = request_info or RequestConfig.get_request_info() + + # Create ProcessInstanceRequest structure + payload = { + "RequestInfo": request_info.to_dict(), + "ProcessInstances": [instance.to_dict() for instance in process_instances] + } + + endpoint = f"{self.base_url}/_transition" + return self.api_client.post( + endpoint, + json_data=payload + ) + + def search_processes(self, + search_criteria: Optional[ProcessInstanceSearchCriteria] = None, + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Search for workflow processes based on criteria + + Args: + search_criteria: Optional ProcessInstanceSearchCriteria object + request_info: Authentication and request metadata + + Returns: + Dict: Response containing process instances and total count + """ + request_info = request_info or RequestConfig.get_request_info() + + # Create RequestInfoWrapper structure + payload = { + "RequestInfo": request_info.to_dict() + } + + # Convert search criteria to dict if provided + params = {} + if search_criteria: + params = search_criteria.to_dict() + + endpoint = f"{self.base_url}/_search" + return self.api_client.post( + endpoint, + json_data=payload, + params=params + ) + + def count_processes(self, + search_criteria: Optional[ProcessInstanceSearchCriteria] = None, + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Get count of workflow processes + + Args: + count_criteria: Optional counting filters + request_info: Authentication and request metadata + + Returns: + Dict: Process count information + """ + request_info = request_info or RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict() + } + + # Add count criteria if provided + params = {} + if search_criteria: + params = search_criteria.to_dict() + + endpoint = f"{self.base_url}/_count" + return self.api_client.post( + endpoint, + json_data=payload, + params=params + ) + + def get_nearing_sla_count(self, + search_criteria: Optional[ProcessInstanceSearchCriteria] = None, + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Get count of processes nearing their SLA deadline + + Args: + criteria: Optional filtering criteria + request_info: Authentication and request metadata + + Returns: + Dict: Count of processes nearing SLA + """ + request_info = request_info or RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict() + } + + # Add criteria if provided + params = {} + if search_criteria: + params = search_criteria.to_dict() + + endpoint = f"{self.base_url}/_nearingslacount" + return self.api_client.post( + endpoint, + json_data=payload, + params=params + ) + + def get_status_count(self, + search_criteria: Optional[ProcessInstanceSearchCriteria] = None, + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Get count of processes by status + + Args: + criteria: Optional filtering criteria + request_info: Authentication and request metadata + + Returns: + Dict: Count of processes grouped by status + """ + request_info = request_info or RequestConfig.get_request_info() + + payload = { + "RequestInfo": request_info.to_dict() + } + + # Add criteria if provided + params = {} + if search_criteria: + params = search_criteria.to_dict() + + endpoint = f"{self.base_url}/_statuscount" + return self.api_client.post( + endpoint, + json_data=payload, + params=params + ) + + def auto_escalate(self, + business_service: str, + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Automatically escalate workflow processes for a specific business service + + Args: + business_service: Business service identifier (e.g., "WaterManagement") + request_info: Authentication and request metadata + + Returns: + Dict: Escalation results + """ + request_info = request_info or RequestConfig.get_request_info() + + # Create RequestInfoWrapper structure + payload = { + "RequestInfo": request_info.to_dict() + } + + endpoint = f"{self.url}/auto/{business_service}/_escalate" + return self.api_client.post( + endpoint, + json_data=payload + ) + + def search_escalations(self, + search_criteria: Optional[ProcessInstanceSearchCriteria] = None, + request_info: Optional[RequestInfo] = None) -> Dict: + """ + Search for escalated workflow processes based on criteria + + Args: + criteria: Search criteria for escalated processes + request_info: Authentication and request metadata + + Returns: + Dict: Matching escalated process instances + """ + request_info = request_info or RequestConfig.get_request_info() + + # Create RequestInfoWrapper structure + payload = { + "RequestInfo": request_info.to_dict() + } + + # Convert criteria to query parameters + params = {} + if search_criteria: + params = search_criteria.to_dict() + + endpoint = f"{self.url}/escalate/_search" + return self.api_client.post( + endpoint, + json_data=payload, + params=params + ) \ No newline at end of file diff --git a/accelerators/digit_client/dist/digit_client-0.1-py3-none-any.whl b/accelerators/digit_client/dist/digit_client-0.1-py3-none-any.whl index 8465980f56f..591c2ce6b60 100644 Binary files a/accelerators/digit_client/dist/digit_client-0.1-py3-none-any.whl and b/accelerators/digit_client/dist/digit_client-0.1-py3-none-any.whl differ diff --git a/accelerators/digit_client/setup.py b/accelerators/digit_client/setup.py index f61dcd5fb4c..26ff20d4af7 100644 --- a/accelerators/digit_client/setup.py +++ b/accelerators/digit_client/setup.py @@ -1,34 +1,34 @@ from setuptools import setup, find_packages -import os -import sys -from setuptools.command.install import install -from setuptools.command.develop import develop +# import os +# import sys +# from setuptools.command.install import install +# from setuptools.command.develop import develop -def run_auth(): - import importlib.util - current_dir = os.path.dirname(os.path.abspath(__file__)) - pre_install_path = os.path.join(current_dir, 'digit_client', 'pre_install.py') +# def run_auth(): +# import importlib.util +# current_dir = os.path.dirname(os.path.abspath(__file__)) +# pre_install_path = os.path.join(current_dir, 'digit_client', 'pre_install.py') - spec = importlib.util.spec_from_file_location("pre_install", pre_install_path) - pre_install = importlib.util.module_from_spec(spec) - spec.loader.exec_module(pre_install) +# spec = importlib.util.spec_from_file_location("pre_install", pre_install_path) +# pre_install = importlib.util.module_from_spec(spec) +# spec.loader.exec_module(pre_install) - return pre_install.main() +# return pre_install.main() -class PreInstallCommand(install): - def run(self): - # Only run authentication during pip install - if any('pip' in arg for arg in sys.argv): - if not run_auth(): - sys.exit(1) - install.run(self) +# class PreInstallCommand(install): +# def run(self): +# # Only run authentication during pip install +# if any('pip' in arg for arg in sys.argv): +# if not run_auth(): +# sys.exit(1) +# install.run(self) -class DevelopCommand(develop): - def run(self): - if any('pip' in arg for arg in sys.argv): - if not run_auth(): - sys.exit(1) - develop.run(self) +# class DevelopCommand(develop): +# def run(self): +# if any('pip' in arg for arg in sys.argv): +# if not run_auth(): +# sys.exit(1) +# develop.run(self) setup( name='digit_client', @@ -36,6 +36,7 @@ def run(self): packages=find_packages(), install_requires=[ 'requests>=2.25.1', + 'jsonschema>=4.0.0', ], description='Python client for DIGIT services with authentication', author='eGov Foundation', diff --git a/accelerators/digit_client/test.py b/accelerators/digit_client/test.py deleted file mode 100644 index 6c41f0c5cf0..00000000000 --- a/accelerators/digit_client/test.py +++ /dev/null @@ -1,2 +0,0 @@ -import digit_client -print(digit_client.__version__) \ No newline at end of file diff --git a/accelerators/digit_client/tests/test_user_service.py b/accelerators/digit_client/tests/test_user_service.py new file mode 100644 index 00000000000..4192eda3935 --- /dev/null +++ b/accelerators/digit_client/tests/test_user_service.py @@ -0,0 +1,104 @@ +import unittest +from digit_client import RequestConfig, UserService, CitizenUserBuilder, Role +from datetime import datetime + +class TestUserService(unittest.TestCase): + def setUp(self): + # Set up test environment before each test + auth_token = "ba5e5f01-dbb5-4c94-95cf-5fa52ffae078" + RequestConfig.initialize( + api_id="DIGIT-CLIENT", + version="1.0.0", + auth_token=auth_token + ) + self.user_service = UserService() + + def test_create_user_no_validate(self): + # Arrange + timestamp = datetime.now().strftime("%Y%m%d%H%M%S") + test_user = (CitizenUserBuilder() + .with_user_name(f"test_user_{timestamp}") + .with_password("Must@123NK") + .with_salutation("Ms") + .with_name("mustak") + .with_gender("MALE") + .with_mobile_number(f"93538{timestamp[-6:]}") + .with_email(f"xyz_{timestamp}@egovernments.org") + .with_alt_contact_number("") + .with_pan("VFGGC5624P") + .with_aadhaar("96a70") + .with_permanent_address("Dawakhana") + .with_permanent_city("Kaikoo") + .with_permanent_pincode("111111") + .with_correspondence_city("banglore") + .with_correspondence_pincode("123456") + .with_correspondence_address("correAddress") + .with_active(True) + .with_dob("08/08/1999") + .with_locale("en_IN") + .with_type("CITIZEN") + .with_signature("") + .with_account_locked(False) + .with_father_or_husband_name("Palash") + .with_blood_group("O_POSITIVE") + .with_identification_mark("Wears spects") + .with_photo("a8a6cf1e-c84d-4a0c-b2d5-57ec8711ba25") + .with_otp_reference("14856") + .with_tenant_id("POM") + .with_roles([ + Role(code="CITIZEN", name="Citizen", tenant_id="POM"), + Role(code="EMPLOYEE", name="Employee", tenant_id="POM"), + Role(code="ADMIN", name="Administrator", tenant_id="POM") + ]) + .build()) + + # Act + response = self.user_service.create_user_no_validate(test_user) + + # Assert + self.assertIsNotNone(response) + self.assertIn('ResponseInfo', response) + + # Check if there are any errors before proceeding + if 'Errors' in response: + self.fail(f"User creation failed: {response['Errors']}") + + self.assertIn('User', response) + + # Check response info + self.assertEqual(response['ResponseInfo']['status'], '200') + + # Check user data + user = response['User'][0] + self.assertIsNotNone(user['id']) + self.assertEqual(user['userName'], test_user.user_name) + self.assertEqual(user['name'], test_user.name) + self.assertEqual(user['gender'], test_user.gender) + self.assertEqual(user['mobileNumber'], test_user.mobile_number) + self.assertEqual(user['emailId'], test_user.email) + self.assertEqual(user['tenantId'], test_user.tenant_id) + + # Check roles + roles = user['roles'] + self.assertEqual(len(roles), 3) + role_codes = {role['code'] for role in roles} + self.assertSetEqual(role_codes, {'ADMIN', 'CITIZEN', 'EMPLOYEE'}) + + # Check other important fields + self.assertTrue(user['active']) + self.assertEqual(user['bloodGroup'], "O+") + self.assertEqual(user['dob'], "08/08/1999") + self.assertIsNotNone(user['uuid']) + self.assertIsNotNone(user['createdDate']) + self.assertIsNotNone(user['lastModifiedDate']) + + def test_create_user_invalid_data(self): + """Test that creating a user with missing required fields raises the correct error""" + # Test with invalid data (missing required fields) + invalid_user = CitizenUserBuilder().build() + + with self.assertRaisesRegex(ValueError, "Missing required fields:"): + self.user_service.create_user_no_validate(invalid_user) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/accelerators/digit_client/use/authenticatetest.py b/accelerators/digit_client/use/authenticatetest.py new file mode 100644 index 00000000000..157029df5e5 --- /dev/null +++ b/accelerators/digit_client/use/authenticatetest.py @@ -0,0 +1,19 @@ +from digit_client import AuthenticationService, AuthenticationRequestBuilder + +# Initialize the service +auth_service = AuthenticationService() + +# Create authentication request using builder +auth_request = (AuthenticationRequestBuilder() + .with_username("superuser") + .with_password("Scaler@123") + .with_tenant_id("pg") + # Optional: override defaults if needed + # .with_grant_type("password") + # .with_scope("read") + # .with_user_type("EMPLOYEE") + .build()) + +# Get authentication token +response = auth_service.get_auth_token(auth_request) +print(response) \ No newline at end of file diff --git a/accelerators/digit_client/use/authorize.py b/accelerators/digit_client/use/authorize.py new file mode 100644 index 00000000000..daf5ff33f00 --- /dev/null +++ b/accelerators/digit_client/use/authorize.py @@ -0,0 +1,49 @@ +from digit_client.services import AuthorizeService +from digit_client.models import AuthorizationRequest, Role, RoleBuilder, AuthorizationRequestBuilder, ActionRequest, ActionRequestBuilder, Action, ActionBuilder +from digit_client.request_config import RequestConfig, RequestInfo + + +authorize_service = AuthorizeService() +RequestConfig.initialize( + api_id="asset-services", + version="1.0.0", + auth_token="0e9b955f-5e25-4809-b680-97ef37ccf53f", + msg_id="authorize_action", + user_info={ + "id": "1", + "userName": None, + "name": None, + "type": None, + "mobileNumber": None, + "emailId": None, + "roles": None, + "uuid": "7025d1b3-9e39-46ee-abc4-05e6562e3cba" + } +) + +# Create a role first +role = RoleBuilder() \ + .with_name("Asset Creator") \ + .with_code("ASSET_CREATOR") \ + .with_tenant_id("LMN") \ + .build() + +# Create the authorization request +authorization_request = AuthorizationRequestBuilder() \ + .with_uri("string") \ + .add_role(role) \ + .add_tenant_id("LMN") \ + .build() + +response = authorize_service.authorize_action(authorization_request) +print(response) + +print(authorize_service.get_mdms_action(ActionRequestBuilder().with_tenant_id("LMN").add_role_code("CITIZEN").with_actions([ActionBuilder().with_parent_module("TradeLicense").with_service_code("ASSET_SERVICE").build()]).build())) + + + + + + + + diff --git a/accelerators/digit_client/use/mdms_v1.py b/accelerators/digit_client/use/mdms_v1.py new file mode 100644 index 00000000000..8c7684103a6 --- /dev/null +++ b/accelerators/digit_client/use/mdms_v1.py @@ -0,0 +1,124 @@ +from digit_client.services.master_data_v1 import MDMSService +from digit_client.models.mdms import ( + MasterDetailBuilder, + ModuleDetailBuilder, + MdmsCriteriaReqBuilder, + MdmsCriteriaBuilder +) +from digit_client.request_config import RequestConfig, RequestInfo +from digit_client.models import Role, UserBuilder + +def example_mdms_search(): + # Initialize the MDMS service + mdms_service = MDMSService() + + roles = [ + Role( + name="Employee", + code="EMPLOYEE", + tenant_id="LMN" + ), + Role( + name="System user", + code="SYSTEM", + tenant_id="LMN" + ), + Role( + name="Super User", + code="SUPERUSER", + tenant_id="LMN" + ) + ] + auth_token="0e9b955f-5e25-4809-b680-97ef37ccf53f" + user_info = UserBuilder()\ + .with_id(181)\ + .with_user_name("TestEggMUSTAKIMNK")\ + .with_uuid("4f6cf5fa-bcb2-4a3a-9dff-9740c04e3a92")\ + .with_type("EMPLOYEE")\ + .with_name("mustak")\ + .with_mobile_number("1234567890")\ + .with_email("xyz@egovernments.org")\ + .with_roles(roles)\ + .with_tenant_id("LMN")\ + .build() + # Initialize RequestConfig with user info + RequestConfig.initialize( + api_id="DIGIT-CLIENT", + version="1.0.0", + user_info=user_info.to_dict(), + auth_token=auth_token + ) + + # Create master detail + master_detail = MasterDetailBuilder()\ + .with_name("CancerCess")\ + .build() + + # Create module detail + module_detail = ModuleDetailBuilder()\ + .with_module_name("PropertyTax")\ + .add_master_detail(master_detail)\ + .build() + + # Create MDMS criteria request + mdms_criteria = MdmsCriteriaBuilder()\ + .with_tenant_id("LMN")\ + .with_module_details([module_detail])\ + .build() + + # Get request info with specific message ID + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="5bfa85e7-dfa1-47c8-98b2-747bf552be86" + ) + + # Make the search request + result = mdms_service.search( + mdms_criteria=mdms_criteria, + request_info=request_info + ) + + print("Search Result with custom request info:", result) + result1=mdms_service.search(mdms_criteria) + print("Search Result with default request info:", result1) + return result + +def example_mdms_get(): + # Initialize the MDMS service + mdms_service = MDMSService() + + master_detail = MasterDetailBuilder()\ + .with_name("ConstructionSubType")\ + .build() + + # Create module detail + module_detail = ModuleDetailBuilder()\ + .with_module_name("PropertyTax")\ + .add_master_detail(master_detail)\ + .build() + + # Create MDMS criteria request + mdms_criteria = MdmsCriteriaBuilder()\ + .with_tenant_id("LMN")\ + .with_module_details([module_detail])\ + .build() + + # Get MDMS data with default parameters + result = mdms_service.get( + module_name="PropertyTax", + master_name="ConstructionSubType", + tenant_id="POR", + mdms_criteria=mdms_criteria + ) + + print("Get Result:", result) + return result + +if __name__ == "__main__": + # Example 1: Search MDMS data + print("\n=== Example 1: Search MDMS Data ===") + search_result = example_mdms_search() + + # Example 2: Get specific MDMS data + print("\n=== Example 2: Get MDMS Data ===") + get_result = example_mdms_get() diff --git a/accelerators/digit_client/use/mdms_v2/mdms_create.py b/accelerators/digit_client/use/mdms_v2/mdms_create.py new file mode 100644 index 00000000000..5c4ff440cb5 --- /dev/null +++ b/accelerators/digit_client/use/mdms_v2/mdms_create.py @@ -0,0 +1,57 @@ +from digit_client.models.mdms_v2 import MdmsBuilder +from digit_client.services.mdms_v2 import MDMSV2Service +from digit_client.request_config import RequestConfig + +# Initialize the service +schema_service = MDMSV2Service() + +# Create schema definition using builder pattern +schema_definition = MdmsBuilder() \ + .with_tenant_id("LMN") \ + .with_schema_code("Owner.cardetials") \ + .with_data({ + "ownerName": "mustakim N kouji", + "contectNumber": [123,9090], + "address": "", + "state": "", + "RTO": 105, + "car": { + "moduleName": "122", + "color": "blue", + "carNumber": 123, + "engine": { + "Ename": "", + "capacity": "", + "power": "", + "average": "123" + } + } + }) \ + .with_is_active(True) \ + .build() + +# Create request info +RequestConfig.initialize( + api_id="asset-services", + version="1.0.0", + auth_token="a0cf23e2-6027-4eed-9d19-121fd2adddec", + user_info={ + "id": "1", + "userName": None, + "name": None, + "type": None, + "mobileNumber": None, + "emailId": None, + "roles": None, + "uuid": "7025d1b3-9e39-46ee-abc4-05e6562e3cba" + }, + msg_id="search with from and to values" +) + +# Create MDMS data +response = schema_service.mdms_create( + schema_code="Owner.cardetials", # This should match the schema_code used in the MdmsBuilder + mdms_data=schema_definition +) + +print(response) diff --git a/accelerators/digit_client/use/mdms_v2/mdms_search.py b/accelerators/digit_client/use/mdms_v2/mdms_search.py new file mode 100644 index 00000000000..4600702d1dd --- /dev/null +++ b/accelerators/digit_client/use/mdms_v2/mdms_search.py @@ -0,0 +1,34 @@ +from digit_client.models.mdms_v2 import MdmsCriteriaV2Builder +from digit_client.services.mdms_v2 import MDMSV2Service +from digit_client.request_config import RequestConfig + +# Initialize the service +schema_service = MDMSV2Service() + +# Create search criteria using builder pattern +criteria = MdmsCriteriaV2Builder() \ + .with_tenant_id("LMN") \ + .with_schema_code("PropertyTax.CancerCess1") \ + .build() + +# Create request info +RequestConfig.initialize( + api_id="asset-services", + version="1.0.0", + auth_token="a0cf23e2-6027-4eed-9d19-121fd2adddec", + user_info={ + "id": "1", + "userName": None, + "name": None, + "type": None, + "mobileNumber": None, + "emailId": None, + "roles": None, + "uuid": "7025d1b3-9e39-46ee-abc4-05e6562e3cba" + }, + msg_id="search with from and to values" +) + +# Call the search method +response = schema_service.mdms_search(criteria) +print(response) diff --git a/accelerators/digit_client/use/mdms_v2/schema_create.py b/accelerators/digit_client/use/mdms_v2/schema_create.py new file mode 100644 index 00000000000..d2debe064aa --- /dev/null +++ b/accelerators/digit_client/use/mdms_v2/schema_create.py @@ -0,0 +1,151 @@ +from digit_client.models.mdms_v2 import SchemaDefinitionBuilder +from digit_client.services.mdms_v2 import MDMSV2Service +from digit_client.request_config import RequestConfig + +# Initialize the service +schema_service = MDMSV2Service() + +# Create schema definition using builder pattern +schema_definition = SchemaDefinitionBuilder() \ + .with_tenant_id("pb") \ + .with_code("Owner.cardetials") \ + .with_description("ChargeSlabs") \ + .with_definition({ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "Root Schema", + "required": [ + "ownerName", + "RTO", + "car" + ], + "x-unique": [ + "ownerName" + ], + "properties": { + "ownerName": { + "type": "string", + "title": "The ownerName Schema" + }, + "contectNumber": { + "type": "array", + "title": "The contectNumber Schema", + "items": { + "type": "number" + } + }, + "address": { + "type": "string", + "title": "The address Schema" + }, + "state": { + "type": "string" + }, + "RTO": { + "type": "integer", + "default": 0 + }, + "car": { + "type": "object", + "default": {}, + "title": "The car Schema", + "required": [ + "moduleName", + "carNumber" + ], + "properties": { + "moduleName": { + "type": "string", + "default": "", + "title": "The moduleName Schema" + }, + "color": { + "type": "string", + "default": "", + "title": "The color Schema" + }, + "carNumber": { + "type": "integer", + "default": 0, + "title": "The carNumber Schema" + }, + "engine": { + "type": "object", + "default": {}, + "title": "The engine Schema", + "properties": { + "Ename": { + "type": "string", + "default": "", + "title": "The Ename Schema" + }, + "capacity": { + "type": "string", + "default": "", + "title": "The capacity Schema", + "examples": [ + "" + ] + }, + "power": { + "type": "string", + "default": "", + "title": "The power Schema" + }, + "average": { + "type": "string", + "default": "", + "title": "The average Schema" + } + } + } + } + } + } + }) \ + .build() + +# Create request info +RequestConfig.initialize( + api_id="DIGIT-CLIENT", + version="1.0.0", + auth_token="130d2471-998a-48b7-8189-1d3ac43b6be4", + user_info={ + "id": 595, + "uuid": "1fda5623-448a-4a59-ad17-657986742d67", + "userName": "UNIFIED_DEV_USERR", + "name": "Unified dev user", + "mobileNumber": "8788788851", + "emailId": "", + "type": "EMPLOYEE", + "roles": [ + { + "name": "Localisation admin", + "code": "LOC_ADMIN", + "tenantId": "LMN" + }, + { + "name": "Employee", + "code": "EMPLOYEE", + "tenantId": "LMN" + }, + { + "name": "MDMS Admin", + "code": "MDMS_ADMIN", + "tenantId": "LMN" + }, + { + "name": "SUPER USER", + "code": "SUPERUSER", + "tenantId": "LMN" + } + ], + "active": True, + "tenantId": "LMN" + }, + msg_id="1695889012604|en_IN" +) + +# Call the create method +response = schema_service.schema_create(schema_definition) +print(response) diff --git a/accelerators/digit_client/use/mdms_v2/schema_search.py b/accelerators/digit_client/use/mdms_v2/schema_search.py new file mode 100644 index 00000000000..784ded42c49 --- /dev/null +++ b/accelerators/digit_client/use/mdms_v2/schema_search.py @@ -0,0 +1,27 @@ +from digit_client.models.mdms_v2 import SchemaDefCriteriaBuilder +from digit_client.services.mdms_v2 import MDMSV2Service +from digit_client.request_config import RequestConfig + +# Initialize the service +schema_service = MDMSV2Service() + +# Create search criteria using builder pattern +criteria = SchemaDefCriteriaBuilder() \ + .with_tenant_id("LMN") \ + .with_codes(["Owner.Cardetials"]) \ + .build() + +# Create request info +RequestConfig.initialize( + api_id="asset-services", + version="", + msg_id="search with from and to values", + auth_token="130d2471-998a-48b7-8189-1d3ac43b6be4", + user_info={ + "id": "1" + } +) + +# Call the search method +response = schema_service.schema_search(criteria) +print(response) diff --git a/accelerators/digit_client/use/usertest.py b/accelerators/digit_client/use/usertest.py new file mode 100644 index 00000000000..271e08d1291 --- /dev/null +++ b/accelerators/digit_client/use/usertest.py @@ -0,0 +1,136 @@ +from digit_client import RequestConfig, UserService, CitizenUserBuilder, Role, UserProfileUpdate, UserProfileUpdateBuilder, RequestInfoBuilder +from pprint import pprint + +def main(): + try: + # Initialize with default auth token + auth_token = "ba5e5f01-dbb5-4c94-95cf-5fa52ffae078" + userinfo = { + "username": "priyanhugupta753@gmail.com", + "password": "Scaler@123", + "tenantId": "pg" + } + + # First initialize with basic config + RequestConfig.initialize( + api_id="DIGIT-CLIENT", + version="1.0.0" + ) + + # Get initial request info + request_info = RequestConfig.get_request_info(action="GET") + print("Initial request info:") + print(request_info.to_dict()) + + # Update with auth token + RequestConfig.update_auth_token(auth_token) + request_info = RequestConfig.get_request_info(action="GET") + print("\nRequest info with auth token:") + print(request_info.to_dict()) + + # Update with user info + RequestConfig.update_user_info(userinfo) + request_info = RequestConfig.get_request_info(action="GET") + print("\nRequest info with user info:") + print(request_info.to_dict()) + + # Create user service + user_service = UserService() + + # Example 1: Create a basic citizen user with minimum required fields + basic_citizen = (CitizenUserBuilder() + .with_user_name("9353822214") # Using mobile number as username for OTP-based login + .with_password("Must@123NK") + .with_name("mustak") + .with_gender("MALE") + .with_mobile_number("9353822214") + .with_tenant_id("POM") + .build()) # Will automatically add CITIZEN role + + print("\nBasic Citizen User:") + pprint(basic_citizen.to_dict()) + + # Example 2: Create a detailed citizen user with all fields + detailed_citizen = (CitizenUserBuilder() + .with_user_name("93538222114") + .with_password("Must@123NK") + .with_salutation("Ms") + .with_name("mustak") + .with_gender("MALE") + .with_mobile_number("9353822214") + .with_email("xyz@egovernments.org") + .with_alt_contact_number("") + .with_pan("VFGGC5624P") + .with_aadhaar("96a70") + .with_permanent_address("Dawakhana") + .with_permanent_city("Kaikoo") + .with_permanent_pincode("111111") + .with_correspondence_city("banglore") + .with_correspondence_pincode("123456") + .with_correspondence_address("correAddress") + .with_active(True) + .with_dob("08/08/1999") + .with_locale("en_IN") + .with_type("CITIZEN") + .with_signature("") + .with_account_locked(False) + .with_father_or_husband_name("Palash") + .with_blood_group("O_POSITIVE") + .with_identification_mark("Wears spects") + .with_photo("a8a6cf1e-c84d-4a0c-b2d5-57ec8711ba25") + .with_otp_reference("14856") + .with_tenant_id("POM") + .with_roles([ + Role(code="CITIZEN", name="Citizen", tenant_id="POM"), + Role(code="EMPLOYEE", name="Employee", tenant_id="POM"), + Role(code="ADMIN", name="Administrator", tenant_id="POM") + ]) + .build()) + + print("\nDetailed Citizen User:") + pprint(detailed_citizen.to_dict()) + + # Create citizen user + response = user_service.create_user_no_validate(detailed_citizen) + print("\nCreate Response:", response) + + # Example 3: Update existing user without validation + update_user = (UserProfileUpdateBuilder() + .with_id(338) + .with_uuid("afc7eaf1-a25f-46c9-b16f-3f7de29009ff") + .with_user_name("EGOvM134NmNmd") + .with_name("gudduPoilce") + .with_mobile_number("9353822214") + .with_email("xyz123@egovernments.org") + .with_locale("string") + .with_type("EMPLOYEE") + .with_roles([ + Role(code="EMPLOYEE", name="Employee", tenant_id="pg"), + Role(code="GRO", name="Grievance Routing Officer", tenant_id="pg"), + Role(code="SYSTEM", name="System user", tenant_id="pg"), + Role(code="SUPERUSER", name="Super User", tenant_id="pg"), + Role(code="HRMS_ADMIN", name="HRMS ADMIN", tenant_id="pg"), + Role(code="DGRO", name="Department Grievance Routing Officer", tenant_id="pg") + ]) + .with_active(True) + .with_tenant_id("pg") + .with_permanent_city("Kaikoo") + # .with_gender("MALE") + # .with_photo(None) + .build()) + + print("\nUpdating User:") + pprint(update_user.to_dict()) + + # Update user + update_response = user_service.update_user_no_validate(update_user) + print("\nUpdate Response:") + pprint(update_response) + + except ValueError as ve: + print(f"Validation error: {str(ve)}") + except Exception as e: + print(f"Error occurred: {str(e)}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/accelerators/digit_client/use/workflow_v2.py b/accelerators/digit_client/use/workflow_v2.py new file mode 100644 index 00000000000..b649ebf2376 --- /dev/null +++ b/accelerators/digit_client/use/workflow_v2.py @@ -0,0 +1,229 @@ +from digit_client.services.workflow import WorkflowV2Service +from digit_client.models.workflow import ( + ProcessInstanceBuilder, + ProcessInstanceSearchCriteriaBuilder, + StateBuilder, + WorkflowActionBuilder +) +from digit_client.request_config import RequestConfig, RequestInfo +from digit_client.models import Role, UserBuilder + +def example_workflow_transition(): + # Initialize the Workflow service + workflow_service = WorkflowV2Service() + + # Create user info + roles = [ + Role( + name="Employee", + code="EMPLOYEE", + tenant_id="LMN" + ), + Role( + name="System user", + code="SYSTEM", + tenant_id="LMN" + ) + ] + + auth_token = "0e9b955f-5e25-4809-b680-97ef37ccf53f" + user_info = UserBuilder()\ + .with_id(181)\ + .with_user_name("TestEggMUSTAKIMNK")\ + .with_uuid("4f6cf5fa-bcb2-4a3a-9dff-9740c04e3a92")\ + .with_type("EMPLOYEE")\ + .with_name("mustak")\ + .with_mobile_number("1234567890")\ + .with_email("xyz@egovernments.org")\ + .with_roles(roles)\ + .with_tenant_id("LMN")\ + .build() + + # Initialize RequestConfig with user info + RequestConfig.initialize( + api_id="DIGIT-CLIENT", + version="1.0.0", + user_info=user_info.to_dict(), + auth_token=auth_token + ) + + # Create state for transition + state = StateBuilder()\ + .with_uuid("state-uuid")\ + .with_state("APPROVED")\ + .build() + + # Create workflow action + action = WorkflowActionBuilder()\ + .with_action("APPROVE")\ + .with_uuid("action-uuid")\ + .with_next_state("APPROVED")\ + .with_roles(["EMPLOYEE"])\ + .build() + + # Create process instance + process_instance = ProcessInstanceBuilder()\ + .with_id("process-id")\ + .with_tenant_id("LMN")\ + .with_business_service("PT")\ + .with_business_id("business-id")\ + .with_action("APPROVE")\ + .with_state(state)\ + .with_module_name("PT")\ + .build() + + # Get request info + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="5bfa85e7-dfa1-47c8-98b2-747bf552be86" + ) + + # Make the transition request + result = workflow_service.transition_process( + process_instances=[process_instance], + request_info=request_info + ) + + print("Transition Result:", result) + return result + +def example_workflow_search(): + # Initialize the Workflow service + workflow_service = WorkflowV2Service() + + # Create search criteria + search_criteria = ProcessInstanceSearchCriteriaBuilder()\ + .with_tenant_id("LMN")\ + .with_business_service("PT")\ + .with_business_ids(["business-id"])\ + .with_status("APPROVED")\ + .build() + + # Get request info + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="5bfa85e7-dfa1-47c8-98b2-747bf552be86" + ) + + # Make the search request + result = workflow_service.search_processes( + search_criteria=search_criteria, + request_info=request_info + ) + + print("Search Result:", result) + return result + +def example_workflow_count(): + # Initialize the Workflow service + workflow_service = WorkflowV2Service() + + # Create search criteria for counting + search_criteria = ProcessInstanceSearchCriteriaBuilder()\ + .with_tenant_id("LMN")\ + .with_business_service("PT")\ + .build() + + # Get count of processes + result = workflow_service.count_processes( + search_criteria=search_criteria + ) + + print("Count Result:", result) + return result + +def example_workflow_status_count(): + # Initialize the Workflow service + workflow_service = WorkflowV2Service() + + # Create search criteria for status count + search_criteria = ProcessInstanceSearchCriteriaBuilder()\ + .with_tenant_id("LMN")\ + .with_business_service("PT")\ + .build() + + # Get status count + result = workflow_service.get_status_count( + search_criteria=search_criteria + ) + + print("Status Count Result:", result) + return result + +def example_workflow_sla_count(): + # Initialize the Workflow service + workflow_service = WorkflowV2Service() + + # Create search criteria for SLA count + search_criteria = ProcessInstanceSearchCriteriaBuilder()\ + .with_tenant_id("LMN")\ + .with_business_service("PT")\ + .build() + + # Get nearing SLA count + result = workflow_service.get_nearing_sla_count( + search_criteria=search_criteria + ) + + print("SLA Count Result:", result) + return result + +def example_search_escalations(): + # Initialize the Workflow service + workflow_service = WorkflowV2Service() + + # Create search criteria for SLA count + search_criteria = ProcessInstanceSearchCriteriaBuilder()\ + .with_tenant_id("LMN")\ + .with_business_service("PT")\ + .build() + + # Get nearing SLA count + result = workflow_service.search_escalations( + search_criteria=search_criteria + ) + + print("Escalations Result:", result) + return result + +def example_auto_escalate(): + # Initialize the Workflow service + workflow_service = WorkflowV2Service() + + result = workflow_service.auto_escalate( + business_service="PT" + ) + + print("Auto Escalate Result:", result) + return result + + + +if __name__ == "__main__": + # Example 1: Transition workflow state + print("\n=== Example 1: Transition Workflow State ===") + transition_result = example_workflow_transition() + + # Example 2: Search workflow processes + print("\n=== Example 2: Search Workflow Processes ===") + search_result = example_workflow_search() + + # Example 3: Count workflow processes + print("\n=== Example 3: Count Workflow Processes ===") + count_result = example_workflow_count() + + # Example 4: Get status count + print("\n=== Example 4: Get Status Count ===") + status_count_result = example_workflow_status_count() + + # Example 5: Get nearing SLA count + print("\n=== Example 5: Get Nearing SLA Count ===") + sla_count_result = example_workflow_sla_count() + + # Example 6: Search escalations + print("\n=== Example 6: Search Escalations ===") + escalations_result = example_search_escalations() + + # Example 7: Auto escalate + print("\n=== Example 7: Auto Escalate ===") + auto_escalate_result = example_auto_escalate()