From 980bed4614c915e88c5143c95fae3203d6a5d204 Mon Sep 17 00:00:00 2001 From: Priyanshu Vaish Date: Mon, 24 Mar 2025 10:36:04 +0530 Subject: [PATCH 1/7] implemented the create ,update ,search and user details --- .gitignore | 16 + .../lib/digit_client => authenticate}/auth.py | 0 .../pre_install.py | 2 +- .../build/lib/digit_client/__init__.py | 75 ++--- .../build/lib/digit_client/api_client.py | 4 +- .../build/lib/digit_client/config.py | 14 +- .../lib/digit_client/models/citizen_user.py | 305 ++++++++++++++++++ .../lib/digit_client/models/search_models.py | 31 ++ .../lib/digit_client/models/user_profile.py | 151 +++++++++ .../build/lib/digit_client/request_config.py | 132 ++++++++ .../lib/digit_client/services/__init__.py | 6 +- .../digit_client/services/tenant_service.py | 44 +++ .../lib/digit_client/services/user_service.py | 192 ++++++++++- .../digit_client.egg-info/SOURCES.txt | 7 +- .../digit_client/digit_client/__init__.py | 75 ++--- .../__pycache__/__init__.cpython-313.pyc | Bin 2014 -> 886 bytes .../digit_client/digit_client/api_client.py | 4 +- .../digit_client/digit_client/auth.py | 73 ----- .../digit_client/digit_client/config.py | 14 +- .../digit_client/models/citizen_user.py | 305 ++++++++++++++++++ .../digit_client/models/search_models.py | 31 ++ .../digit_client/models/user_profile.py | 151 +++++++++ .../digit_client/digit_client/pre_install.py | 119 ------- .../digit_client/request_config.py | 132 ++++++++ .../digit_client/services/__init__.py | 6 +- .../digit_client/services/tenant_service.py | 44 +++ .../digit_client/services/user_service.py | 192 ++++++++++- .../dist/digit_client-0.1-py3-none-any.whl | Bin 7614 -> 12753 bytes accelerators/digit_client/setup.py | 50 +-- accelerators/digit_client/test.py | 77 ++++- 30 files changed, 1910 insertions(+), 342 deletions(-) create mode 100644 .gitignore rename accelerators/{digit_client/build/lib/digit_client => authenticate}/auth.py (100%) rename accelerators/{digit_client/build/lib/digit_client => authenticate}/pre_install.py (99%) create mode 100644 accelerators/digit_client/build/lib/digit_client/models/citizen_user.py create mode 100644 accelerators/digit_client/build/lib/digit_client/models/search_models.py create mode 100644 accelerators/digit_client/build/lib/digit_client/models/user_profile.py create mode 100644 accelerators/digit_client/build/lib/digit_client/request_config.py create mode 100644 accelerators/digit_client/build/lib/digit_client/services/tenant_service.py delete mode 100644 accelerators/digit_client/digit_client/auth.py create mode 100644 accelerators/digit_client/digit_client/models/citizen_user.py create mode 100644 accelerators/digit_client/digit_client/models/search_models.py create mode 100644 accelerators/digit_client/digit_client/models/user_profile.py delete mode 100644 accelerators/digit_client/digit_client/pre_install.py create mode 100644 accelerators/digit_client/digit_client/request_config.py create mode 100644 accelerators/digit_client/digit_client/services/tenant_service.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..f344609e9f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +accelerators/digit_client/digit_client/services/__pycache__/user_service.cpython-313.pyc +accelerators/digit_client/digit_client/services/__pycache__/tenant_service.cpython-313.pyc +accelerators/digit_client/digit_client/services/__pycache__/__init__.cpython-313.pyc +accelerators/digit_client/dist/digit_client-0.1-py3-none-any.whl +accelerators/digit_client/digit_client/models/__pycache__/user_search_request.cpython-313.pyc +accelerators/digit_client/digit_client/models/__pycache__/user_profile.cpython-313.pyc +accelerators/digit_client/digit_client/models/__pycache__/search_models.cpython-313.pyc +accelerators/digit_client/digit_client/models/__pycache__/citizen_user.cpython-313.pyc +accelerators/digit_client/digit_client/models/__pycache__/__init__.cpython-313.pyc +accelerators/digit_client/digit_client/__pycache__/request_config.cpython-313.pyc +accelerators/digit_client/digit_client/__pycache__/config.cpython-313.pyc +accelerators/digit_client/digit_client/__pycache__/api_client.cpython-313.pyc +accelerators/digit_client/digit_client/__pycache__/__init__.cpython-313.pyc +accelerators/authenticate/__pycache__/auth.cpython-313.pyc +accelerators/digit_client/dist/digit_client-0.1-py3-none-any.whl +accelerators/digit_client/digit_client/__pycache__/__init__.cpython-313.pyc 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..51012afee9c 100644 --- a/accelerators/digit_client/build/lib/digit_client/__init__.py +++ b/accelerators/digit_client/build/lib/digit_client/__init__.py @@ -1,52 +1,29 @@ -# __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 +from .api_client import APIClient +from .config import Config +from .services import TenantService, UserService +from .request_config import RequestConfig, RequestInfo +from .models.citizen_user import CitizenUser, Role, CitizenUserBuilder +from .models.search_models import UserSearchModel +from .models.user_profile import UserProfileUpdate, UserRole,UserProfileUpdateBuilder -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 +__all__ = [ + 'APIClient', + 'Config', + 'TenantService', + 'UserService', + 'RequestConfig', + 'RequestInfo', + 'CitizenUser', + 'Role', + 'UserSearchModel', + 'UserProfileUpdate', + 'UserRole', + 'UserProfileUpdateBuilder', + 'CitizenUserBuilder' +] \ 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..f23be315a63 100644 --- a/accelerators/digit_client/build/lib/digit_client/api_client.py +++ b/accelerators/digit_client/build/lib/digit_client/api_client.py @@ -11,8 +11,10 @@ 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): + def post(self, endpoint, json_data, additional_headers=None): headers = {'Authorization': f'Bearer {self.auth_token}'} + if additional_headers: + headers.update(additional_headers) response = requests.post(f"{self.base_url}/{endpoint}", headers=headers, json=json_data) return response.json() 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/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..43686e273b9 --- /dev/null +++ b/accelerators/digit_client/build/lib/digit_client/models/search_models.py @@ -0,0 +1,31 @@ +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 \ 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..4687367f8db --- /dev/null +++ b/accelerators/digit_client/build/lib/digit_client/models/user_profile.py @@ -0,0 +1,151 @@ +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 + + 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 + } + +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 + + 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 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 + ) \ 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..bdc934b52f2 --- /dev/null +++ b/accelerators/digit_client/build/lib/digit_client/request_config.py @@ -0,0 +1,132 @@ +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 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) -> 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_request_info = RequestInfo( + api_id=api_id, + ver=version, + ts=int(time.time() * 1000), + action="", + auth_token=auth_token, + user_info=user_info, + msg_id="", + correlation_id="" + ) + + @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_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({ + "ts": int(time.time() * 1000), + "action": action, + "msgId": str(uuid.uuid4()), + "correlationId": str(uuid.uuid4()), + **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..e8a306493e8 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,5 @@ -# __init__.py for services package \ No newline at end of file +# __init__.py for services package +from .tenant_service import TenantService +from .user_service import UserService + +__all__ = ['TenantService', 'UserService'] \ 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..0d505738d91 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,191 @@ +# from typing import Dict, Optional +# from ..api_client import APIClient +# from ..models.citizen_user import CitizenUser +# from ..request_config import RequestConfig + +# class UserService: +# def __init__(self, api_client: Optional[APIClient] = None): +# self.api_client = api_client or APIClient() +# self.base_url = "user/citizen" + +# def create_citizen(self, citizen_user: CitizenUser, temp_auth_token: Optional[str] = None) -> Dict: +# """ +# Create a new citizen user in the DIGIT platform. + +# Args: +# citizen_user (CitizenUser): The citizen user data +# temp_auth_token (str, optional): Temporary auth token to use for this request + +# Returns: +# Dict: Response from the user creation API +# """ +# # Get request info for this specific action +# request_info = RequestConfig.get_request_info( +# action="POST", +# msg_id="create-citizen-user", +# temp_auth_token=temp_auth_token +# ) + +# payload = { +# "RequestInfo": request_info.to_dict(), +# "user": citizen_user.to_dict() +# } + +# endpoint = f"{self.base_url}/_create" +# return self.api_client.post(endpoint, json_data=payload) + +# def get_user(self, user_id: str, temp_auth_token: Optional[str] = None) -> Dict: +# """ +# Get user details by user ID + +# Args: +# user_id (str): The ID of the user to retrieve +# temp_auth_token (str, optional): Temporary auth token to use for this request + +# Returns: +# Dict: User details from the API +# """ +# request_info = RequestConfig.get_request_info( +# action="GET", +# msg_id="get-user-details", +# temp_auth_token=temp_auth_token +# ) + +# params = {"RequestInfo": request_info.to_dict()} +# return self.api_client.get(f"{self.base_url}/{user_id}", params=params) + +# # Add more user related methods as needed + + + +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_profile import UserProfileUpdate +from ..request_config import RequestConfig 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_citizen(self, citizen_user: CitizenUser, temp_auth_token: Optional[str] = None) -> Dict: + """ + Create a new citizen user in the DIGIT platform. + + Args: + citizen_user (CitizenUser): The citizen user data + temp_auth_token (str, optional): Temporary auth token to use for this request + + Returns: + Dict: Response from the user creation API + """ + # Get request info for this specific action + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="create-citizen-user", + temp_auth_token=temp_auth_token + ) + + payload = { + "RequestInfo": request_info.to_dict(), + "user": citizen_user.to_dict() + } + + endpoint = f"{self.base_url}/citizen/_create" + return self.api_client.post(endpoint, json_data=payload) + + def get_user_details(self, tenant_id: str,temp_auth_token: Optional[str] = None) -> Dict: + """ + Get user details using access token. If temp_auth_token is not provided, uses the auth token from RequestInfo. + + Args: + tenant_id (str): Tenant ID + temp_auth_token (str, optional): Access token of the user. If not provided, uses auth token from RequestInfo + + Returns: + Dict: User details from the API + """ + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="get-user-details", + temp_auth_token=temp_auth_token + ) + + 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: UserProfileUpdate, temp_auth_token: Optional[str] = None) -> Dict: + """ + Update user profile. + + Args: + user_profile (UserProfileUpdate): The updated user profile data + temp_auth_token (str, optional): Temporary auth token to use for this request + + Returns: + Dict: Response from the user profile update API + """ + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="update-user-profile", + temp_auth_token=temp_auth_token + ) + + 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 create_user(self, user_data): - return self.api_client.post("user/create", user_data) - def get_user(self, user_id): - return self.api_client.get(f"user/{user_id}") + def search_users(self, search_criteria: UserSearchModel, temp_auth_token: Optional[str] = None) -> Dict: + """ + Search for users based on given criteria + + Args: + search_criteria (UserSearchModel): The search criteria + temp_auth_token (str, optional): Temporary auth token to use for this request + + Returns: + Dict: Search results from the API + """ + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="search-users", + temp_auth_token=temp_auth_token + ) + + # 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) - # Add more user related methods as needed \ No newline at end of file + \ No newline at end of file diff --git a/accelerators/digit_client/digit_client.egg-info/SOURCES.txt b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt index bb7f70ac1e9..637112be276 100644 --- a/accelerators/digit_client/digit_client.egg-info/SOURCES.txt +++ b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt @@ -2,10 +2,9 @@ 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 @@ -14,8 +13,12 @@ digit_client.egg-info/requires.txt digit_client.egg-info/top_level.txt digit_client/models/__init__.py digit_client/models/boundary.py +digit_client/models/citizen_user.py +digit_client/models/search_models.py digit_client/models/service.py digit_client/models/service_definition.py digit_client/models/user.py +digit_client/models/user_profile.py digit_client/services/__init__.py +digit_client/services/tenant_service.py digit_client/services/user_service.py \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/__init__.py b/accelerators/digit_client/digit_client/__init__.py index 2345eeff1d3..51012afee9c 100644 --- a/accelerators/digit_client/digit_client/__init__.py +++ b/accelerators/digit_client/digit_client/__init__.py @@ -1,52 +1,29 @@ -# __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 +from .api_client import APIClient +from .config import Config +from .services import TenantService, UserService +from .request_config import RequestConfig, RequestInfo +from .models.citizen_user import CitizenUser, Role, CitizenUserBuilder +from .models.search_models import UserSearchModel +from .models.user_profile import UserProfileUpdate, UserRole,UserProfileUpdateBuilder -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 +__all__ = [ + 'APIClient', + 'Config', + 'TenantService', + 'UserService', + 'RequestConfig', + 'RequestInfo', + 'CitizenUser', + 'Role', + 'UserSearchModel', + 'UserProfileUpdate', + 'UserRole', + 'UserProfileUpdateBuilder', + 'CitizenUserBuilder' +] \ 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 4f0b89956a22c0c9384ac72f9da31fb914e9afba..e3d43bacf87ef5eef1f003d97e37bf0d476de110 100644 GIT binary patch literal 886 zcmZWn%Wl&^6dgM+$F397qy>SH(q$HeR7flkLV`ps8S^Mjv$HgEok=>9YlmwmQnD%^ z!AI~b?7AJP$^x-qSLhEg*G@};SsdSU=RS|;%C=3SPIvEO@>M6~8wlT_xnf9ZXkG=G~bA0@L>xcB61CCM{jsM?CbxWWZrAd@ikF%wbLN z_K<&^@i@uCrM0I0a2&OA(&~ytEP04IGJhBaTyEZSy_$(h*IW>r1#Pu@l&9;nGy`HV@6u{noWM4=+4Hxl3t6!(nV zex?wcsq9l<&7j=u>DTsyV zz7Kg1#GJdm$e*36M-sdHB6&SKc6(wXl1JSLxa;{o4>)*9r1D1SUn}r8VN8VTinXT; zX*^ex&I0}d9;&wZy5c7aE_0fu{nT|We`%2F_L=pa>|BuhE7Dn!rz^5|PWCRy?urc0 Q$?#XcV(Fhs@IdAL23-pNqW}N^ literal 2014 zcma)7&2JM&6o2bod)Hpu`5+DvO<+NR#x1y%mXD@wkd=@S645kEPO-GwtjErRvukG8 zd{~GAm*T3Dd!Yge71XLyEA>FHy;S)RTqTEOn^r}fxTS`VlpE43G-HkX%{3z)MZat`Rw}*1dPiNadCo{Ge>6}(p%GQs1yojpI7c(FeV~RK zZ({87J~+B=+PA)Dj{*348!@0>^k4LLt?Nlm?{Sme&5_h)^cil_VKGED_i9*`$6C&` z(pCl3C~Z??Z*jCe&n4qenfciy=Lr+KJ6mo~WzD?lYDU2%maFL{ch+cjCg6t0-`aVc z?!a!C-ngAy&&I|93X=_tn+~Uu2ldg2uNU^h^aj2n=;%R#J&@;e@FBkka$GV{-aj}? zj0=jHQ&>aSW4Em$Z#^~1#HI?iug5Nh+Hup*YU#XsdJ~b;nDF^ zUij1%gA|$J_prvo@N$FwnJI@*CsU;6RXy{OMTV$(iDZUsqcn$(oXm*no-WN~hFEG2 z4ce4sbi*J8LUq?hyXKT_-DsL=VK%i?@v0Xw*a!G2rHqcOFr0%p-GEhr4<=Sx+Nzf; zm#fz+*YBGPEqyhq@5PGTu?Ac)vE0(J%H#UC6Gekt`}V`Pkt6(%+!1~YIxO2hI!h=p zbW{#&2+nX8=5q=*`Bg%!itSTegO7R`U@EY&!`t1E=kp=Ng(2<_#H9#651}#2~mi0Baf#83w-Gl2W;Ig3z2z=eALSG6pg#&*`RhuG~GWn+1|p zTw5_H(Ou%JD>*#uSI!QXJJVZ`uNdc zt@FfU>w7CZyI-yb@#5uk38D$qL3&BCTI+#_7atn6_#2DTn=36HOowsmF#5gKlNRCY zNLt{3h@a$t5^(o(5O?zekK()-NhkUFws^XipYIK#T<#u4W}AA!ESpwd85$iKJ*N!% zT_crBm6MTe$))6C$)tz|;z(7CK=@aP{03bJJGO^> zC115G_T8BW`Fr^X_C5Q%*@fN57vd*w$V<_9b)+&<9j}bveXVw&e9w2JmW6OFa%@pJzAW#o4pat~kstAaMd{>{Bv+-+q%Yb& cmwlZBi^A(jN3Q#i_zv_Vciuj9Q}pBi2N5pI3jhEB diff --git a/accelerators/digit_client/digit_client/api_client.py b/accelerators/digit_client/digit_client/api_client.py index a28a964c9db..f23be315a63 100644 --- a/accelerators/digit_client/digit_client/api_client.py +++ b/accelerators/digit_client/digit_client/api_client.py @@ -11,8 +11,10 @@ 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): + def post(self, endpoint, json_data, additional_headers=None): headers = {'Authorization': f'Bearer {self.auth_token}'} + if additional_headers: + headers.update(additional_headers) response = requests.post(f"{self.base_url}/{endpoint}", headers=headers, json=json_data) return response.json() 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/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/search_models.py b/accelerators/digit_client/digit_client/models/search_models.py new file mode 100644 index 00000000000..43686e273b9 --- /dev/null +++ b/accelerators/digit_client/digit_client/models/search_models.py @@ -0,0 +1,31 @@ +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 \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/models/user_profile.py b/accelerators/digit_client/digit_client/models/user_profile.py new file mode 100644 index 00000000000..4687367f8db --- /dev/null +++ b/accelerators/digit_client/digit_client/models/user_profile.py @@ -0,0 +1,151 @@ +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 + + 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 + } + +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 + + 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 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 + ) \ No newline at end of file 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..bdc934b52f2 --- /dev/null +++ b/accelerators/digit_client/digit_client/request_config.py @@ -0,0 +1,132 @@ +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 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) -> 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_request_info = RequestInfo( + api_id=api_id, + ver=version, + ts=int(time.time() * 1000), + action="", + auth_token=auth_token, + user_info=user_info, + msg_id="", + correlation_id="" + ) + + @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_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({ + "ts": int(time.time() * 1000), + "action": action, + "msgId": str(uuid.uuid4()), + "correlationId": str(uuid.uuid4()), + **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..e8a306493e8 100644 --- a/accelerators/digit_client/digit_client/services/__init__.py +++ b/accelerators/digit_client/digit_client/services/__init__.py @@ -1 +1,5 @@ -# __init__.py for services package \ No newline at end of file +# __init__.py for services package +from .tenant_service import TenantService +from .user_service import UserService + +__all__ = ['TenantService', 'UserService'] \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/services/tenant_service.py b/accelerators/digit_client/digit_client/services/tenant_service.py new file mode 100644 index 00000000000..4195702a0b7 --- /dev/null +++ b/accelerators/digit_client/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/digit_client/services/user_service.py b/accelerators/digit_client/digit_client/services/user_service.py index 205ed1e55a0..0d505738d91 100644 --- a/accelerators/digit_client/digit_client/services/user_service.py +++ b/accelerators/digit_client/digit_client/services/user_service.py @@ -1,13 +1,191 @@ +# from typing import Dict, Optional +# from ..api_client import APIClient +# from ..models.citizen_user import CitizenUser +# from ..request_config import RequestConfig + +# class UserService: +# def __init__(self, api_client: Optional[APIClient] = None): +# self.api_client = api_client or APIClient() +# self.base_url = "user/citizen" + +# def create_citizen(self, citizen_user: CitizenUser, temp_auth_token: Optional[str] = None) -> Dict: +# """ +# Create a new citizen user in the DIGIT platform. + +# Args: +# citizen_user (CitizenUser): The citizen user data +# temp_auth_token (str, optional): Temporary auth token to use for this request + +# Returns: +# Dict: Response from the user creation API +# """ +# # Get request info for this specific action +# request_info = RequestConfig.get_request_info( +# action="POST", +# msg_id="create-citizen-user", +# temp_auth_token=temp_auth_token +# ) + +# payload = { +# "RequestInfo": request_info.to_dict(), +# "user": citizen_user.to_dict() +# } + +# endpoint = f"{self.base_url}/_create" +# return self.api_client.post(endpoint, json_data=payload) + +# def get_user(self, user_id: str, temp_auth_token: Optional[str] = None) -> Dict: +# """ +# Get user details by user ID + +# Args: +# user_id (str): The ID of the user to retrieve +# temp_auth_token (str, optional): Temporary auth token to use for this request + +# Returns: +# Dict: User details from the API +# """ +# request_info = RequestConfig.get_request_info( +# action="GET", +# msg_id="get-user-details", +# temp_auth_token=temp_auth_token +# ) + +# params = {"RequestInfo": request_info.to_dict()} +# return self.api_client.get(f"{self.base_url}/{user_id}", params=params) + +# # Add more user related methods as needed + + + +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_profile import UserProfileUpdate +from ..request_config import RequestConfig 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_citizen(self, citizen_user: CitizenUser, temp_auth_token: Optional[str] = None) -> Dict: + """ + Create a new citizen user in the DIGIT platform. + + Args: + citizen_user (CitizenUser): The citizen user data + temp_auth_token (str, optional): Temporary auth token to use for this request + + Returns: + Dict: Response from the user creation API + """ + # Get request info for this specific action + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="create-citizen-user", + temp_auth_token=temp_auth_token + ) + + payload = { + "RequestInfo": request_info.to_dict(), + "user": citizen_user.to_dict() + } + + endpoint = f"{self.base_url}/citizen/_create" + return self.api_client.post(endpoint, json_data=payload) + + def get_user_details(self, tenant_id: str,temp_auth_token: Optional[str] = None) -> Dict: + """ + Get user details using access token. If temp_auth_token is not provided, uses the auth token from RequestInfo. + + Args: + tenant_id (str): Tenant ID + temp_auth_token (str, optional): Access token of the user. If not provided, uses auth token from RequestInfo + + Returns: + Dict: User details from the API + """ + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="get-user-details", + temp_auth_token=temp_auth_token + ) + + 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: UserProfileUpdate, temp_auth_token: Optional[str] = None) -> Dict: + """ + Update user profile. + + Args: + user_profile (UserProfileUpdate): The updated user profile data + temp_auth_token (str, optional): Temporary auth token to use for this request + + Returns: + Dict: Response from the user profile update API + """ + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="update-user-profile", + temp_auth_token=temp_auth_token + ) + + 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 create_user(self, user_data): - return self.api_client.post("user/create", user_data) - def get_user(self, user_id): - return self.api_client.get(f"user/{user_id}") + def search_users(self, search_criteria: UserSearchModel, temp_auth_token: Optional[str] = None) -> Dict: + """ + Search for users based on given criteria + + Args: + search_criteria (UserSearchModel): The search criteria + temp_auth_token (str, optional): Temporary auth token to use for this request + + Returns: + Dict: Search results from the API + """ + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="search-users", + temp_auth_token=temp_auth_token + ) + + # 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) - # Add more user related methods as needed \ No newline at end of file + \ 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 8465980f56f4eeb4efcc101fd4424169d4ddffd2..661591c475cfb437018515fa449579c5fe3cc4b7 100644 GIT binary patch delta 9899 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 delta 4864 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 diff --git a/accelerators/digit_client/setup.py b/accelerators/digit_client/setup.py index f61dcd5fb4c..c15bc2c76f2 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', diff --git a/accelerators/digit_client/test.py b/accelerators/digit_client/test.py index 6c41f0c5cf0..0ac957e6e36 100644 --- a/accelerators/digit_client/test.py +++ b/accelerators/digit_client/test.py @@ -1,2 +1,75 @@ -import digit_client -print(digit_client.__version__) \ No newline at end of file +from digit_client import RequestConfig, UserService, CitizenUserBuilder, Role +from pprint import pprint + +def main(): + try: + # Initialize with default auth token + auth_token = "50b80fa6-bec2-438e-a168-ce5ad53770b5" + RequestConfig.initialize( + api_id="DIGIT-CLIENT", + version="1.0.0", + auth_token=auth_token + ) + + # 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("pg") + .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("9353822214") + .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("pg") + .build()) + + print("\nDetailed Citizen User:") + pprint(detailed_citizen.to_dict()) + + # Create citizen user + response = user_service.create_citizen(detailed_citizen) + print("\nCreate Response:", 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 From 40436d772aa7ca90ac57f1ddc2795dc2c1e9a5c8 Mon Sep 17 00:00:00 2001 From: Priyanshu Vaish Date: Mon, 24 Mar 2025 16:32:35 +0530 Subject: [PATCH 2/7] user is implemented --- .gitignore | 2 + .../lib/digit_client/services/user_service.py | 50 +++++++++ .../digit_client.egg-info/SOURCES.txt | 3 +- .../__pycache__/__init__.cpython-313.pyc | Bin 886 -> 886 bytes .../digit_client/models/user_profile.py | 20 +++- .../digit_client/services/user_service.py | 50 +++++++++ .../dist/digit_client-0.1-py3-none-any.whl | Bin 12753 -> 12938 bytes accelerators/digit_client/test.py | 50 ++++++++- .../digit_client/tests/test_user_service.py | 104 ++++++++++++++++++ 9 files changed, 270 insertions(+), 9 deletions(-) create mode 100644 accelerators/digit_client/tests/test_user_service.py diff --git a/.gitignore b/.gitignore index f344609e9f9..f20644bc5db 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ accelerators/digit_client/digit_client/__pycache__/__init__.cpython-313.pyc accelerators/authenticate/__pycache__/auth.cpython-313.pyc accelerators/digit_client/dist/digit_client-0.1-py3-none-any.whl accelerators/digit_client/digit_client/__pycache__/__init__.cpython-313.pyc +accelerators/digit_client/tests/__pycache__/test_user_service.cpython-313.pyc +accelerators/digit_client/digit_client/__pycache__/__init__.cpython-313.pyc 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 0d505738d91..a4a0a0f2fd2 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 @@ -188,4 +188,54 @@ def search_users(self, search_criteria: UserSearchModel, temp_auth_token: Option endpoint = f"{self.base_url}/_search" return self.api_client.post(endpoint, json_data=payload) + def create_user_no_validate(self, citizen_user: CitizenUser, temp_auth_token: Optional[str] = None) -> Dict: + """ + Create a new user without validation in the DIGIT platform. + + Args: + user_data (Dict): The user data containing all required fields + temp_auth_token (str, optional): Temporary auth token to use for this request + + Returns: + Dict: Response from the user creation API + """ + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="create-user-no-validate", + temp_auth_token=temp_auth_token + ) + + 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: UserProfileUpdate, temp_auth_token: Optional[str] = None) -> Dict: + """ + Update a user without validation in the DIGIT platform. + + Args: + user_data (Dict): The user data containing all fields to update including id and uuid + temp_auth_token (str, optional): Temporary auth token to use for this request + + Returns: + Dict: Response from the user update API + """ + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="update-user-no-validate", + temp_auth_token=temp_auth_token + ) + + 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/SOURCES.txt b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt index 637112be276..a33210f0b4f 100644 --- a/accelerators/digit_client/digit_client.egg-info/SOURCES.txt +++ b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt @@ -21,4 +21,5 @@ digit_client/models/user.py digit_client/models/user_profile.py digit_client/services/__init__.py digit_client/services/tenant_service.py -digit_client/services/user_service.py \ No newline at end of file +digit_client/services/user_service.py +tests/test_user_service.py \ 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 e3d43bacf87ef5eef1f003d97e37bf0d476de110..e5de5ddadfc76e8dec9822601f27521f465ab197 100644 GIT binary patch delta 22 ccmeyy_Kl7AGcPX}0}yPJd6@oeBX1@%092y~M*si- delta 22 ccmeyy_Kl7AGcPX}0}wd7-AzBWkvEeW08u~&-T(jq diff --git a/accelerators/digit_client/digit_client/models/user_profile.py b/accelerators/digit_client/digit_client/models/user_profile.py index 4687367f8db..88b2207e952 100644 --- a/accelerators/digit_client/digit_client/models/user_profile.py +++ b/accelerators/digit_client/digit_client/models/user_profile.py @@ -29,6 +29,8 @@ class UserProfileUpdate: active: bool tenant_id: str permanent_city: Optional[str] = None + gender: Optional[str] = None + photo: Optional[str] = None def to_dict(self) -> dict: return { @@ -43,7 +45,9 @@ def to_dict(self) -> dict: "roles": [role.to_dict() for role in self.roles], "active": self.active, "tenantId": self.tenant_id, - "permanentCity": self.permanent_city + "permanentCity": self.permanent_city, + "gender": self.gender, + "photo": self.photo } class UserProfileUpdateBuilder: @@ -62,6 +66,8 @@ def __init__(self): 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 @@ -115,6 +121,14 @@ 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 @@ -147,5 +161,7 @@ def build(self) -> UserProfileUpdate: roles=self._roles, active=self._active, tenant_id=self._tenant_id, - permanent_city=self._permanent_city + permanent_city=self._permanent_city, + gender=self._gender, + photo=self._photo ) \ 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 0d505738d91..a4a0a0f2fd2 100644 --- a/accelerators/digit_client/digit_client/services/user_service.py +++ b/accelerators/digit_client/digit_client/services/user_service.py @@ -188,4 +188,54 @@ def search_users(self, search_criteria: UserSearchModel, temp_auth_token: Option endpoint = f"{self.base_url}/_search" return self.api_client.post(endpoint, json_data=payload) + def create_user_no_validate(self, citizen_user: CitizenUser, temp_auth_token: Optional[str] = None) -> Dict: + """ + Create a new user without validation in the DIGIT platform. + + Args: + user_data (Dict): The user data containing all required fields + temp_auth_token (str, optional): Temporary auth token to use for this request + + Returns: + Dict: Response from the user creation API + """ + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="create-user-no-validate", + temp_auth_token=temp_auth_token + ) + + 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: UserProfileUpdate, temp_auth_token: Optional[str] = None) -> Dict: + """ + Update a user without validation in the DIGIT platform. + + Args: + user_data (Dict): The user data containing all fields to update including id and uuid + temp_auth_token (str, optional): Temporary auth token to use for this request + + Returns: + Dict: Response from the user update API + """ + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="update-user-no-validate", + temp_auth_token=temp_auth_token + ) + + 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/dist/digit_client-0.1-py3-none-any.whl b/accelerators/digit_client/dist/digit_client-0.1-py3-none-any.whl index 661591c475cfb437018515fa449579c5fe3cc4b7..6e35e40217f5fc0e8cad8aff5c379392059a81c0 100644 GIT binary patch delta 4580 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 Date: Wed, 26 Mar 2025 14:04:41 +0530 Subject: [PATCH 3/7] temporary requestinfo is done and authenticate api is done --- .gitignore | 27 +- .../build/lib/digit_client/__init__.py | 12 +- .../build/lib/digit_client/api_client.py | 25 +- .../lib/digit_client/models/user_profile.py | 20 +- .../build/lib/digit_client/request_config.py | 337 +++++++++++++++++- .../lib/digit_client/services/__init__.py | 4 +- .../lib/digit_client/services/user_service.py | 1 - .../digit_client.egg-info/SOURCES.txt | 5 +- .../digit_client/digit_client/__init__.py | 12 +- .../digit_client/digit_client/api_client.py | 25 +- .../digit_client/digit_client/models/auth.py | 84 +++++ .../digit_client/models/service.py | 10 - .../digit_client/digit_client/models/user.py | 12 - .../digit_client/request_config.py | 337 +++++++++++++++++- .../digit_client/services/__init__.py | 4 +- .../digit_client/services/authenticate.py | 91 +++++ .../digit_client/services/tenant_service.py | 44 --- .../digit_client/services/user_service.py | 160 +++------ .../dist/digit_client-0.1-py3-none-any.whl | Bin 12938 -> 16148 bytes .../digit_client/use/authenticatetest.py | 19 + .../digit_client/{test.py => use/usertest.py} | 29 +- 21 files changed, 1039 insertions(+), 219 deletions(-) create mode 100644 accelerators/digit_client/digit_client/models/auth.py delete mode 100644 accelerators/digit_client/digit_client/models/service.py delete mode 100644 accelerators/digit_client/digit_client/models/user.py create mode 100644 accelerators/digit_client/digit_client/services/authenticate.py delete mode 100644 accelerators/digit_client/digit_client/services/tenant_service.py create mode 100644 accelerators/digit_client/use/authenticatetest.py rename accelerators/digit_client/{test.py => use/usertest.py} (81%) diff --git a/.gitignore b/.gitignore index f20644bc5db..d73ca7c1394 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,9 @@ -accelerators/digit_client/digit_client/services/__pycache__/user_service.cpython-313.pyc -accelerators/digit_client/digit_client/services/__pycache__/tenant_service.cpython-313.pyc -accelerators/digit_client/digit_client/services/__pycache__/__init__.cpython-313.pyc -accelerators/digit_client/dist/digit_client-0.1-py3-none-any.whl -accelerators/digit_client/digit_client/models/__pycache__/user_search_request.cpython-313.pyc -accelerators/digit_client/digit_client/models/__pycache__/user_profile.cpython-313.pyc -accelerators/digit_client/digit_client/models/__pycache__/search_models.cpython-313.pyc -accelerators/digit_client/digit_client/models/__pycache__/citizen_user.cpython-313.pyc -accelerators/digit_client/digit_client/models/__pycache__/__init__.cpython-313.pyc -accelerators/digit_client/digit_client/__pycache__/request_config.cpython-313.pyc -accelerators/digit_client/digit_client/__pycache__/config.cpython-313.pyc -accelerators/digit_client/digit_client/__pycache__/api_client.cpython-313.pyc -accelerators/digit_client/digit_client/__pycache__/__init__.cpython-313.pyc -accelerators/authenticate/__pycache__/auth.cpython-313.pyc -accelerators/digit_client/dist/digit_client-0.1-py3-none-any.whl -accelerators/digit_client/digit_client/__pycache__/__init__.cpython-313.pyc -accelerators/digit_client/tests/__pycache__/test_user_service.cpython-313.pyc -accelerators/digit_client/digit_client/__pycache__/__init__.cpython-313.pyc +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/__init__.py b/accelerators/digit_client/build/lib/digit_client/__init__.py index 51012afee9c..9d24096decf 100644 --- a/accelerators/digit_client/build/lib/digit_client/__init__.py +++ b/accelerators/digit_client/build/lib/digit_client/__init__.py @@ -6,24 +6,28 @@ from .api_client import APIClient from .config import Config -from .services import TenantService, UserService -from .request_config import RequestConfig, RequestInfo +from .services import AuthenticationService, UserService +from .request_config import RequestConfig, RequestInfo, RequestInfoBuilder from .models.citizen_user import CitizenUser, Role, CitizenUserBuilder from .models.search_models import UserSearchModel from .models.user_profile import UserProfileUpdate, UserRole,UserProfileUpdateBuilder +from .models.auth import AuthenticationRequest, AuthenticationRequestBuilder __all__ = [ 'APIClient', 'Config', - 'TenantService', + 'AuthenticationService', 'UserService', 'RequestConfig', 'RequestInfo', + 'RequestInfoBuilder', 'CitizenUser', 'Role', 'UserSearchModel', 'UserProfileUpdate', 'UserRole', 'UserProfileUpdateBuilder', - 'CitizenUserBuilder' + 'CitizenUserBuilder', + 'AuthenticationRequest', + 'AuthenticationRequestBuilder' ] \ 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 f23be315a63..15deb280f93 100644 --- a/accelerators/digit_client/build/lib/digit_client/api_client.py +++ b/accelerators/digit_client/build/lib/digit_client/api_client.py @@ -11,11 +11,32 @@ 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, additional_headers=None): + def post(self, endpoint, json_data=None, data=None, additional_headers=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 + + Returns: + dict: Response JSON + """ headers = {'Authorization': f'Bearer {self.auth_token}'} if additional_headers: headers.update(additional_headers) - response = requests.post(f"{self.base_url}/{endpoint}", headers=headers, json=json_data) + print(headers) + print(json_data) + print(data) + + 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 + ) 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/models/user_profile.py b/accelerators/digit_client/build/lib/digit_client/models/user_profile.py index 4687367f8db..88b2207e952 100644 --- a/accelerators/digit_client/build/lib/digit_client/models/user_profile.py +++ b/accelerators/digit_client/build/lib/digit_client/models/user_profile.py @@ -29,6 +29,8 @@ class UserProfileUpdate: active: bool tenant_id: str permanent_city: Optional[str] = None + gender: Optional[str] = None + photo: Optional[str] = None def to_dict(self) -> dict: return { @@ -43,7 +45,9 @@ def to_dict(self) -> dict: "roles": [role.to_dict() for role in self.roles], "active": self.active, "tenantId": self.tenant_id, - "permanentCity": self.permanent_city + "permanentCity": self.permanent_city, + "gender": self.gender, + "photo": self.photo } class UserProfileUpdateBuilder: @@ -62,6 +66,8 @@ def __init__(self): 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 @@ -115,6 +121,14 @@ 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 @@ -147,5 +161,7 @@ def build(self) -> UserProfileUpdate: roles=self._roles, active=self._active, tenant_id=self._tenant_id, - permanent_city=self._permanent_city + 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 index bdc934b52f2..80696d27ba4 100644 --- a/accelerators/digit_client/build/lib/digit_client/request_config.py +++ b/accelerators/digit_client/build/lib/digit_client/request_config.py @@ -52,6 +52,159 @@ def with_auth_token(self, temp_auth_token: str) -> 'RequestInfo': 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 @@ -66,7 +219,13 @@ def initialize(cls, api_id: str = "DIGIT-CLIENT", version: str = "1.0.0", auth_token: Optional[str] = None, - user_info: Optional[dict] = None) -> 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 = "") -> None: """ Initialize the default request configuration. @@ -75,16 +234,186 @@ def initialize(cls, 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=int(time.time() * 1000), - action="", + action=action, + auth_token=auth_token, + user_info=user_info, + did=did, + key=key, + msg_id=msg_id or str(uuid.uuid4()), + requester_id=requester_id, + correlation_id=correlation_id or str(uuid.uuid4()) + ) + + @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, - msg_id="", - correlation_id="" + 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 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 e8a306493e8..1d450100415 100644 --- a/accelerators/digit_client/build/lib/digit_client/services/__init__.py +++ b/accelerators/digit_client/build/lib/digit_client/services/__init__.py @@ -1,5 +1,5 @@ # __init__.py for services package -from .tenant_service import TenantService +from .authenticate import AuthenticationService from .user_service import UserService -__all__ = ['TenantService', 'UserService'] \ No newline at end of file +__all__ = ['AuthenticationService', 'UserService'] \ 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 a4a0a0f2fd2..6a6e3552c6d 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 @@ -161,7 +161,6 @@ def update_profile(self, user_profile: UserProfileUpdate, temp_auth_token: Optio endpoint = f"{self.base_url}/profile/_update" return self.api_client.post(endpoint, json_data=payload) - def search_users(self, search_criteria: UserSearchModel, temp_auth_token: Optional[str] = None) -> Dict: """ Search for users based on given criteria diff --git a/accelerators/digit_client/digit_client.egg-info/SOURCES.txt b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt index a33210f0b4f..d5d64baf913 100644 --- a/accelerators/digit_client/digit_client.egg-info/SOURCES.txt +++ b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt @@ -12,14 +12,13 @@ digit_client.egg-info/dependency_links.txt digit_client.egg-info/requires.txt digit_client.egg-info/top_level.txt digit_client/models/__init__.py +digit_client/models/auth.py digit_client/models/boundary.py digit_client/models/citizen_user.py digit_client/models/search_models.py -digit_client/models/service.py digit_client/models/service_definition.py -digit_client/models/user.py digit_client/models/user_profile.py digit_client/services/__init__.py -digit_client/services/tenant_service.py +digit_client/services/authenticate.py digit_client/services/user_service.py tests/test_user_service.py \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/__init__.py b/accelerators/digit_client/digit_client/__init__.py index 51012afee9c..9d24096decf 100644 --- a/accelerators/digit_client/digit_client/__init__.py +++ b/accelerators/digit_client/digit_client/__init__.py @@ -6,24 +6,28 @@ from .api_client import APIClient from .config import Config -from .services import TenantService, UserService -from .request_config import RequestConfig, RequestInfo +from .services import AuthenticationService, UserService +from .request_config import RequestConfig, RequestInfo, RequestInfoBuilder from .models.citizen_user import CitizenUser, Role, CitizenUserBuilder from .models.search_models import UserSearchModel from .models.user_profile import UserProfileUpdate, UserRole,UserProfileUpdateBuilder +from .models.auth import AuthenticationRequest, AuthenticationRequestBuilder __all__ = [ 'APIClient', 'Config', - 'TenantService', + 'AuthenticationService', 'UserService', 'RequestConfig', 'RequestInfo', + 'RequestInfoBuilder', 'CitizenUser', 'Role', 'UserSearchModel', 'UserProfileUpdate', 'UserRole', 'UserProfileUpdateBuilder', - 'CitizenUserBuilder' + 'CitizenUserBuilder', + 'AuthenticationRequest', + 'AuthenticationRequestBuilder' ] \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/api_client.py b/accelerators/digit_client/digit_client/api_client.py index f23be315a63..15deb280f93 100644 --- a/accelerators/digit_client/digit_client/api_client.py +++ b/accelerators/digit_client/digit_client/api_client.py @@ -11,11 +11,32 @@ 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, additional_headers=None): + def post(self, endpoint, json_data=None, data=None, additional_headers=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 + + Returns: + dict: Response JSON + """ headers = {'Authorization': f'Bearer {self.auth_token}'} if additional_headers: headers.update(additional_headers) - response = requests.post(f"{self.base_url}/{endpoint}", headers=headers, json=json_data) + print(headers) + print(json_data) + print(data) + + 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 + ) return response.json() # Add more HTTP methods as needed \ 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/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/user.py b/accelerators/digit_client/digit_client/models/user.py deleted file mode 100644 index a535f2ed7c1..00000000000 --- a/accelerators/digit_client/digit_client/models/user.py +++ /dev/null @@ -1,12 +0,0 @@ -class User: - def __init__(self, name, mobile_number, email): - self.name = name - self.mobile_number = mobile_number - self.email = email - - def to_dict(self): - return { - "name": self.name, - "mobile_number": self.mobile_number, - "email": self.email - } \ 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 index bdc934b52f2..80696d27ba4 100644 --- a/accelerators/digit_client/digit_client/request_config.py +++ b/accelerators/digit_client/digit_client/request_config.py @@ -52,6 +52,159 @@ def with_auth_token(self, temp_auth_token: str) -> 'RequestInfo': 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 @@ -66,7 +219,13 @@ def initialize(cls, api_id: str = "DIGIT-CLIENT", version: str = "1.0.0", auth_token: Optional[str] = None, - user_info: Optional[dict] = None) -> 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 = "") -> None: """ Initialize the default request configuration. @@ -75,16 +234,186 @@ def initialize(cls, 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=int(time.time() * 1000), - action="", + action=action, + auth_token=auth_token, + user_info=user_info, + did=did, + key=key, + msg_id=msg_id or str(uuid.uuid4()), + requester_id=requester_id, + correlation_id=correlation_id or str(uuid.uuid4()) + ) + + @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, - msg_id="", - correlation_id="" + 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 diff --git a/accelerators/digit_client/digit_client/services/__init__.py b/accelerators/digit_client/digit_client/services/__init__.py index e8a306493e8..1d450100415 100644 --- a/accelerators/digit_client/digit_client/services/__init__.py +++ b/accelerators/digit_client/digit_client/services/__init__.py @@ -1,5 +1,5 @@ # __init__.py for services package -from .tenant_service import TenantService +from .authenticate import AuthenticationService from .user_service import UserService -__all__ = ['TenantService', 'UserService'] \ No newline at end of file +__all__ = ['AuthenticationService', 'UserService'] \ 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..fff81117e57 --- /dev/null +++ b/accelerators/digit_client/digit_client/services/authenticate.py @@ -0,0 +1,91 @@ +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", + ) + + payload = { + "RequestInfo": request_info.to_dict() + } + + endpoint = f"{self.base_url}/password/nologin/_update" + return self.api_client.post( + endpoint, + json_data=payload + ) + + def logout(self, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: + """ + Logout the user using their access token + + Args: + request_info (Optional[RequestInfo]): Request information containing auth details + temp_auth_token (Optional[str]): Temporary authentication token for the request + + 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( + action="POST", + msg_id="create-citizen-user", + temp_auth_token=temp_auth_token + ) + + 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/tenant_service.py b/accelerators/digit_client/digit_client/services/tenant_service.py deleted file mode 100644 index 4195702a0b7..00000000000 --- a/accelerators/digit_client/digit_client/services/tenant_service.py +++ /dev/null @@ -1,44 +0,0 @@ -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/digit_client/services/user_service.py b/accelerators/digit_client/digit_client/services/user_service.py index a4a0a0f2fd2..8e018d6e622 100644 --- a/accelerators/digit_client/digit_client/services/user_service.py +++ b/accelerators/digit_client/digit_client/services/user_service.py @@ -1,92 +1,34 @@ -# from typing import Dict, Optional -# from ..api_client import APIClient -# from ..models.citizen_user import CitizenUser -# from ..request_config import RequestConfig - -# class UserService: -# def __init__(self, api_client: Optional[APIClient] = None): -# self.api_client = api_client or APIClient() -# self.base_url = "user/citizen" - -# def create_citizen(self, citizen_user: CitizenUser, temp_auth_token: Optional[str] = None) -> Dict: -# """ -# Create a new citizen user in the DIGIT platform. - -# Args: -# citizen_user (CitizenUser): The citizen user data -# temp_auth_token (str, optional): Temporary auth token to use for this request - -# Returns: -# Dict: Response from the user creation API -# """ -# # Get request info for this specific action -# request_info = RequestConfig.get_request_info( -# action="POST", -# msg_id="create-citizen-user", -# temp_auth_token=temp_auth_token -# ) - -# payload = { -# "RequestInfo": request_info.to_dict(), -# "user": citizen_user.to_dict() -# } - -# endpoint = f"{self.base_url}/_create" -# return self.api_client.post(endpoint, json_data=payload) - -# def get_user(self, user_id: str, temp_auth_token: Optional[str] = None) -> Dict: -# """ -# Get user details by user ID - -# Args: -# user_id (str): The ID of the user to retrieve -# temp_auth_token (str, optional): Temporary auth token to use for this request - -# Returns: -# Dict: User details from the API -# """ -# request_info = RequestConfig.get_request_info( -# action="GET", -# msg_id="get-user-details", -# temp_auth_token=temp_auth_token -# ) - -# params = {"RequestInfo": request_info.to_dict()} -# return self.api_client.get(f"{self.base_url}/{user_id}", params=params) - -# # Add more user related methods as needed - - - 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_profile import UserProfileUpdate -from ..request_config import RequestConfig +from ..request_config import RequestConfig, RequestInfo class UserService: def __init__(self, api_client: Optional[APIClient] = None): self.api_client = api_client or APIClient() self.base_url = "user" - def create_citizen(self, citizen_user: CitizenUser, temp_auth_token: Optional[str] = None) -> Dict: + def create_citizen(self, citizen_user: CitizenUser, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = 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 temp_auth_token (str, optional): Temporary auth token to use for this request Returns: Dict: Response from the user creation API """ - # Get request info for this specific action - request_info = RequestConfig.get_request_info( - action="POST", - msg_id="create-citizen-user", - temp_auth_token=temp_auth_token - ) + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="create-citizen-user", + temp_auth_token=temp_auth_token + ) payload = { "RequestInfo": request_info.to_dict(), @@ -96,22 +38,25 @@ def create_citizen(self, citizen_user: CitizenUser, temp_auth_token: Optional[st endpoint = f"{self.base_url}/citizen/_create" return self.api_client.post(endpoint, json_data=payload) - def get_user_details(self, tenant_id: str,temp_auth_token: Optional[str] = None) -> Dict: + def get_user_details(self, tenant_id: str, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: """ - Get user details using access token. If temp_auth_token is not provided, uses the auth token from RequestInfo. + 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 temp_auth_token (str, optional): Access token of the user. If not provided, uses auth token from RequestInfo Returns: Dict: User details from the API """ - request_info = RequestConfig.get_request_info( - action="POST", - msg_id="get-user-details", - temp_auth_token=temp_auth_token - ) + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="get-user-details", + temp_auth_token=temp_auth_token + ) payload = { "RequestInfo": request_info.to_dict() @@ -136,22 +81,25 @@ def get_user_details(self, tenant_id: str,temp_auth_token: Optional[str] = None) endpoint = f"{self.base_url}/_details" return self.api_client.post(endpoint, json_data=payload, params=params) - def update_profile(self, user_profile: UserProfileUpdate, temp_auth_token: Optional[str] = None) -> Dict: + def update_profile(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: """ Update user profile. Args: user_profile (UserProfileUpdate): The updated user profile data + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig temp_auth_token (str, optional): Temporary auth token to use for this request Returns: Dict: Response from the user profile update API """ - request_info = RequestConfig.get_request_info( - action="POST", - msg_id="update-user-profile", - temp_auth_token=temp_auth_token - ) + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="update-user-profile", + temp_auth_token=temp_auth_token + ) payload = { "RequestInfo": request_info.to_dict(), @@ -161,23 +109,25 @@ def update_profile(self, user_profile: UserProfileUpdate, temp_auth_token: Optio endpoint = f"{self.base_url}/profile/_update" return self.api_client.post(endpoint, json_data=payload) - - def search_users(self, search_criteria: UserSearchModel, temp_auth_token: Optional[str] = None) -> Dict: + def search_users(self, search_criteria: UserSearchModel, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = 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 temp_auth_token (str, optional): Temporary auth token to use for this request Returns: Dict: Search results from the API """ - request_info = RequestConfig.get_request_info( - action="POST", - msg_id="search-users", - temp_auth_token=temp_auth_token - ) + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="search-users", + temp_auth_token=temp_auth_token + ) # Combine RequestInfo with search criteria at root level payload = { @@ -188,22 +138,25 @@ def search_users(self, search_criteria: UserSearchModel, temp_auth_token: Option endpoint = f"{self.base_url}/_search" return self.api_client.post(endpoint, json_data=payload) - def create_user_no_validate(self, citizen_user: CitizenUser, temp_auth_token: Optional[str] = None) -> Dict: + def create_user_no_validate(self, citizen_user: CitizenUser, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: """ Create a new user without validation in the DIGIT platform. Args: - user_data (Dict): The user data containing all required fields + citizen_user (CitizenUser): The citizen user data + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig temp_auth_token (str, optional): Temporary auth token to use for this request Returns: Dict: Response from the user creation API """ - request_info = RequestConfig.get_request_info( - action="POST", - msg_id="create-user-no-validate", - temp_auth_token=temp_auth_token - ) + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="create-user-no-validate", + temp_auth_token=temp_auth_token + ) payload = { "RequestInfo": request_info.to_dict(), @@ -213,22 +166,25 @@ def create_user_no_validate(self, citizen_user: CitizenUser, temp_auth_token: Op endpoint = f"{self.base_url}/users/_createnovalidate" return self.api_client.post(endpoint, json_data=payload) - def update_user_no_validate(self, user_profile: UserProfileUpdate, temp_auth_token: Optional[str] = None) -> Dict: + def update_user_no_validate(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: """ Update a user without validation in the DIGIT platform. Args: - user_data (Dict): The user data containing all fields to update including id and uuid + user_profile (UserProfileUpdate): The updated user profile data + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig temp_auth_token (str, optional): Temporary auth token to use for this request Returns: Dict: Response from the user update API """ - request_info = RequestConfig.get_request_info( - action="POST", - msg_id="update-user-no-validate", - temp_auth_token=temp_auth_token - ) + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="update-user-no-validate", + temp_auth_token=temp_auth_token + ) payload = { "RequestInfo": request_info.to_dict(), 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 6e35e40217f5fc0e8cad8aff5c379392059a81c0..73b6a2d2ee927456d475c90b4b723fb86b5a72c5 100644 GIT binary patch delta 8158 zcmZv>WmFtnw>8{p+?^)4(-5SA-~ZQPyU9yDm<1Sd4^?(QC3g9QS?-GdVd5a8ux z+&i9ozUQkywQAN}dzX&6_FNI>34UbC@(7580000L@JHNJH(7&qT^4SRO~I*TOLgnV+t7JNfa7SQL037|3IGb!(0ww%uS4$eXUO&)Ug4si>VkmK%r~+Z&Za7OsB6 zOrXb-di7M1JY3IlhVq{$+{@~zE5)_dy7T;mY`8tN*UY3w!`NID&^sdqtt`;Gd^H-5{J$u{87RE?gBs`Opfj_OP0-_2t7 zSFZ8kRPh=(?S}Wd<*Bc=BGb$Wd!cTh&An^3Gogh*rvDpE0_tE42h?+(qLB{gmaU)b z(KhrUax@j)z(|-lWq#Q4E^`D*PXh$SbXzC_`rEdH@qBmw{kGMt)Q#1s7lv4m^GJw} z7?fz-l|(kcfE1+wGqLH@(84HjTm($HuP}v;CUu2Iqhy`G1eTVmix?u9ERi2lGDO4= z4EOl?>toxSIXmcT9y___TIl*M_vvQ3M0kb5ZI z!#u5`?6@gE@#LlMR%#cW@(%~Y&9;HLg~!TSLl5fZ(gXkM=roQ@iL`bK7<Mp>iPO|s(G!vzxBL+8X?4U-eE zl_O(^;~o(H^C390xa!{98m`|z|9I2zPF587H&)_$6ol*-_-8Vnx|0G=ZWd?&0EH9) zKnZ~Bvr)s2_4FKWI}8Uy0b9Nw37&>Y8ojb8aTwMONcW|Y8jOBX3V7? z?aWI5&}X(Pjgw_7js8|)5wOq$c{+UR$@)cB%07HjINF1{yZBw$?2h?wuB zmE9jVu~NR#t+;4ns6JTJTdbS0(V3*eYe8?L*U^M&GK|#~6qfHKY^QU*iWIQc;b!1# z`0P=U39mDq>Jp3DWTxtCLtjWFk56`!D&9_8UBsC?Hs8g)?R#yS*YH7Bw#!`WWqGwV z52U|oV6{axWj65O1+m-AltxUX6SC5t7a`$*f((#!AbJHgK7jbQt*8Z?uz4jnF6bq8 zzvEBXU|>CX`CQ!LjO1}KHsmTtYIPQiom!k>@@$erHf`~Zf{KLbWihurn@&dQqX~g^ zDKNULS=R0aLgpnkzqx&M#e}lKGKt#7wwA+urYY%YlcJP}?p>CDAT)AhSv}nzk3c!W zCQPsRb7NU_BNRTBLYemDEF2p|=$i)ts(|l*iGL^{+KE`w_ z+AD9|SI#?xpo1kU<-(q4;X}~NZtF8B?#E#`J5CuJ?%nr-3p|c#bU5}GK0xQ;Bik*x z0{QezL=VQ&9H>%kW93LY6*&|`Rp}kAJ4y2V2>XsduujM3Q${{gzh*yXi-u>B1;RST zZkpyd7K=E?`{RosU;CzhFZ*%WZ3Jb8T204O$jwnoSV19&lV?it@k84OJw4)r3}&$% z3xwdeb}w=(`K}9EWKozH6xKF0g0=ji>&k(hVc=;xaygT`FhlkAdKg+-Sa|a-14A*B zrsxcjM1{iX2W|I5A=g>!8S5d`XjpaU@XKX)T$q844svE|!|S*z*jiFKFrJeM*q8Jr zoLXN(!62vH*xa~H_Tl7>f!IfQR`-+HdhV_JK%JBU+n9XnpEmbuM##k`B~_=Lm3R_^ z0TDEBO#M7(G~Pw~K4c`9Zl`bnRZzq6DxQ=;-PJjeR&1D|eI`)~D~kA>3KrHy(#mUi z6_umbIEAX(PD@J1M>iEi%{R)R7_CW3<56Bhjk3DO=a6SRT6HL~1k%+?tsaG0*J(GZ znYPBZL&63oeS!lzushJC4(=ao)6V~WJyHs+x4-4S6RDh|>x?V6qD z4sG+HqNG9@({+3-?fGXls4SZnmM?Vs=jTvO1!o#}u7+S~{DefFVKOVaA9EE_9tGWl zyS!j=2t1Xl^^YKA>$|oR7Fdf6oZ0VH>tVFe4?=wF_se_!{aJE?Im`kX&%*Jf@XrO< zDt-HW>j+2SN1xbE!D+S{ueK<&cgKj9nD|8*^>HY>9BQs|bx?m=UCuFoAE7rzA7lsa zK?t7-_R3Bb;Ed?;z2*WR1&!O>@FBlumYIZ!8Rc= z5AJHnAIN69g-QjDzsz=*QlFcD~U6RhL=q&O!c}AxY1Z| z^>0t5NKaS{_?DCB2M+wzCl%j)sWG>mtXt~9#OD6m5J~1M4CB#ADddzP=S`F8)`!bs)0!q)hN~ z1#l8M(g6DVtBAx?d$<(JtM<{Q3~?QH5GT*QVE!i3v=B7wY^2bT$-}4waqx+$v|W$b zVZ+49FeSOF9d_!kp@Uf&zx;XFq;y&KOkH`+DpUkWY{_7+P*pYNwWS3LnA&9h<*t>B zlQ%~-J}q^znSox}yxApp+4F^;3}QASZBmxM@Y~olBl-ti28ygLl$MI15@T$AVo(1b z2S7RQhq=1&zJ%S1Cs<1g%N?K?t&}T|V2X?#_r?qP0j6x9@ujm74oA(|&1d2Qpx!9S zzLk$g_GTja$SfReDg0@$u)96y`l=SU{$i2Nk>~?K0MG2DLLS;l(tD$~OAoC?A;P)3yHY)x^#H#y;_~9w^6c&DNgEY>h~R)j@RtkZ)NCI&5v`@x3JHc&Y)^@ zBHg7y^|4lm{-mLhXjCf#ZS6v;(!BiwGDXv~g00I3f3t2jp3X=S%F7Ei63L?GXk>5! zhK38wR$L6I60E3_WMXYgs9k0}w}tHShIfR<8rEDBtx$2R_!*p;s;@6pFMXR@Q>vT| zj(vzPm=86Azhs{L z!{iuMmjnEdZoD~t)VDo=xAXGw#M;uHu~OrX$DuiK?CfSPyt(a2+hKpsxUZypB~&bt zo3LEyHpOU3tv+e6v^O?LGMpztHQJRoW0;m1bH*7$Go<=q5(EEDsz4SzlU#7O5cxaL z@d&S;AHQvm>Ep>7hSH%}b0nqm`3A?G(!vF|m=NSwZgkSYGRTFnwVBF+@HJk+^!EGj z%O^U%t`l@PX9F+VV`F?&V0YUy_i;hK=`f~WL6mM!F1L?2kgi_N2j4&Q{zb)_t$h1E zEVZZlEo&jId#K&hWnLnYC~JF}8IzwGJfMHr1Z#cw2?ihU^cVQkmpw0iS4%L~^Nm(2 zJx1=-{`@KL67`_UZo<~+Gxo}scl_?|u{VuCxx&o-akb`h?{qZ#$!L`lO%xU(3WC`r z>qo87g3w(TThIJ4T#j5P@?jERuzD3lBO*nVNe7cbElawm>{qR9dA&km#~#`a0TbyI zqPcxBN)6L4OPPJnChpj_>^dK-e3khIL)>BwE-0?e$3jgx*pD;rUzG1Zf_S=cP9G)K zXh;~F@6k8Hexq-g1XWoyyxV5#Zh?^qFa-=b!FB9eF!&(kU!f!q?Pa<5uNB7y@SOTt~Qd z+tUSNn~Y!c`i55?kM`2D+YKzG#p0 zPCED_=uRL#hc>6`3WF@k;z6lVoF54HZdSMSMF-qe{Vw=9j$1>%yh}gJLs~9ZvF&H1 z(pyvq2kPCu*f)(blJjL9O*yv%uHPtWvm6}bV;6(L`DNg?nQ-V1R*Er8O<a<1ZYBu}fb zxiQ#tqhkAL<9p)`9hr-GQ>QDgm&Uwst3I-)M8ACt6Bn3d9`^&3%AYNi*%@ zs@zR(EB3B8(F#t|lZ>^aJgySx<4mqacy-rDt%$D-CHtgpk*`JVKREszu6!yEnVhq)QgAi#Ym?!oH+8NWaQ|4x7#%tqbGe z*Q`^>aa=96dLCZ=Kqjs}>(M}0UvaO)27{htljmjv8()}!R&}ICNAW77rIL$){u*;u zETunOh)QEkK9lJiQ;XBkWdo32#v63G?^mU?FcdzapGx!xsXhmN6%)Hj^P_H=$c3^u zA^^}q2zSIFg|k3#V1?R>j$6FAe^;clL3Z80a3DE($EG+h78}6s-X(abb#Luk;@&0} zG-DCpuL(&;5X$PH9pB;mb!-TKptKxr2)udQ8D;nTt+uFh?l6Z!8O5!=dK8X^2W+0U zpj2JMBQZf;H-jvVnup40nY@th`txoj#OcG{J^@$Sb-K2Hces|}2gh#Hohan+Zok6fhIN8kkYvpb4!*k2`aR) z_a5Jd@Imik5pL!+x0-b4i4RB zSESExC~jvKQSWY2(H7&9rmH@S$a1Ts!O(t3?1v=QGm5sfi@W}ssI_T7z;LWsPMm$& zcriMPFv}pOoIrlj7^dlf@kz2;xY608fZX|*3fMHm&Aed_aa>kuzN!~*b||X0N{rZBIfvt~pn!wj#ACRgz>5;%~Z+(Tw|(X3)LPt zlDMP>Ss(}yIm&-UFZ-xrw9BaS`z8`KK7^#f`hBQ96(z9}nj;=%(yb+<^ z5P^=CgabMZxD$pb){w2KDQkzB0GX2G2<$&@3BoH+-NDu$98S+}Ne%prOw~}Hm2E_3 z{MCp8lvP$^_$%8m2aAf++fU}Zdq|S5y{{91iVLN?Zm^?$Yf+4|h)^whnJDUWD-R`xa&xE>YxUs*SIaJ7DK;%fenysI17 zEwI{)a%P0ZCmHCFEn8(qfbC6ZNs58Fi7o5&IecQ)Z zb$Z`2i0u|@JI?kY9=F`jc_H70L-6jvFLBojc0TPv=l_Y5l2XLw=m;})m@0sD>rN-g zimdRZt}QxZ&X~_I#R>Fk5s|6o6=GQY!@B8IN$d@y*p!NVE|ovsd`2t3ZrV9#-walB zbP^}#Bt=)Mu54dlyi*Oz!iBG777Fy`JhIot>nb6a;Y4DX@R?48MRp0Ez+IhbtO7X| zY>(=s8B{JWsda-Tq_*%AGKylUYuwO+AZs94c~l4A*uOEYUtmQqEJq><93$U0uFvhh z?4q=q6y;s7(;Q^ns;_nKh!u7u-!Ps1#`<&R8%wWPzZs>J>fl=;yaF}mqsOoo&;1S6 zN{#p4r@I1tMU5^IQQuDmfAtpMH?^Mc!!XffB|N@wLLXPMUjP#L0nBYKNvLu?9QYTX zlYnL$Pv*j3D#))jk(yCf*FD!Q>#`APFhaV~d@08>cTMJ(j`z0e&KLFCD)Er%YZ0q7 zL#wMDPED!QEff*QoV$+`6Y}(j}%&UjcJrE~eutDGbgd)>* zaigN_2iJa}Div3jaojLgeC5!JUE5v$h9E{##NS-BvlF$&tP$Vu9`xI~VI@0O<`wf} z^2jm9yUgDv3G{x>LMHjAYy%Y!7`m6hdKmcx5KB}0qb^^9d)UIW_uOBvOLc$qo_=bBGIMb`rk)bHlHf|GRMMTX;7GWQv@{E+1tqS<|NRd&X}VJM}X`EzU zHHv$iQ0+T8C#vYzxL+pk((x0VkWE8Uw~VsV<#K?UsZsI5iQa>cH%0|M-!eZf_`DRo zc54_SJ9E0BwK%wJrj63-!WmO9_AoLz+(S3&0O>ap^8Bf*0n1--@$lWey8ft^v{Bof z54Z{K^)AeMqC%vEiVFdkaD7k%p8k(xdn@Bv@5oU60^vD;zhp}OyRTfLaPLz9LQOEmfk6+O*y%e3@4p6Xf9M)Mya5bKRhh#KxZH@4<&L zB}LwEdnoOhy5Ud}Br{{n3l*)EYO|=WHmzC9Co&*u z8-|o;vsOBoD~g2;YHuWq%+xv9U!XZrGwG0~;WRa(g2H&zmmJdC&a#^oF+$0x@g z^N;8lSJ3Xw9oBsYZdwU_WJJf)8b*2WGvpnT^mz+4Da0TA)<5&(!J&VQC|r39i>M@H zuu6+viUVe+A&rLREoSfK)4ZTEW8ro%+SupAdck)#l+^JwoNxRwYMiW@xIc~M;;zoh z^~;T78k!>!nxKIT`$OvI%D7eCQh$i*_Ls;v-GU`3XL|{_o91OPf&r&sEmGG7(K9mP z_&k=*vO$hQRW6^E+9IoC`Y5x7(?3sXkHu91dIvBB9D&e127CU`h0Ub$pOf$SDJ+FW z%7&cQ>`fYCA>#C1U2^>A(p>|dqU)ZSIoU~vauM5d*}Ow|_mbc;KlKpL5A)qsYp?n< z9PA1fw=-=H+$fq5M`G~@BWKt4;Zc9Jm&lS@DY)t{~@G#bQKsW+? zid6#GiwLJgB!@%TFsc8$!}Py7&wbwjeWd4plN|678(zXD4vfcv$AO69Dy$&t|8hV6 zll7c-{+EdOj~N0-V`l??$N!rMW#<7#5&R9)*#&^1WPih-?1I4e6n{fwB6PS62MZ8H z`9Fs!a57?Sc=j{2`42=2-{HWa{`WE!0HFKV+@(~{LW4-JwxeSf1y!MyuZ-uGsMIF7g}S&V*S4_|G!k`|6FcE XK*T>uudqP?5a13O06^sb=j{Ij+qE3* delta 5018 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 jNBD>MmwNv%VqNg>|9Cu?BPON-PysCN Date: Wed, 26 Mar 2025 16:56:28 +0530 Subject: [PATCH 4/7] implemented the mdms data search --- .../build/lib/digit_client/__init__.py | 23 +- .../build/lib/digit_client/api_client.py | 7 +- .../lib/digit_client/services/__init__.py | 3 +- .../lib/digit_client/services/user_service.py | 159 +++++-------- .../digit_client.egg-info/SOURCES.txt | 4 +- .../digit_client/digit_client/__init__.py | 23 +- .../digit_client/digit_client/api_client.py | 7 +- .../digit_client/models/boundary.py | 10 - .../digit_client/digit_client/models/mdms.py | 221 ++++++++++++++++++ .../digit_client/models/service_definition.py | 10 - .../digit_client/services/__init__.py | 3 +- .../digit_client/services/master_data_v1.py | 93 ++++++++ .../dist/digit_client-0.1-py3-none-any.whl | Bin 16148 -> 18851 bytes accelerators/digit_client/use/mdms_v1.py | 127 ++++++++++ 14 files changed, 557 insertions(+), 133 deletions(-) delete mode 100644 accelerators/digit_client/digit_client/models/boundary.py create mode 100644 accelerators/digit_client/digit_client/models/mdms.py delete mode 100644 accelerators/digit_client/digit_client/models/service_definition.py create mode 100644 accelerators/digit_client/digit_client/services/master_data_v1.py create mode 100644 accelerators/digit_client/use/mdms_v1.py diff --git a/accelerators/digit_client/build/lib/digit_client/__init__.py b/accelerators/digit_client/build/lib/digit_client/__init__.py index 9d24096decf..514f0bee20f 100644 --- a/accelerators/digit_client/build/lib/digit_client/__init__.py +++ b/accelerators/digit_client/build/lib/digit_client/__init__.py @@ -6,18 +6,29 @@ from .api_client import APIClient from .config import Config -from .services import AuthenticationService, UserService +from .services import AuthenticationService, UserService, MDMSService from .request_config import RequestConfig, RequestInfo, RequestInfoBuilder from .models.citizen_user import CitizenUser, Role, CitizenUserBuilder from .models.search_models import UserSearchModel from .models.user_profile import UserProfileUpdate, UserRole,UserProfileUpdateBuilder from .models.auth import AuthenticationRequest, AuthenticationRequestBuilder +from .models.mdms import ( + MdmsCriteriaReq, + MdmsCriteriaReqBuilder, + MdmsCriteria, + MdmsCriteriaBuilder, + ModuleDetail, + ModuleDetailBuilder, + MasterDetail, + MasterDetailBuilder +) __all__ = [ 'APIClient', 'Config', 'AuthenticationService', 'UserService', + 'MDMSService', 'RequestConfig', 'RequestInfo', 'RequestInfoBuilder', @@ -29,5 +40,13 @@ 'UserProfileUpdateBuilder', 'CitizenUserBuilder', 'AuthenticationRequest', - 'AuthenticationRequestBuilder' + 'AuthenticationRequestBuilder', + 'MdmsCriteriaReq', + 'MdmsCriteriaReqBuilder', + 'MdmsCriteria', + 'MdmsCriteriaBuilder', + 'ModuleDetail', + 'ModuleDetailBuilder', + 'MasterDetail', + 'MasterDetailBuilder' ] \ 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 15deb280f93..24684fbbc2c 100644 --- a/accelerators/digit_client/build/lib/digit_client/api_client.py +++ b/accelerators/digit_client/build/lib/digit_client/api_client.py @@ -11,7 +11,7 @@ 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=None, data=None, additional_headers=None): + def post(self, endpoint, json_data=None, data=None, additional_headers=None, params=None): """ Make a POST request @@ -20,6 +20,7 @@ def post(self, endpoint, json_data=None, data=None, additional_headers=None): 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 @@ -30,12 +31,14 @@ def post(self, endpoint, json_data=None, data=None, additional_headers=None): 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 + data=data if data is not None else None, + params=params if params is not None else None ) return response.json() 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 1d450100415..7dff1f34cf7 100644 --- a/accelerators/digit_client/build/lib/digit_client/services/__init__.py +++ b/accelerators/digit_client/build/lib/digit_client/services/__init__.py @@ -1,5 +1,6 @@ # __init__.py for services package from .authenticate import AuthenticationService from .user_service import UserService +from .master_data_v1 import MDMSService -__all__ = ['AuthenticationService', 'UserService'] \ No newline at end of file +__all__ = ['AuthenticationService', 'UserService', 'MDMSService'] \ 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 6a6e3552c6d..8e018d6e622 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,92 +1,34 @@ -# from typing import Dict, Optional -# from ..api_client import APIClient -# from ..models.citizen_user import CitizenUser -# from ..request_config import RequestConfig - -# class UserService: -# def __init__(self, api_client: Optional[APIClient] = None): -# self.api_client = api_client or APIClient() -# self.base_url = "user/citizen" - -# def create_citizen(self, citizen_user: CitizenUser, temp_auth_token: Optional[str] = None) -> Dict: -# """ -# Create a new citizen user in the DIGIT platform. - -# Args: -# citizen_user (CitizenUser): The citizen user data -# temp_auth_token (str, optional): Temporary auth token to use for this request - -# Returns: -# Dict: Response from the user creation API -# """ -# # Get request info for this specific action -# request_info = RequestConfig.get_request_info( -# action="POST", -# msg_id="create-citizen-user", -# temp_auth_token=temp_auth_token -# ) - -# payload = { -# "RequestInfo": request_info.to_dict(), -# "user": citizen_user.to_dict() -# } - -# endpoint = f"{self.base_url}/_create" -# return self.api_client.post(endpoint, json_data=payload) - -# def get_user(self, user_id: str, temp_auth_token: Optional[str] = None) -> Dict: -# """ -# Get user details by user ID - -# Args: -# user_id (str): The ID of the user to retrieve -# temp_auth_token (str, optional): Temporary auth token to use for this request - -# Returns: -# Dict: User details from the API -# """ -# request_info = RequestConfig.get_request_info( -# action="GET", -# msg_id="get-user-details", -# temp_auth_token=temp_auth_token -# ) - -# params = {"RequestInfo": request_info.to_dict()} -# return self.api_client.get(f"{self.base_url}/{user_id}", params=params) - -# # Add more user related methods as needed - - - 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_profile import UserProfileUpdate -from ..request_config import RequestConfig +from ..request_config import RequestConfig, RequestInfo class UserService: def __init__(self, api_client: Optional[APIClient] = None): self.api_client = api_client or APIClient() self.base_url = "user" - def create_citizen(self, citizen_user: CitizenUser, temp_auth_token: Optional[str] = None) -> Dict: + def create_citizen(self, citizen_user: CitizenUser, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = 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 temp_auth_token (str, optional): Temporary auth token to use for this request Returns: Dict: Response from the user creation API """ - # Get request info for this specific action - request_info = RequestConfig.get_request_info( - action="POST", - msg_id="create-citizen-user", - temp_auth_token=temp_auth_token - ) + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="create-citizen-user", + temp_auth_token=temp_auth_token + ) payload = { "RequestInfo": request_info.to_dict(), @@ -96,22 +38,25 @@ def create_citizen(self, citizen_user: CitizenUser, temp_auth_token: Optional[st endpoint = f"{self.base_url}/citizen/_create" return self.api_client.post(endpoint, json_data=payload) - def get_user_details(self, tenant_id: str,temp_auth_token: Optional[str] = None) -> Dict: + def get_user_details(self, tenant_id: str, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: """ - Get user details using access token. If temp_auth_token is not provided, uses the auth token from RequestInfo. + 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 temp_auth_token (str, optional): Access token of the user. If not provided, uses auth token from RequestInfo Returns: Dict: User details from the API """ - request_info = RequestConfig.get_request_info( - action="POST", - msg_id="get-user-details", - temp_auth_token=temp_auth_token - ) + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="get-user-details", + temp_auth_token=temp_auth_token + ) payload = { "RequestInfo": request_info.to_dict() @@ -136,22 +81,25 @@ def get_user_details(self, tenant_id: str,temp_auth_token: Optional[str] = None) endpoint = f"{self.base_url}/_details" return self.api_client.post(endpoint, json_data=payload, params=params) - def update_profile(self, user_profile: UserProfileUpdate, temp_auth_token: Optional[str] = None) -> Dict: + def update_profile(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: """ Update user profile. Args: user_profile (UserProfileUpdate): The updated user profile data + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig temp_auth_token (str, optional): Temporary auth token to use for this request Returns: Dict: Response from the user profile update API """ - request_info = RequestConfig.get_request_info( - action="POST", - msg_id="update-user-profile", - temp_auth_token=temp_auth_token - ) + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="update-user-profile", + temp_auth_token=temp_auth_token + ) payload = { "RequestInfo": request_info.to_dict(), @@ -161,22 +109,25 @@ def update_profile(self, user_profile: UserProfileUpdate, temp_auth_token: Optio endpoint = f"{self.base_url}/profile/_update" return self.api_client.post(endpoint, json_data=payload) - def search_users(self, search_criteria: UserSearchModel, temp_auth_token: Optional[str] = None) -> Dict: + def search_users(self, search_criteria: UserSearchModel, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = 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 temp_auth_token (str, optional): Temporary auth token to use for this request Returns: Dict: Search results from the API """ - request_info = RequestConfig.get_request_info( - action="POST", - msg_id="search-users", - temp_auth_token=temp_auth_token - ) + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="search-users", + temp_auth_token=temp_auth_token + ) # Combine RequestInfo with search criteria at root level payload = { @@ -187,22 +138,25 @@ def search_users(self, search_criteria: UserSearchModel, temp_auth_token: Option endpoint = f"{self.base_url}/_search" return self.api_client.post(endpoint, json_data=payload) - def create_user_no_validate(self, citizen_user: CitizenUser, temp_auth_token: Optional[str] = None) -> Dict: + def create_user_no_validate(self, citizen_user: CitizenUser, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: """ Create a new user without validation in the DIGIT platform. Args: - user_data (Dict): The user data containing all required fields + citizen_user (CitizenUser): The citizen user data + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig temp_auth_token (str, optional): Temporary auth token to use for this request Returns: Dict: Response from the user creation API """ - request_info = RequestConfig.get_request_info( - action="POST", - msg_id="create-user-no-validate", - temp_auth_token=temp_auth_token - ) + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="create-user-no-validate", + temp_auth_token=temp_auth_token + ) payload = { "RequestInfo": request_info.to_dict(), @@ -212,22 +166,25 @@ def create_user_no_validate(self, citizen_user: CitizenUser, temp_auth_token: Op endpoint = f"{self.base_url}/users/_createnovalidate" return self.api_client.post(endpoint, json_data=payload) - def update_user_no_validate(self, user_profile: UserProfileUpdate, temp_auth_token: Optional[str] = None) -> Dict: + def update_user_no_validate(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: """ Update a user without validation in the DIGIT platform. Args: - user_data (Dict): The user data containing all fields to update including id and uuid + user_profile (UserProfileUpdate): The updated user profile data + request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig temp_auth_token (str, optional): Temporary auth token to use for this request Returns: Dict: Response from the user update API """ - request_info = RequestConfig.get_request_info( - action="POST", - msg_id="update-user-no-validate", - temp_auth_token=temp_auth_token - ) + # Use provided RequestInfo or get from global config + if request_info is None: + request_info = RequestConfig.get_request_info( + action="POST", + msg_id="update-user-no-validate", + temp_auth_token=temp_auth_token + ) payload = { "RequestInfo": request_info.to_dict(), diff --git a/accelerators/digit_client/digit_client.egg-info/SOURCES.txt b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt index d5d64baf913..ca033aaf5ba 100644 --- a/accelerators/digit_client/digit_client.egg-info/SOURCES.txt +++ b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt @@ -13,12 +13,12 @@ digit_client.egg-info/requires.txt digit_client.egg-info/top_level.txt digit_client/models/__init__.py digit_client/models/auth.py -digit_client/models/boundary.py digit_client/models/citizen_user.py +digit_client/models/mdms.py digit_client/models/search_models.py -digit_client/models/service_definition.py digit_client/models/user_profile.py digit_client/services/__init__.py digit_client/services/authenticate.py +digit_client/services/master_data_v1.py digit_client/services/user_service.py tests/test_user_service.py \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/__init__.py b/accelerators/digit_client/digit_client/__init__.py index 9d24096decf..514f0bee20f 100644 --- a/accelerators/digit_client/digit_client/__init__.py +++ b/accelerators/digit_client/digit_client/__init__.py @@ -6,18 +6,29 @@ from .api_client import APIClient from .config import Config -from .services import AuthenticationService, UserService +from .services import AuthenticationService, UserService, MDMSService from .request_config import RequestConfig, RequestInfo, RequestInfoBuilder from .models.citizen_user import CitizenUser, Role, CitizenUserBuilder from .models.search_models import UserSearchModel from .models.user_profile import UserProfileUpdate, UserRole,UserProfileUpdateBuilder from .models.auth import AuthenticationRequest, AuthenticationRequestBuilder +from .models.mdms import ( + MdmsCriteriaReq, + MdmsCriteriaReqBuilder, + MdmsCriteria, + MdmsCriteriaBuilder, + ModuleDetail, + ModuleDetailBuilder, + MasterDetail, + MasterDetailBuilder +) __all__ = [ 'APIClient', 'Config', 'AuthenticationService', 'UserService', + 'MDMSService', 'RequestConfig', 'RequestInfo', 'RequestInfoBuilder', @@ -29,5 +40,13 @@ 'UserProfileUpdateBuilder', 'CitizenUserBuilder', 'AuthenticationRequest', - 'AuthenticationRequestBuilder' + 'AuthenticationRequestBuilder', + 'MdmsCriteriaReq', + 'MdmsCriteriaReqBuilder', + 'MdmsCriteria', + 'MdmsCriteriaBuilder', + 'ModuleDetail', + 'ModuleDetailBuilder', + 'MasterDetail', + 'MasterDetailBuilder' ] \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/api_client.py b/accelerators/digit_client/digit_client/api_client.py index 15deb280f93..24684fbbc2c 100644 --- a/accelerators/digit_client/digit_client/api_client.py +++ b/accelerators/digit_client/digit_client/api_client.py @@ -11,7 +11,7 @@ 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=None, data=None, additional_headers=None): + def post(self, endpoint, json_data=None, data=None, additional_headers=None, params=None): """ Make a POST request @@ -20,6 +20,7 @@ def post(self, endpoint, json_data=None, data=None, additional_headers=None): 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 @@ -30,12 +31,14 @@ def post(self, endpoint, json_data=None, data=None, additional_headers=None): 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 + data=data if data is not None else None, + params=params if params is not None else None ) return response.json() 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/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/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/services/__init__.py b/accelerators/digit_client/digit_client/services/__init__.py index 1d450100415..7dff1f34cf7 100644 --- a/accelerators/digit_client/digit_client/services/__init__.py +++ b/accelerators/digit_client/digit_client/services/__init__.py @@ -1,5 +1,6 @@ # __init__.py for services package from .authenticate import AuthenticationService from .user_service import UserService +from .master_data_v1 import MDMSService -__all__ = ['AuthenticationService', 'UserService'] \ No newline at end of file +__all__ = ['AuthenticationService', 'UserService', 'MDMSService'] \ No newline at end of file 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..ff510164975 --- /dev/null +++ b/accelerators/digit_client/digit_client/services/master_data_v1.py @@ -0,0 +1,93 @@ +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( + action="POST", + msg_id="5bfa85e7-dfa1-47c8-98b2-747bf552be86" + ) + + # 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( + action="POST", + msg_id="5bfa85e7-dfa1-47c8-98b2-747bf552be86" + ) + + # 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/dist/digit_client-0.1-py3-none-any.whl b/accelerators/digit_client/dist/digit_client-0.1-py3-none-any.whl index 73b6a2d2ee927456d475c90b4b723fb86b5a72c5..50c4e6b304ccf60bfe30d5085334fa188cc2933e 100644 GIT binary patch delta 7613 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 Date: Thu, 27 Mar 2025 16:35:06 +0530 Subject: [PATCH 5/7] mdms_v2 and access control is build --- .../build/lib/digit_client/__init__.py | 34 +- .../build/lib/digit_client/api_client.py | 3 +- .../build/lib/digit_client/models/__init__.py | 5 +- .../build/lib/digit_client/request_config.py | 15 +- .../lib/digit_client/services/__init__.py | 5 +- .../lib/digit_client/services/user_service.py | 54 +- .../digit_client.egg-info/PKG-INFO | 1 + .../digit_client.egg-info/SOURCES.txt | 4 + .../digit_client.egg-info/requires.txt | 1 + .../digit_client/digit_client/__init__.py | 34 +- .../digit_client/digit_client/api_client.py | 3 +- .../models/AuthorizationRequest.py | 117 +++++ .../digit_client/models/__init__.py | 5 +- .../digit_client/models/mdms_v2.py | 467 ++++++++++++++++++ .../digit_client/request_config.py | 15 +- .../digit_client/services/__init__.py | 5 +- .../digit_client/services/authenticate.py | 17 +- .../digit_client/services/authorize.py | 37 ++ .../digit_client/services/master_data_v1.py | 10 +- .../digit_client/services/mdms_v2.py | 142 ++++++ .../digit_client/services/user_service.py | 54 +- .../dist/digit_client-0.1-py3-none-any.whl | Bin 18851 -> 24201 bytes accelerators/digit_client/setup.py | 1 + accelerators/digit_client/use/authorize.py | 46 ++ .../digit_client/use/mdms_v2/mdms_create.py | 57 +++ .../digit_client/use/mdms_v2/mdms_search.py | 34 ++ .../digit_client/use/mdms_v2/schema_create.py | 151 ++++++ .../digit_client/use/mdms_v2/schema_search.py | 27 + 28 files changed, 1213 insertions(+), 131 deletions(-) create mode 100644 accelerators/digit_client/digit_client/models/AuthorizationRequest.py create mode 100644 accelerators/digit_client/digit_client/models/mdms_v2.py create mode 100644 accelerators/digit_client/digit_client/services/authorize.py create mode 100644 accelerators/digit_client/digit_client/services/mdms_v2.py create mode 100644 accelerators/digit_client/use/authorize.py create mode 100644 accelerators/digit_client/use/mdms_v2/mdms_create.py create mode 100644 accelerators/digit_client/use/mdms_v2/mdms_search.py create mode 100644 accelerators/digit_client/use/mdms_v2/schema_create.py create mode 100644 accelerators/digit_client/use/mdms_v2/schema_search.py diff --git a/accelerators/digit_client/build/lib/digit_client/__init__.py b/accelerators/digit_client/build/lib/digit_client/__init__.py index 514f0bee20f..eae395be889 100644 --- a/accelerators/digit_client/build/lib/digit_client/__init__.py +++ b/accelerators/digit_client/build/lib/digit_client/__init__.py @@ -6,7 +6,7 @@ from .api_client import APIClient from .config import Config -from .services import AuthenticationService, UserService, MDMSService +from .services import AuthenticationService, UserService, MDMSService, MDMSV2Service, AuthorizeService from .request_config import RequestConfig, RequestInfo, RequestInfoBuilder from .models.citizen_user import CitizenUser, Role, CitizenUserBuilder from .models.search_models import UserSearchModel @@ -22,6 +22,20 @@ MasterDetail, MasterDetailBuilder ) +from .models.mdms_v2 import ( + SchemaDefinition, + SchemaDefinitionBuilder, + SchemaDefCriteria, + SchemaDefCriteriaBuilder, + AuditDetails, + AuditDetailsBuilder, + Mdms, + MdmsBuilder, + MdmsCriteriaV2, + MdmsCriteriaV2Builder, + +) +from .models.AuthorizationRequest import AuthorizationRequest, AuthorizationRequestBuilder, Role, RoleBuilder __all__ = [ 'APIClient', @@ -29,6 +43,8 @@ 'AuthenticationService', 'UserService', 'MDMSService', + 'MDMSV2Service', + 'AuthorizeService', 'RequestConfig', 'RequestInfo', 'RequestInfoBuilder', @@ -48,5 +64,19 @@ 'ModuleDetail', 'ModuleDetailBuilder', 'MasterDetail', - 'MasterDetailBuilder' + 'MasterDetailBuilder', + 'SchemaDefinition', + 'SchemaDefinitionBuilder', + 'SchemaDefCriteria', + 'SchemaDefCriteriaBuilder', + 'AuditDetails', + 'AuditDetailsBuilder', + 'Mdms', + 'MdmsBuilder', + 'MdmsCriteriaV2', + 'MdmsCriteriaV2Builder', + 'AuthorizationRequest', + 'AuthorizationRequestBuilder', + 'Role', + 'RoleBuilder', ] \ 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 24684fbbc2c..3824e7b0931 100644 --- a/accelerators/digit_client/build/lib/digit_client/api_client.py +++ b/accelerators/digit_client/build/lib/digit_client/api_client.py @@ -25,7 +25,8 @@ def post(self, endpoint, json_data=None, data=None, additional_headers=None, par Returns: dict: Response JSON """ - headers = {'Authorization': f'Bearer {self.auth_token}'} + # headers = {'Authorization': f'Bearer {self.auth_token}'} + headers = {'Content-Type': 'application/json'} if additional_headers: headers.update(additional_headers) print(headers) 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..12bd52ad096 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,4 @@ -# __init__.py for models package \ No newline at end of file +# __init__.py for models package +from .AuthorizationRequest import Role, RoleBuilder, AuthorizationRequest, AuthorizationRequestBuilder + +__all__ = ['Role', 'RoleBuilder', 'AuthorizationRequest', 'AuthorizationRequestBuilder'] \ 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 index 80696d27ba4..f283c002b82 100644 --- a/accelerators/digit_client/build/lib/digit_client/request_config.py +++ b/accelerators/digit_client/build/lib/digit_client/request_config.py @@ -225,7 +225,8 @@ def initialize(cls, msg_id: Optional[str] = None, requester_id: Optional[str] = None, correlation_id: Optional[str] = None, - action: str = "") -> None: + action: str = "", + ts: Optional[int] = None) -> None: """ Initialize the default request configuration. @@ -244,15 +245,15 @@ def initialize(cls, cls._default_request_info = RequestInfo( api_id=api_id, ver=version, - ts=int(time.time() * 1000), + ts=ts, action=action, auth_token=auth_token, user_info=user_info, did=did, key=key, - msg_id=msg_id or str(uuid.uuid4()), + msg_id=msg_id, requester_id=requester_id, - correlation_id=correlation_id or str(uuid.uuid4()) + correlation_id=correlation_id ) @classmethod @@ -417,7 +418,7 @@ def update_requester_id(cls, requester_id: str) -> None: ) @classmethod - def get_request_info(cls, action: str, temp_auth_token: Optional[str] = None, **kwargs) -> RequestInfo: + def get_request_info(cls,temp_auth_token: Optional[str] = None, **kwargs) -> RequestInfo: """ Get a new RequestInfo instance with default values and specified overrides. @@ -435,10 +436,6 @@ def get_request_info(cls, action: str, temp_auth_token: Optional[str] = None, ** # Create new request info with current timestamp request_info_dict = cls._default_request_info.to_dict() request_info_dict.update({ - "ts": int(time.time() * 1000), - "action": action, - "msgId": str(uuid.uuid4()), - "correlationId": str(uuid.uuid4()), **kwargs }) 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 7dff1f34cf7..b1fb2116298 100644 --- a/accelerators/digit_client/build/lib/digit_client/services/__init__.py +++ b/accelerators/digit_client/build/lib/digit_client/services/__init__.py @@ -2,5 +2,6 @@ from .authenticate import AuthenticationService from .user_service import UserService from .master_data_v1 import MDMSService - -__all__ = ['AuthenticationService', 'UserService', 'MDMSService'] \ No newline at end of file +from .mdms_v2 import MDMSV2Service +from .authorize import AuthorizeService +__all__ = ['AuthenticationService', 'UserService', 'MDMSService', 'MDMSV2Service', 'AuthorizeService'] \ 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 8e018d6e622..35b0c1fbc8f 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 @@ -10,25 +10,20 @@ def __init__(self, api_client: Optional[APIClient] = None): self.api_client = api_client or APIClient() self.base_url = "user" - def create_citizen(self, citizen_user: CitizenUser, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: + 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 - temp_auth_token (str, optional): Temporary auth token to use for this request 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( - action="POST", - msg_id="create-citizen-user", - temp_auth_token=temp_auth_token - ) + request_info = RequestConfig.get_request_info() payload = { "RequestInfo": request_info.to_dict(), @@ -38,25 +33,20 @@ def create_citizen(self, citizen_user: CitizenUser, request_info: Optional[Reque 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, temp_auth_token: Optional[str] = None) -> Dict: + 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 - temp_auth_token (str, optional): Access token of the user. If not provided, uses auth token from RequestInfo 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( - action="POST", - msg_id="get-user-details", - temp_auth_token=temp_auth_token - ) + request_info = RequestConfig.get_request_info() payload = { "RequestInfo": request_info.to_dict() @@ -81,25 +71,20 @@ def get_user_details(self, tenant_id: str, request_info: Optional[RequestInfo] = endpoint = f"{self.base_url}/_details" return self.api_client.post(endpoint, json_data=payload, params=params) - def update_profile(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: + def update_profile(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None) -> Dict: """ Update user profile. Args: user_profile (UserProfileUpdate): The updated user profile data request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig - temp_auth_token (str, optional): Temporary auth token to use for this request 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( - action="POST", - msg_id="update-user-profile", - temp_auth_token=temp_auth_token - ) + request_info = RequestConfig.get_request_info() payload = { "RequestInfo": request_info.to_dict(), @@ -109,25 +94,20 @@ def update_profile(self, user_profile: UserProfileUpdate, request_info: Optional 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, temp_auth_token: Optional[str] = None) -> Dict: + 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 - temp_auth_token (str, optional): Temporary auth token to use for this request 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( - action="POST", - msg_id="search-users", - temp_auth_token=temp_auth_token - ) + request_info = RequestConfig.get_request_info() # Combine RequestInfo with search criteria at root level payload = { @@ -138,25 +118,20 @@ def search_users(self, search_criteria: UserSearchModel, request_info: Optional[ 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, temp_auth_token: Optional[str] = None) -> Dict: + 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 - temp_auth_token (str, optional): Temporary auth token to use for this request 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( - action="POST", - msg_id="create-user-no-validate", - temp_auth_token=temp_auth_token - ) + request_info = RequestConfig.get_request_info() payload = { "RequestInfo": request_info.to_dict(), @@ -166,25 +141,20 @@ def create_user_no_validate(self, citizen_user: CitizenUser, request_info: Optio endpoint = f"{self.base_url}/users/_createnovalidate" return self.api_client.post(endpoint, json_data=payload) - def update_user_no_validate(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: + def update_user_no_validate(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None) -> Dict: """ Update a user without validation in the DIGIT platform. Args: user_profile (UserProfileUpdate): The updated user profile data request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig - temp_auth_token (str, optional): Temporary auth token to use for this request 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( - action="POST", - msg_id="update-user-no-validate", - temp_auth_token=temp_auth_token - ) + request_info = RequestConfig.get_request_info() payload = { "RequestInfo": request_info.to_dict(), 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 ca033aaf5ba..f24af2f50ff 100644 --- a/accelerators/digit_client/digit_client.egg-info/SOURCES.txt +++ b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt @@ -11,14 +11,18 @@ 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/AuthorizationRequest.py digit_client/models/__init__.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_profile.py digit_client/services/__init__.py 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 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 514f0bee20f..eae395be889 100644 --- a/accelerators/digit_client/digit_client/__init__.py +++ b/accelerators/digit_client/digit_client/__init__.py @@ -6,7 +6,7 @@ from .api_client import APIClient from .config import Config -from .services import AuthenticationService, UserService, MDMSService +from .services import AuthenticationService, UserService, MDMSService, MDMSV2Service, AuthorizeService from .request_config import RequestConfig, RequestInfo, RequestInfoBuilder from .models.citizen_user import CitizenUser, Role, CitizenUserBuilder from .models.search_models import UserSearchModel @@ -22,6 +22,20 @@ MasterDetail, MasterDetailBuilder ) +from .models.mdms_v2 import ( + SchemaDefinition, + SchemaDefinitionBuilder, + SchemaDefCriteria, + SchemaDefCriteriaBuilder, + AuditDetails, + AuditDetailsBuilder, + Mdms, + MdmsBuilder, + MdmsCriteriaV2, + MdmsCriteriaV2Builder, + +) +from .models.AuthorizationRequest import AuthorizationRequest, AuthorizationRequestBuilder, Role, RoleBuilder __all__ = [ 'APIClient', @@ -29,6 +43,8 @@ 'AuthenticationService', 'UserService', 'MDMSService', + 'MDMSV2Service', + 'AuthorizeService', 'RequestConfig', 'RequestInfo', 'RequestInfoBuilder', @@ -48,5 +64,19 @@ 'ModuleDetail', 'ModuleDetailBuilder', 'MasterDetail', - 'MasterDetailBuilder' + 'MasterDetailBuilder', + 'SchemaDefinition', + 'SchemaDefinitionBuilder', + 'SchemaDefCriteria', + 'SchemaDefCriteriaBuilder', + 'AuditDetails', + 'AuditDetailsBuilder', + 'Mdms', + 'MdmsBuilder', + 'MdmsCriteriaV2', + 'MdmsCriteriaV2Builder', + 'AuthorizationRequest', + 'AuthorizationRequestBuilder', + 'Role', + 'RoleBuilder', ] \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/api_client.py b/accelerators/digit_client/digit_client/api_client.py index 24684fbbc2c..3824e7b0931 100644 --- a/accelerators/digit_client/digit_client/api_client.py +++ b/accelerators/digit_client/digit_client/api_client.py @@ -25,7 +25,8 @@ def post(self, endpoint, json_data=None, data=None, additional_headers=None, par Returns: dict: Response JSON """ - headers = {'Authorization': f'Bearer {self.auth_token}'} + # headers = {'Authorization': f'Bearer {self.auth_token}'} + headers = {'Content-Type': 'application/json'} if additional_headers: headers.update(additional_headers) print(headers) 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..f7cc48edf7f --- /dev/null +++ b/accelerators/digit_client/digit_client/models/AuthorizationRequest.py @@ -0,0 +1,117 @@ +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]: + return { + "id": self.id, + "name": self.name, + "code": self.code, + "tenantId": self.tenant_id + } + +@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..12bd52ad096 100644 --- a/accelerators/digit_client/digit_client/models/__init__.py +++ b/accelerators/digit_client/digit_client/models/__init__.py @@ -1 +1,4 @@ -# __init__.py for models package \ No newline at end of file +# __init__.py for models package +from .AuthorizationRequest import Role, RoleBuilder, AuthorizationRequest, AuthorizationRequestBuilder + +__all__ = ['Role', 'RoleBuilder', 'AuthorizationRequest', 'AuthorizationRequestBuilder'] \ 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/request_config.py b/accelerators/digit_client/digit_client/request_config.py index 80696d27ba4..f283c002b82 100644 --- a/accelerators/digit_client/digit_client/request_config.py +++ b/accelerators/digit_client/digit_client/request_config.py @@ -225,7 +225,8 @@ def initialize(cls, msg_id: Optional[str] = None, requester_id: Optional[str] = None, correlation_id: Optional[str] = None, - action: str = "") -> None: + action: str = "", + ts: Optional[int] = None) -> None: """ Initialize the default request configuration. @@ -244,15 +245,15 @@ def initialize(cls, cls._default_request_info = RequestInfo( api_id=api_id, ver=version, - ts=int(time.time() * 1000), + ts=ts, action=action, auth_token=auth_token, user_info=user_info, did=did, key=key, - msg_id=msg_id or str(uuid.uuid4()), + msg_id=msg_id, requester_id=requester_id, - correlation_id=correlation_id or str(uuid.uuid4()) + correlation_id=correlation_id ) @classmethod @@ -417,7 +418,7 @@ def update_requester_id(cls, requester_id: str) -> None: ) @classmethod - def get_request_info(cls, action: str, temp_auth_token: Optional[str] = None, **kwargs) -> RequestInfo: + def get_request_info(cls,temp_auth_token: Optional[str] = None, **kwargs) -> RequestInfo: """ Get a new RequestInfo instance with default values and specified overrides. @@ -435,10 +436,6 @@ def get_request_info(cls, action: str, temp_auth_token: Optional[str] = None, ** # Create new request info with current timestamp request_info_dict = cls._default_request_info.to_dict() request_info_dict.update({ - "ts": int(time.time() * 1000), - "action": action, - "msgId": str(uuid.uuid4()), - "correlationId": str(uuid.uuid4()), **kwargs }) diff --git a/accelerators/digit_client/digit_client/services/__init__.py b/accelerators/digit_client/digit_client/services/__init__.py index 7dff1f34cf7..b1fb2116298 100644 --- a/accelerators/digit_client/digit_client/services/__init__.py +++ b/accelerators/digit_client/digit_client/services/__init__.py @@ -2,5 +2,6 @@ from .authenticate import AuthenticationService from .user_service import UserService from .master_data_v1 import MDMSService - -__all__ = ['AuthenticationService', 'UserService', 'MDMSService'] \ No newline at end of file +from .mdms_v2 import MDMSV2Service +from .authorize import AuthorizeService +__all__ = ['AuthenticationService', 'UserService', 'MDMSService', 'MDMSV2Service', 'AuthorizeService'] \ 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 index fff81117e57..f652355f046 100644 --- a/accelerators/digit_client/digit_client/services/authenticate.py +++ b/accelerators/digit_client/digit_client/services/authenticate.py @@ -44,7 +44,10 @@ def update_password_no_login(self, request_info: Optional[RequestInfo] = 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() } @@ -52,27 +55,23 @@ def update_password_no_login(self, request_info: Optional[RequestInfo] = None) - endpoint = f"{self.base_url}/password/nologin/_update" return self.api_client.post( endpoint, - json_data=payload + json_data=payload, + additional_headers=headers ) - def logout(self, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: + 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 - temp_auth_token (Optional[str]): Temporary authentication token for the request 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( - action="POST", - msg_id="create-citizen-user", - temp_auth_token=temp_auth_token - ) + request_info = RequestConfig.get_request_info() payload = { "RequestInfo": request_info.to_dict() 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..6747dd4a39b --- /dev/null +++ b/accelerators/digit_client/digit_client/services/authorize.py @@ -0,0 +1,37 @@ +from typing import Dict, List, Optional +from ..api_client import APIClient +from ..models.AuthorizationRequest import AuthorizationRequest # New model +from ..request_config import RequestConfig, RequestInfo + +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 + ) diff --git a/accelerators/digit_client/digit_client/services/master_data_v1.py b/accelerators/digit_client/digit_client/services/master_data_v1.py index ff510164975..a20eaef5b7f 100644 --- a/accelerators/digit_client/digit_client/services/master_data_v1.py +++ b/accelerators/digit_client/digit_client/services/master_data_v1.py @@ -23,10 +23,7 @@ def search(self, """ # Get request info if not provided if request_info is None: - request_info = RequestConfig.get_request_info( - action="POST", - msg_id="5bfa85e7-dfa1-47c8-98b2-747bf552be86" - ) + request_info = RequestConfig.get_request_info() # Build complete request body payload = { @@ -63,10 +60,7 @@ def get(self, """ # Get request info if not provided if request_info is None: - request_info = RequestConfig.get_request_info( - action="POST", - msg_id="5bfa85e7-dfa1-47c8-98b2-747bf552be86" - ) + request_info = RequestConfig.get_request_info() # Build query parameters params = { 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 8e018d6e622..35b0c1fbc8f 100644 --- a/accelerators/digit_client/digit_client/services/user_service.py +++ b/accelerators/digit_client/digit_client/services/user_service.py @@ -10,25 +10,20 @@ def __init__(self, api_client: Optional[APIClient] = None): self.api_client = api_client or APIClient() self.base_url = "user" - def create_citizen(self, citizen_user: CitizenUser, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: + 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 - temp_auth_token (str, optional): Temporary auth token to use for this request 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( - action="POST", - msg_id="create-citizen-user", - temp_auth_token=temp_auth_token - ) + request_info = RequestConfig.get_request_info() payload = { "RequestInfo": request_info.to_dict(), @@ -38,25 +33,20 @@ def create_citizen(self, citizen_user: CitizenUser, request_info: Optional[Reque 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, temp_auth_token: Optional[str] = None) -> Dict: + 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 - temp_auth_token (str, optional): Access token of the user. If not provided, uses auth token from RequestInfo 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( - action="POST", - msg_id="get-user-details", - temp_auth_token=temp_auth_token - ) + request_info = RequestConfig.get_request_info() payload = { "RequestInfo": request_info.to_dict() @@ -81,25 +71,20 @@ def get_user_details(self, tenant_id: str, request_info: Optional[RequestInfo] = endpoint = f"{self.base_url}/_details" return self.api_client.post(endpoint, json_data=payload, params=params) - def update_profile(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: + def update_profile(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None) -> Dict: """ Update user profile. Args: user_profile (UserProfileUpdate): The updated user profile data request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig - temp_auth_token (str, optional): Temporary auth token to use for this request 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( - action="POST", - msg_id="update-user-profile", - temp_auth_token=temp_auth_token - ) + request_info = RequestConfig.get_request_info() payload = { "RequestInfo": request_info.to_dict(), @@ -109,25 +94,20 @@ def update_profile(self, user_profile: UserProfileUpdate, request_info: Optional 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, temp_auth_token: Optional[str] = None) -> Dict: + 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 - temp_auth_token (str, optional): Temporary auth token to use for this request 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( - action="POST", - msg_id="search-users", - temp_auth_token=temp_auth_token - ) + request_info = RequestConfig.get_request_info() # Combine RequestInfo with search criteria at root level payload = { @@ -138,25 +118,20 @@ def search_users(self, search_criteria: UserSearchModel, request_info: Optional[ 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, temp_auth_token: Optional[str] = None) -> Dict: + 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 - temp_auth_token (str, optional): Temporary auth token to use for this request 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( - action="POST", - msg_id="create-user-no-validate", - temp_auth_token=temp_auth_token - ) + request_info = RequestConfig.get_request_info() payload = { "RequestInfo": request_info.to_dict(), @@ -166,25 +141,20 @@ def create_user_no_validate(self, citizen_user: CitizenUser, request_info: Optio endpoint = f"{self.base_url}/users/_createnovalidate" return self.api_client.post(endpoint, json_data=payload) - def update_user_no_validate(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None, temp_auth_token: Optional[str] = None) -> Dict: + def update_user_no_validate(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None) -> Dict: """ Update a user without validation in the DIGIT platform. Args: user_profile (UserProfileUpdate): The updated user profile data request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig - temp_auth_token (str, optional): Temporary auth token to use for this request 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( - action="POST", - msg_id="update-user-no-validate", - temp_auth_token=temp_auth_token - ) + request_info = RequestConfig.get_request_info() payload = { "RequestInfo": request_info.to_dict(), 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 50c4e6b304ccf60bfe30d5085334fa188cc2933e..6076ae5f748e3cc827d97c81ced9b16ea2a20bd2 100644 GIT binary patch delta 14222 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 diff --git a/accelerators/digit_client/setup.py b/accelerators/digit_client/setup.py index c15bc2c76f2..26ff20d4af7 100644 --- a/accelerators/digit_client/setup.py +++ b/accelerators/digit_client/setup.py @@ -36,6 +36,7 @@ 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/use/authorize.py b/accelerators/digit_client/use/authorize.py new file mode 100644 index 00000000000..a6677662449 --- /dev/null +++ b/accelerators/digit_client/use/authorize.py @@ -0,0 +1,46 @@ +from digit_client.services import AuthorizeService +from digit_client.models import AuthorizationRequest, Role, RoleBuilder, AuthorizationRequestBuilder +from digit_client.request_config import RequestConfig, RequestInfo + +authorize_service = AuthorizeService() +RequestConfig.initialize( + api_id="asset-services", + version="1.0.0", + auth_token="a0cf23e2-6027-4eed-9d19-121fd2adddec", + 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) + + + + + + + + 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) From 85958f34b17fde05541d050cd980e494dd9828c5 Mon Sep 17 00:00:00 2001 From: Priyanshu Vaish Date: Tue, 1 Apr 2025 11:15:30 +0530 Subject: [PATCH 6/7] made the access get api --- .../build/lib/digit_client/__init__.py | 6 +- .../build/lib/digit_client/models/__init__.py | 3 +- .../digit_client.egg-info/SOURCES.txt | 1 + .../digit_client/digit_client/__init__.py | 6 +- .../digit_client/models/ActionRequest.py | 352 ++++++++++++++++++ .../digit_client/models/__init__.py | 3 +- .../digit_client/services/authorize.py | 29 ++ .../dist/digit_client-0.1-py3-none-any.whl | Bin 24201 -> 26594 bytes accelerators/digit_client/use/authorize.py | 7 +- 9 files changed, 401 insertions(+), 6 deletions(-) create mode 100644 accelerators/digit_client/digit_client/models/ActionRequest.py diff --git a/accelerators/digit_client/build/lib/digit_client/__init__.py b/accelerators/digit_client/build/lib/digit_client/__init__.py index eae395be889..f1f1dfa7cff 100644 --- a/accelerators/digit_client/build/lib/digit_client/__init__.py +++ b/accelerators/digit_client/build/lib/digit_client/__init__.py @@ -36,7 +36,7 @@ ) from .models.AuthorizationRequest import AuthorizationRequest, AuthorizationRequestBuilder, Role, RoleBuilder - +from .models.ActionRequest import ActionRequest, ActionBuilder, ActionRequestBuilder, Action __all__ = [ 'APIClient', 'Config', @@ -79,4 +79,8 @@ 'AuthorizationRequestBuilder', 'Role', 'RoleBuilder', + 'ActionRequest', + 'ActionBuilder', + 'ActionRequestBuilder', + 'Action' ] \ 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 12bd52ad096..24a6454288f 100644 --- a/accelerators/digit_client/build/lib/digit_client/models/__init__.py +++ b/accelerators/digit_client/build/lib/digit_client/models/__init__.py @@ -1,4 +1,5 @@ # __init__.py for models package from .AuthorizationRequest import Role, RoleBuilder, AuthorizationRequest, AuthorizationRequestBuilder +from .ActionRequest import ActionRequest, ActionBuilder, ActionRequestBuilder, Action -__all__ = ['Role', 'RoleBuilder', 'AuthorizationRequest', 'AuthorizationRequestBuilder'] \ No newline at end of file +__all__ = ['Role', 'RoleBuilder', 'AuthorizationRequest', 'AuthorizationRequestBuilder', 'ActionRequest', 'ActionBuilder', 'ActionRequestBuilder', 'Action'] \ No newline at end of file diff --git a/accelerators/digit_client/digit_client.egg-info/SOURCES.txt b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt index f24af2f50ff..cce0c74825e 100644 --- a/accelerators/digit_client/digit_client.egg-info/SOURCES.txt +++ b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt @@ -11,6 +11,7 @@ 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/__init__.py digit_client/models/auth.py diff --git a/accelerators/digit_client/digit_client/__init__.py b/accelerators/digit_client/digit_client/__init__.py index eae395be889..f1f1dfa7cff 100644 --- a/accelerators/digit_client/digit_client/__init__.py +++ b/accelerators/digit_client/digit_client/__init__.py @@ -36,7 +36,7 @@ ) from .models.AuthorizationRequest import AuthorizationRequest, AuthorizationRequestBuilder, Role, RoleBuilder - +from .models.ActionRequest import ActionRequest, ActionBuilder, ActionRequestBuilder, Action __all__ = [ 'APIClient', 'Config', @@ -79,4 +79,8 @@ 'AuthorizationRequestBuilder', 'Role', 'RoleBuilder', + 'ActionRequest', + 'ActionBuilder', + 'ActionRequestBuilder', + 'Action' ] \ 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/__init__.py b/accelerators/digit_client/digit_client/models/__init__.py index 12bd52ad096..24a6454288f 100644 --- a/accelerators/digit_client/digit_client/models/__init__.py +++ b/accelerators/digit_client/digit_client/models/__init__.py @@ -1,4 +1,5 @@ # __init__.py for models package from .AuthorizationRequest import Role, RoleBuilder, AuthorizationRequest, AuthorizationRequestBuilder +from .ActionRequest import ActionRequest, ActionBuilder, ActionRequestBuilder, Action -__all__ = ['Role', 'RoleBuilder', 'AuthorizationRequest', 'AuthorizationRequestBuilder'] \ No newline at end of file +__all__ = ['Role', 'RoleBuilder', 'AuthorizationRequest', 'AuthorizationRequestBuilder', 'ActionRequest', 'ActionBuilder', 'ActionRequestBuilder', 'Action'] \ 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 index 6747dd4a39b..45136ea4acf 100644 --- a/accelerators/digit_client/digit_client/services/authorize.py +++ b/accelerators/digit_client/digit_client/services/authorize.py @@ -2,6 +2,7 @@ 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): @@ -35,3 +36,31 @@ def authorize_action(self, 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/dist/digit_client-0.1-py3-none-any.whl b/accelerators/digit_client/dist/digit_client-0.1-py3-none-any.whl index 6076ae5f748e3cc827d97c81ced9b16ea2a20bd2..4eabc9864861566752f2771344bae2d539377134 100644 GIT binary patch delta 8917 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 GIQ|E>4w<3= diff --git a/accelerators/digit_client/use/authorize.py b/accelerators/digit_client/use/authorize.py index a6677662449..3dabd81d307 100644 --- a/accelerators/digit_client/use/authorize.py +++ b/accelerators/digit_client/use/authorize.py @@ -1,12 +1,13 @@ from digit_client.services import AuthorizeService -from digit_client.models import AuthorizationRequest, Role, RoleBuilder, AuthorizationRequestBuilder +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="a0cf23e2-6027-4eed-9d19-121fd2adddec", + auth_token="0e9b955f-5e25-4809-b680-97ef37ccf53f", msg_id="authorize_action", user_info={ "id": "1", @@ -37,6 +38,8 @@ response = authorize_service.authorize_action(authorization_request) print(response) +print(authorize_service.get_mdms_action(ActionRequestBuilder().with_tenant_id("LMN").with_role_codes(["CITIZEN"]).with_actions([ActionBuilder().with_parent_module("TradeLicense").with_service_code("ASSET_SERVICE").build()]).build())) + From bed38c499ef37a7ac29936f84fdf52fa0017ead3 Mon Sep 17 00:00:00 2001 From: Priyanshu Vaish Date: Tue, 1 Apr 2025 16:44:50 +0530 Subject: [PATCH 7/7] implemented workflow and escalate --- .../build/lib/digit_client/__init__.py | 37 +- .../build/lib/digit_client/models/__init__.py | 63 +- .../lib/digit_client/models/search_models.py | 41 +- .../build/lib/digit_client/models/user.py | 117 ++- .../lib/digit_client/services/__init__.py | 3 +- .../lib/digit_client/services/user_service.py | 10 +- .../digit_client.egg-info/SOURCES.txt | 5 +- .../digit_client/digit_client/__init__.py | 37 +- .../models/AuthorizationRequest.py | 22 +- .../digit_client/models/__init__.py | 63 +- .../digit_client/models/search_models.py | 41 +- .../digit_client/digit_client/models/user.py | 113 +++ .../digit_client/models/user_profile.py | 167 ---- .../digit_client/models/workflow.py | 796 ++++++++++++++++++ .../digit_client/services/__init__.py | 3 +- .../digit_client/services/user_service.py | 10 +- .../digit_client/services/workflow.py | 220 +++++ .../dist/digit_client-0.1-py3-none-any.whl | Bin 26594 -> 34767 bytes accelerators/digit_client/use/authorize.py | 2 +- accelerators/digit_client/use/mdms_v1.py | 15 +- accelerators/digit_client/use/workflow_v2.py | 229 +++++ 21 files changed, 1770 insertions(+), 224 deletions(-) create mode 100644 accelerators/digit_client/digit_client/models/user.py delete mode 100644 accelerators/digit_client/digit_client/models/user_profile.py create mode 100644 accelerators/digit_client/digit_client/models/workflow.py create mode 100644 accelerators/digit_client/digit_client/services/workflow.py create mode 100644 accelerators/digit_client/use/workflow_v2.py diff --git a/accelerators/digit_client/build/lib/digit_client/__init__.py b/accelerators/digit_client/build/lib/digit_client/__init__.py index f1f1dfa7cff..4328e62888a 100644 --- a/accelerators/digit_client/build/lib/digit_client/__init__.py +++ b/accelerators/digit_client/build/lib/digit_client/__init__.py @@ -6,11 +6,11 @@ from .api_client import APIClient from .config import Config -from .services import AuthenticationService, UserService, MDMSService, MDMSV2Service, AuthorizeService +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 -from .models.user_profile import UserProfileUpdate, UserRole,UserProfileUpdateBuilder +from .models.search_models import UserSearchModel, UserSearchModelBuilder +from .models.user import User,UserBuilder from .models.auth import AuthenticationRequest, AuthenticationRequestBuilder from .models.mdms import ( MdmsCriteriaReq, @@ -33,7 +33,18 @@ 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 @@ -45,15 +56,16 @@ 'MDMSService', 'MDMSV2Service', 'AuthorizeService', + 'WorkflowV2Service', 'RequestConfig', 'RequestInfo', 'RequestInfoBuilder', 'CitizenUser', 'Role', 'UserSearchModel', - 'UserProfileUpdate', - 'UserRole', - 'UserProfileUpdateBuilder', + 'UserSearchModelBuilder', + 'User', + 'UserBuilder', 'CitizenUserBuilder', 'AuthenticationRequest', 'AuthenticationRequestBuilder', @@ -82,5 +94,14 @@ 'ActionRequest', 'ActionBuilder', 'ActionRequestBuilder', - 'Action' + '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/models/__init__.py b/accelerators/digit_client/build/lib/digit_client/models/__init__.py index 24a6454288f..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,5 +1,66 @@ # __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'] \ No newline at end of file + +__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/search_models.py b/accelerators/digit_client/build/lib/digit_client/models/search_models.py index 43686e273b9..592641f58da 100644 --- a/accelerators/digit_client/build/lib/digit_client/models/search_models.py +++ b/accelerators/digit_client/build/lib/digit_client/models/search_models.py @@ -28,4 +28,43 @@ def to_dict(self) -> dict: if self.userName: search_dict["userName"] = self.userName - return search_dict \ No newline at end of file + 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/services/__init__.py b/accelerators/digit_client/build/lib/digit_client/services/__init__.py index b1fb2116298..37864849c20 100644 --- a/accelerators/digit_client/build/lib/digit_client/services/__init__.py +++ b/accelerators/digit_client/build/lib/digit_client/services/__init__.py @@ -4,4 +4,5 @@ from .master_data_v1 import MDMSService from .mdms_v2 import MDMSV2Service from .authorize import AuthorizeService -__all__ = ['AuthenticationService', 'UserService', 'MDMSService', 'MDMSV2Service', 'AuthorizeService'] \ No newline at end of file +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/user_service.py b/accelerators/digit_client/build/lib/digit_client/services/user_service.py index 35b0c1fbc8f..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 @@ -2,7 +2,7 @@ from ..api_client import APIClient from ..models.citizen_user import CitizenUser from ..models.search_models import UserSearchModel -from ..models.user_profile import UserProfileUpdate +from ..models.user import User from ..request_config import RequestConfig, RequestInfo class UserService: @@ -71,12 +71,12 @@ def get_user_details(self, tenant_id: str, request_info: Optional[RequestInfo] = endpoint = f"{self.base_url}/_details" return self.api_client.post(endpoint, json_data=payload, params=params) - def update_profile(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None) -> Dict: + def update_profile(self, user_profile: User, request_info: Optional[RequestInfo] = None) -> Dict: """ Update user profile. Args: - user_profile (UserProfileUpdate): The updated user profile data + user_profile (User): The updated user profile data request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig Returns: @@ -141,12 +141,12 @@ def create_user_no_validate(self, citizen_user: CitizenUser, request_info: Optio endpoint = f"{self.base_url}/users/_createnovalidate" return self.api_client.post(endpoint, json_data=payload) - def update_user_no_validate(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None) -> Dict: + 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 (UserProfileUpdate): The updated user profile data + user_profile (User): The updated user profile data request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig Returns: diff --git a/accelerators/digit_client/digit_client.egg-info/SOURCES.txt b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt index cce0c74825e..3d1c0fcc3eb 100644 --- a/accelerators/digit_client/digit_client.egg-info/SOURCES.txt +++ b/accelerators/digit_client/digit_client.egg-info/SOURCES.txt @@ -13,17 +13,20 @@ 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/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_profile.py +digit_client/models/user.py +digit_client/models/workflow.py digit_client/services/__init__.py 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/__init__.py b/accelerators/digit_client/digit_client/__init__.py index f1f1dfa7cff..4328e62888a 100644 --- a/accelerators/digit_client/digit_client/__init__.py +++ b/accelerators/digit_client/digit_client/__init__.py @@ -6,11 +6,11 @@ from .api_client import APIClient from .config import Config -from .services import AuthenticationService, UserService, MDMSService, MDMSV2Service, AuthorizeService +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 -from .models.user_profile import UserProfileUpdate, UserRole,UserProfileUpdateBuilder +from .models.search_models import UserSearchModel, UserSearchModelBuilder +from .models.user import User,UserBuilder from .models.auth import AuthenticationRequest, AuthenticationRequestBuilder from .models.mdms import ( MdmsCriteriaReq, @@ -33,7 +33,18 @@ 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 @@ -45,15 +56,16 @@ 'MDMSService', 'MDMSV2Service', 'AuthorizeService', + 'WorkflowV2Service', 'RequestConfig', 'RequestInfo', 'RequestInfoBuilder', 'CitizenUser', 'Role', 'UserSearchModel', - 'UserProfileUpdate', - 'UserRole', - 'UserProfileUpdateBuilder', + 'UserSearchModelBuilder', + 'User', + 'UserBuilder', 'CitizenUserBuilder', 'AuthenticationRequest', 'AuthenticationRequestBuilder', @@ -82,5 +94,14 @@ 'ActionRequest', 'ActionBuilder', 'ActionRequestBuilder', - 'Action' + 'WorkflowAction', + 'WorkflowActionBuilder', + 'Document', + 'DocumentBuilder', + 'State', + 'StateBuilder', + 'ProcessInstance', + 'ProcessInstanceBuilder', + 'ProcessInstanceSearchCriteria', + 'ProcessInstanceSearchCriteriaBuilder', ] \ No newline at end of file diff --git a/accelerators/digit_client/digit_client/models/AuthorizationRequest.py b/accelerators/digit_client/digit_client/models/AuthorizationRequest.py index f7cc48edf7f..7b36561c0a1 100644 --- a/accelerators/digit_client/digit_client/models/AuthorizationRequest.py +++ b/accelerators/digit_client/digit_client/models/AuthorizationRequest.py @@ -20,12 +20,22 @@ def __post_init__(self): raise ValueError("tenant_id must be at most 50 characters") def to_dict(self) -> Dict[str, Any]: - return { - "id": self.id, - "name": self.name, - "code": self.code, - "tenantId": self.tenant_id - } + 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: diff --git a/accelerators/digit_client/digit_client/models/__init__.py b/accelerators/digit_client/digit_client/models/__init__.py index 24a6454288f..06c33192c2c 100644 --- a/accelerators/digit_client/digit_client/models/__init__.py +++ b/accelerators/digit_client/digit_client/models/__init__.py @@ -1,5 +1,66 @@ # __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'] \ No newline at end of file + +__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/search_models.py b/accelerators/digit_client/digit_client/models/search_models.py index 43686e273b9..592641f58da 100644 --- a/accelerators/digit_client/digit_client/models/search_models.py +++ b/accelerators/digit_client/digit_client/models/search_models.py @@ -28,4 +28,43 @@ def to_dict(self) -> dict: if self.userName: search_dict["userName"] = self.userName - return search_dict \ No newline at end of file + 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/user.py b/accelerators/digit_client/digit_client/models/user.py new file mode 100644 index 00000000000..646bfb038c7 --- /dev/null +++ b/accelerators/digit_client/digit_client/models/user.py @@ -0,0 +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: + 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) -> dict: + return { + "id": self.id, + "uuid": self.uuid, + "userName": self.user_name, + "name": self.name, + "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/user_profile.py b/accelerators/digit_client/digit_client/models/user_profile.py deleted file mode 100644 index 88b2207e952..00000000000 --- a/accelerators/digit_client/digit_client/models/user_profile.py +++ /dev/null @@ -1,167 +0,0 @@ -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/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/services/__init__.py b/accelerators/digit_client/digit_client/services/__init__.py index b1fb2116298..37864849c20 100644 --- a/accelerators/digit_client/digit_client/services/__init__.py +++ b/accelerators/digit_client/digit_client/services/__init__.py @@ -4,4 +4,5 @@ from .master_data_v1 import MDMSService from .mdms_v2 import MDMSV2Service from .authorize import AuthorizeService -__all__ = ['AuthenticationService', 'UserService', 'MDMSService', 'MDMSV2Service', 'AuthorizeService'] \ No newline at end of file +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/user_service.py b/accelerators/digit_client/digit_client/services/user_service.py index 35b0c1fbc8f..73f86eb0770 100644 --- a/accelerators/digit_client/digit_client/services/user_service.py +++ b/accelerators/digit_client/digit_client/services/user_service.py @@ -2,7 +2,7 @@ from ..api_client import APIClient from ..models.citizen_user import CitizenUser from ..models.search_models import UserSearchModel -from ..models.user_profile import UserProfileUpdate +from ..models.user import User from ..request_config import RequestConfig, RequestInfo class UserService: @@ -71,12 +71,12 @@ def get_user_details(self, tenant_id: str, request_info: Optional[RequestInfo] = endpoint = f"{self.base_url}/_details" return self.api_client.post(endpoint, json_data=payload, params=params) - def update_profile(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None) -> Dict: + def update_profile(self, user_profile: User, request_info: Optional[RequestInfo] = None) -> Dict: """ Update user profile. Args: - user_profile (UserProfileUpdate): The updated user profile data + user_profile (User): The updated user profile data request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig Returns: @@ -141,12 +141,12 @@ def create_user_no_validate(self, citizen_user: CitizenUser, request_info: Optio endpoint = f"{self.base_url}/users/_createnovalidate" return self.api_client.post(endpoint, json_data=payload) - def update_user_no_validate(self, user_profile: UserProfileUpdate, request_info: Optional[RequestInfo] = None) -> Dict: + 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 (UserProfileUpdate): The updated user profile data + user_profile (User): The updated user profile data request_info (RequestInfo, optional): Custom RequestInfo to use. If not provided, uses global RequestConfig Returns: 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 4eabc9864861566752f2771344bae2d539377134..591c2ce6b600a004b4f6e47abc8a805ca114506a 100644 GIT binary patch delta 13920 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 diff --git a/accelerators/digit_client/use/authorize.py b/accelerators/digit_client/use/authorize.py index 3dabd81d307..daf5ff33f00 100644 --- a/accelerators/digit_client/use/authorize.py +++ b/accelerators/digit_client/use/authorize.py @@ -38,7 +38,7 @@ response = authorize_service.authorize_action(authorization_request) print(response) -print(authorize_service.get_mdms_action(ActionRequestBuilder().with_tenant_id("LMN").with_role_codes(["CITIZEN"]).with_actions([ActionBuilder().with_parent_module("TradeLicense").with_service_code("ASSET_SERVICE").build()]).build())) +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 index fd7aa500a4c..8c7684103a6 100644 --- a/accelerators/digit_client/use/mdms_v1.py +++ b/accelerators/digit_client/use/mdms_v1.py @@ -6,43 +6,40 @@ MdmsCriteriaBuilder ) from digit_client.request_config import RequestConfig, RequestInfo -from digit_client.models.user_profile import UserRole, UserProfileUpdateBuilder +from digit_client.models import Role, UserBuilder def example_mdms_search(): # Initialize the MDMS service mdms_service = MDMSService() roles = [ - UserRole( + Role( name="Employee", code="EMPLOYEE", tenant_id="LMN" ), - UserRole( + Role( name="System user", code="SYSTEM", tenant_id="LMN" ), - UserRole( + Role( name="Super User", code="SUPERUSER", tenant_id="LMN" ) ] - auth_token="3a9dc53a-169a-4cfc-90bb-7a290f3fa9e4" - user_info = UserProfileUpdateBuilder()\ + 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_locale("string")\ .with_type("EMPLOYEE")\ .with_name("mustak")\ .with_mobile_number("1234567890")\ .with_email("xyz@egovernments.org")\ .with_roles(roles)\ - .with_active(True)\ .with_tenant_id("LMN")\ - .with_permanent_city("Kaikoo")\ .build() # Initialize RequestConfig with user info RequestConfig.initialize( 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()