diff --git a/client-code-gen/admin-management-openapi.json b/client-code-gen/admin-management-openapi.json index 6b2204e0a..343a76d02 100644 --- a/client-code-gen/admin-management-openapi.json +++ b/client-code-gen/admin-management-openapi.json @@ -1 +1,1189 @@ -{"openapi":"3.0.3","info":{"title":"Forest Access Management - FAM - Admin Management API","description":"\nForest Access Management Admin Management API used by the Forest Access Management application\nto define admin access to forest applications.\n","contact":{"name":"Team Heartwood","url":"https://apps.nrs.gov.bc.ca/int/confluence/display/FSAST1/Team+Heartwood","email":"SIBIFSAF@victoria1.gov.bc.ca"},"license":{"name":"Apache 2.0","url":"https://www.apache.org/licenses/LICENSE-2.0.html"},"version":"0.0.1"},"paths":{"/smoke_test":{"get":{"tags":["Smoke Test"],"summary":"Smoke Test","description":"List of different applications that are administered by FAM","operationId":"smoke_test","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"6jfveou69mgford233or30hmta":[]}]}},"/application_admins":{"get":{"tags":["FAM Application Admin"],"summary":"Get Application Admins","operationId":"get_application_admins","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FamAppAdminGetResponse"},"type":"array","title":"Response Get Application Admins"}}}}},"security":[{"6jfveou69mgford233or30hmta":[]}]},"post":{"tags":["FAM Application Admin"],"summary":"Create Application Admin","operationId":"create_application_admin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FamAppAdminCreateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FamAppAdminGetResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"6jfveou69mgford233or30hmta":[]}]}},"/application_admins/{application_admin_id}":{"delete":{"tags":["FAM Application Admin"],"summary":"Delete Application Admin","operationId":"delete_application_admin","security":[{"6jfveou69mgford233or30hmta":[]}],"parameters":[{"name":"application_admin_id","in":"path","required":true,"schema":{"type":"integer","title":"Application Admin Id"}}],"responses":{"200":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/access_control_privileges":{"post":{"tags":["FAM Access Control Privileges"],"summary":"Create Access Control Privilege Many","description":"Grant Delegated Admin Privileges","operationId":"create_access_control_privilege_many","security":[{"6jfveou69mgford233or30hmta":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FamAccessControlPrivilegeCreateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FamAccessControlPrivilegeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["FAM Access Control Privileges"],"summary":"Get Access Control Privileges By Application Id","description":"Get Delegated Admin Privileges For an Application","operationId":"get_access_control_privileges_by_application_id","security":[{"6jfveou69mgford233or30hmta":[]}],"parameters":[{"name":"application_id","in":"query","required":true,"schema":{"type":"integer","title":"Application Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FamAccessControlPrivilegeGetResponse"},"title":"Response Get Access Control Privileges By Application Id"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/access_control_privileges/{access_control_privilege_id}":{"delete":{"tags":["FAM Access Control Privileges"],"summary":"Delete Access Control Privilege","operationId":"delete_access_control_privilege","security":[{"6jfveou69mgford233or30hmta":[]}],"parameters":[{"name":"access_control_privilege_id","in":"path","required":true,"schema":{"type":"integer","title":"Access Control Privilege Id"}}],"responses":{"200":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin-user-accesses":{"get":{"tags":["Admin User Accesses"],"summary":"Admin User Access Privilege","description":"Access privilege for logged on admin user for what applications/roles(scoped) the user can grant.","operationId":"Admin user access privilege","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUserAccessResponse"}}}}},"security":[{"6jfveou69mgford233or30hmta":[]}]}}},"components":{"schemas":{"AdminRoleAuthGroup":{"type":"string","enum":["FAM_ADMIN","APP_ADMIN","DELEGATED_ADMIN"],"title":"AdminRoleAuthGroup","description":"FAM data model does not explicitly have these role group of admins.\nHowever, business rules do differentiate purpose of admins as:\n (FAM_ADMIN, [APP]_ADMIN, DELEGATED_ADMIN)\n# Referencing to FAM confluence for design:\n https://apps.nrs.gov.bc.ca/int/confluence/display/FSAST1/Delegated+Access+Administration+Design (Auth Function)"},"AdminUserAccessResponse":{"properties":{"access":{"items":{"$ref":"#/components/schemas/FamAuthGrantDto"},"type":"array","title":"Access"}},"type":"object","required":["access"],"title":"AdminUserAccessResponse"},"AppEnv":{"type":"string","enum":["DEV","TEST","PROD"],"title":"AppEnv"},"EmailSendingStatus":{"type":"string","enum":["NOT_REQUIRED","SENT_TO_EMAIL_SERVICE_SUCCESS","SENT_TO_EMAIL_SERVICE_FAILURE"],"title":"EmailSendingStatus"},"FamAccessControlPrivilegeCreateRequest":{"properties":{"user_name":{"type":"string","maxLength":20,"minLength":3,"title":"User Name"},"user_guid":{"type":"string","maxLength":32,"minLength":32,"title":"User Guid"},"user_type_code":{"$ref":"#/components/schemas/UserType"},"role_id":{"type":"integer","title":"Role Id"},"forest_client_numbers":{"anyOf":[{"items":{"type":"string","maxLength":8,"minLength":1},"type":"array"},{"type":"null"}],"title":"Forest Client Numbers"},"requires_send_user_email":{"type":"boolean","title":"Requires Send User Email","default":false}},"type":"object","required":["user_name","user_guid","user_type_code","role_id"],"title":"FamAccessControlPrivilegeCreateRequest","description":"This is used at router level, the data we receive from frontend.\nUse username and user_type_code to get user_id,\nand for concrete role, can use its role_id directly,\nbut for abstract role, need to create/get child role_id based on the forest client number,\nand then use schema FamAccessControlPrivilegeCreateDto to insert into the database"},"FamAccessControlPrivilegeCreateResponse":{"properties":{"status_code":{"type":"integer","title":"Status Code"},"detail":{"$ref":"#/components/schemas/FamAccessControlPrivilegeGetResponse"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"}},"type":"object","required":["status_code","detail"],"title":"FamAccessControlPrivilegeCreateResponse"},"FamAccessControlPrivilegeGetResponse":{"properties":{"access_control_privilege_id":{"type":"integer","title":"Access Control Privilege Id"},"user_id":{"type":"integer","title":"User Id"},"role_id":{"type":"integer","title":"Role Id"},"user":{"$ref":"#/components/schemas/FamUserInfoDto"},"role":{"$ref":"#/components/schemas/FamRoleWithClientDto"}},"type":"object","required":["access_control_privilege_id","user_id","role_id","user","role"],"title":"FamAccessControlPrivilegeGetResponse"},"FamAccessControlPrivilegeResponse":{"properties":{"email_sending_status":{"allOf":[{"$ref":"#/components/schemas/EmailSendingStatus"}],"default":"NOT_REQUIRED"},"assignments_detail":{"items":{"$ref":"#/components/schemas/FamAccessControlPrivilegeCreateResponse"},"type":"array","title":"Assignments Detail"}},"type":"object","required":["assignments_detail"],"title":"FamAccessControlPrivilegeResponse"},"FamAppAdminCreateRequest":{"properties":{"user_name":{"type":"string","maxLength":20,"minLength":3,"title":"User Name"},"user_guid":{"type":"string","maxLength":32,"minLength":32,"title":"User Guid"},"user_type_code":{"$ref":"#/components/schemas/UserType"},"application_id":{"type":"integer","title":"Application Id"}},"type":"object","required":["user_name","user_guid","user_type_code","application_id"],"title":"FamAppAdminCreateRequest"},"FamAppAdminGetResponse":{"properties":{"application_admin_id":{"type":"integer","title":"Application Admin Id"},"user_id":{"type":"integer","title":"User Id"},"application_id":{"type":"integer","title":"Application Id"},"user":{"$ref":"#/components/schemas/FamUserInfoDto"},"application":{"$ref":"#/components/schemas/FamApplicationBase"}},"type":"object","required":["application_admin_id","user_id","application_id","user","application"],"title":"FamAppAdminGetResponse"},"FamApplicationBase":{"properties":{"application_name":{"type":"string","maxLength":100,"title":"Application Name"},"application_description":{"type":"string","maxLength":200,"title":"Application Description"},"app_environment":{"anyOf":[{"$ref":"#/components/schemas/AppEnv"},{"type":"null"}]}},"type":"object","required":["application_name","application_description"],"title":"FamApplicationBase"},"FamApplicationDto":{"properties":{"id":{"type":"integer","title":"Id"},"name":{"type":"string","maxLength":100,"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Description"},"env":{"anyOf":[{"$ref":"#/components/schemas/AppEnv"},{"type":"null"}]}},"type":"object","required":["id","name"],"title":"FamApplicationDto"},"FamAuthGrantDto":{"properties":{"auth_key":{"$ref":"#/components/schemas/AdminRoleAuthGroup"},"grants":{"items":{"$ref":"#/components/schemas/FamGrantDetailDto"},"type":"array","title":"Grants"}},"type":"object","required":["auth_key","grants"],"title":"FamAuthGrantDto"},"FamForestClientBase":{"properties":{"forest_client_number":{"type":"string","maxLength":8,"title":"Forest Client Number"}},"type":"object","required":["forest_client_number"],"title":"FamForestClientBase"},"FamGrantDetailDto":{"properties":{"application":{"$ref":"#/components/schemas/FamApplicationDto"},"roles":{"anyOf":[{"items":{"$ref":"#/components/schemas/FamRoleDto"},"type":"array"},{"type":"null"}],"title":"Roles"}},"type":"object","required":["application"],"title":"FamGrantDetailDto"},"FamRoleBase":{"properties":{"role_name":{"type":"string","maxLength":100,"title":"Role Name"},"role_type_code":{"$ref":"#/components/schemas/RoleType"}},"type":"object","required":["role_name","role_type_code"],"title":"FamRoleBase"},"FamRoleDto":{"properties":{"id":{"type":"integer","title":"Id"},"name":{"type":"string","maxLength":100,"title":"Name"},"description":{"type":"string","maxLength":300,"title":"Description"},"type_code":{"$ref":"#/components/schemas/RoleType"},"forest_clients":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Forest Clients"}},"type":"object","required":["id","name","description","type_code"],"title":"FamRoleDto"},"FamRoleWithClientDto":{"properties":{"role_id":{"type":"integer","title":"Role Id"},"role_name":{"type":"string","maxLength":100,"title":"Role Name"},"client_number":{"anyOf":[{"$ref":"#/components/schemas/FamForestClientBase"},{"type":"null"}]},"parent_role":{"anyOf":[{"$ref":"#/components/schemas/FamRoleBase"},{"type":"null"}]},"application":{"$ref":"#/components/schemas/FamApplicationBase"}},"type":"object","required":["role_id","role_name","application"],"title":"FamRoleWithClientDto"},"FamUserInfoDto":{"properties":{"user_name":{"type":"string","maxLength":20,"title":"User Name"},"user_type":{"$ref":"#/components/schemas/FamUserTypeDto"},"first_name":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"First Name"},"last_name":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Last Name"},"email":{"anyOf":[{"type":"string","maxLength":250},{"type":"null"}],"title":"Email"}},"type":"object","required":["user_name","user_type"],"title":"FamUserInfoDto"},"FamUserTypeDto":{"properties":{"code":{"$ref":"#/components/schemas/UserType"},"description":{"type":"string","maxLength":35,"title":"Description"}},"type":"object","required":["code","description"],"title":"FamUserTypeDto"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"RoleType":{"type":"string","enum":["A","C"],"title":"RoleType"},"UserType":{"type":"string","enum":["I","B"],"title":"UserType"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"6jfveou69mgford233or30hmta":{"type":"oauth2","flows":{"authorizationCode":{"scopes":{},"authorizationUrl":"https://dev-fam-user-pool-domain.auth.ca-central-1.amazoncognito.com/authorize","tokenUrl":"https://dev-fam-user-pool-domain.auth.ca-central-1.amazoncognito.com/token"}}}}}} \ No newline at end of file +{ + "openapi": "3.0.3", + "info": { + "title": "Forest Access Management - FAM - Admin Management API", + "description": "\nForest Access Management Admin Management API used by the Forest Access Management application\nto define admin access to forest applications.\n", + "contact": { + "name": "Team Heartwood", + "url": "https://apps.nrs.gov.bc.ca/int/confluence/display/FSAST1/Team+Heartwood", + "email": "SIBIFSAF@victoria1.gov.bc.ca" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "0.0.1" + }, + "paths": { + "/smoke_test": { + "get": { + "tags": [ + "Smoke Test" + ], + "summary": "Smoke Test", + "description": "List of different applications that are administered by FAM", + "operationId": "smoke_test", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + + } + } + } + } + }, + "security": [ + { + "6jfveou69mgford233or30hmta": [] + } + ] + } + }, + "/application_admins": { + "get": { + "tags": [ + "FAM Application Admin" + ], + "summary": "Get Application Admins", + "operationId": "get_application_admins", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/FamAppAdminGetResponse" + }, + "type": "array", + "title": "Response Get Application Admins" + } + } + } + } + }, + "security": [ + { + "6jfveou69mgford233or30hmta": [] + } + ] + }, + "post": { + "tags": [ + "FAM Application Admin" + ], + "summary": "Create Application Admin", + "operationId": "create_application_admin", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FamAppAdminCreateRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FamAppAdminGetResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "6jfveou69mgford233or30hmta": [] + } + ] + } + }, + "/application_admins/{application_admin_id}": { + "delete": { + "tags": [ + "FAM Application Admin" + ], + "summary": "Delete Application Admin", + "operationId": "delete_application_admin", + "security": [ + { + "6jfveou69mgford233or30hmta": [] + } + ], + "parameters": [ + { + "name": "application_admin_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Application Admin Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/access-control-privileges": { + "post": { + "tags": [ + "FAM Access Control Privileges" + ], + "summary": "Create Access Control Privilege Many", + "description": "Grant Delegated Admin Privileges", + "operationId": "create_access_control_privilege_many", + "security": [ + { + "6jfveou69mgford233or30hmta": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FamAccessControlPrivilegeCreateRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FamAccessControlPrivilegeResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "get": { + "tags": [ + "FAM Access Control Privileges" + ], + "summary": "Get Access Control Privileges By Application Id", + "description": "Get 'Delegated Admin Privileges' for an application with pagination.", + "operationId": "get_access_control_privileges_by_application_id", + "security": [ + { + "6jfveou69mgford233or30hmta": [] + } + ], + "parameters": [ + { + "name": "application_id", + "in": "query", + "required": true, + "schema": { + "type": "integer", + "title": "Application Id" + } + }, + { + "name": "pageNumber", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "minimum": 1 + }, + { + "type": "null" + } + ], + "description": "Page number", + "default": 1, + "title": "Pagenumber" + }, + "description": "Page number" + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 100000, + "minimum": 10 + }, + { + "type": "null" + } + ], + "description": "Number of records per page", + "default": 50, + "title": "Pagesize" + }, + "description": "Number of records per page" + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "minLength": 3, + "maxLength": 30 + }, + { + "type": "null" + } + ], + "description": "Search by keyword", + "title": "Search" + }, + "description": "Search by keyword" + }, + { + "name": "sortOrder", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SortOrderEnum" + }, + { + "type": "null" + } + ], + "description": "Column sorting order by \u003Cbr\u003EPossible values: [asc, desc]", + "default": "desc", + "title": "Sortorder" + }, + "description": "Column sorting order by \u003Cbr\u003EPossible values: [asc, desc]" + }, + { + "name": "sortBy", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/DelegatedAdminSortByEnum" + }, + { + "type": "null" + } + ], + "description": "Column to be sorted by \u003Cbr\u003EPossible values: [create_date, user_name, user_type_code, email, full_name, role_display_name, forest_client_number]", + "default": "create_date", + "title": "Sortby" + }, + "description": "Column to be sorted by \u003Cbr\u003EPossible values: [create_date, user_name, user_type_code, email, full_name, role_display_name, forest_client_number]" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedResultsSchema_FamAccessControlPrivilegeGetResponse_" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/access-control-privileges/{access_control_privilege_id}": { + "delete": { + "tags": [ + "FAM Access Control Privileges" + ], + "summary": "Delete Access Control Privilege", + "operationId": "delete_access_control_privilege", + "security": [ + { + "6jfveou69mgford233or30hmta": [] + } + ], + "parameters": [ + { + "name": "access_control_privilege_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Access Control Privilege Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/admin-user-accesses": { + "get": { + "tags": [ + "Admin User Accesses" + ], + "summary": "Admin User Access Privilege", + "description": "Access privilege for logged on admin user for what applications/roles(scoped) the user can grant.", + "operationId": "Admin user access privilege", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdminUserAccessResponse" + } + } + } + } + }, + "security": [ + { + "6jfveou69mgford233or30hmta": [] + } + ] + } + } + }, + "components": { + "schemas": { + "AdminRoleAuthGroup": { + "type": "string", + "enum": [ + "FAM_ADMIN", + "APP_ADMIN", + "DELEGATED_ADMIN" + ], + "title": "AdminRoleAuthGroup", + "description": "FAM data model does not explicitly have these role group of admins.\nHowever, business rules do differentiate purpose of admins as:\n (FAM_ADMIN, [APP]_ADMIN, DELEGATED_ADMIN)\n# Referencing to FAM confluence for design:\n https://apps.nrs.gov.bc.ca/int/confluence/display/FSAST1/Delegated+Access+Administration+Design (Auth Function)" + }, + "AdminUserAccessResponse": { + "properties": { + "access": { + "items": { + "$ref": "#/components/schemas/FamAuthGrantDto" + }, + "type": "array", + "title": "Access" + } + }, + "type": "object", + "required": [ + "access" + ], + "title": "AdminUserAccessResponse" + }, + "AppEnv": { + "type": "string", + "enum": [ + "DEV", + "TEST", + "PROD" + ], + "title": "AppEnv" + }, + "DelegatedAdminSortByEnum": { + "type": "string", + "enum": [ + "create_date", + "user_name", + "user_type_code", + "email", + "full_name", + "role_display_name", + "forest_client_number" + ], + "title": "DelegatedAdminSortByEnum" + }, + "EmailSendingStatus": { + "type": "string", + "enum": [ + "NOT_REQUIRED", + "SENT_TO_EMAIL_SERVICE_SUCCESS", + "SENT_TO_EMAIL_SERVICE_FAILURE" + ], + "title": "EmailSendingStatus" + }, + "FamAccessControlPrivilegeCreateRequest": { + "properties": { + "user_name": { + "type": "string", + "maxLength": 20, + "minLength": 3, + "title": "User Name" + }, + "user_guid": { + "type": "string", + "maxLength": 32, + "minLength": 32, + "title": "User Guid" + }, + "user_type_code": { + "$ref": "#/components/schemas/UserType" + }, + "role_id": { + "type": "integer", + "title": "Role Id" + }, + "forest_client_numbers": { + "anyOf": [ + { + "items": { + "type": "string", + "maxLength": 8, + "minLength": 1 + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Forest Client Numbers" + }, + "requires_send_user_email": { + "type": "boolean", + "title": "Requires Send User Email", + "default": false + } + }, + "type": "object", + "required": [ + "user_name", + "user_guid", + "user_type_code", + "role_id" + ], + "title": "FamAccessControlPrivilegeCreateRequest", + "description": "This is used at router level, the data we receive from frontend.\nUse username and user_type_code to get user_id,\nand for concrete role, can use its role_id directly,\nbut for abstract role, need to create/get child role_id based on the forest client number,\nand then use schema FamAccessControlPrivilegeCreateDto to insert into the database" + }, + "FamAccessControlPrivilegeCreateResponse": { + "properties": { + "status_code": { + "type": "integer", + "title": "Status Code" + }, + "detail": { + "$ref": "#/components/schemas/FamAccessControlPrivilegeGetResponse" + }, + "error_message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error Message" + } + }, + "type": "object", + "required": [ + "status_code", + "detail" + ], + "title": "FamAccessControlPrivilegeCreateResponse" + }, + "FamAccessControlPrivilegeGetResponse": { + "properties": { + "access_control_privilege_id": { + "type": "integer", + "title": "Access Control Privilege Id" + }, + "user_id": { + "type": "integer", + "title": "User Id" + }, + "role_id": { + "type": "integer", + "title": "Role Id" + }, + "user": { + "$ref": "#/components/schemas/FamUserInfoDto" + }, + "role": { + "$ref": "#/components/schemas/FamRoleWithClientDto" + }, + "create_date": { + "type": "string", + "format": "date-time", + "title": "Create Date" + } + }, + "type": "object", + "required": [ + "access_control_privilege_id", + "user_id", + "role_id", + "user", + "role", + "create_date" + ], + "title": "FamAccessControlPrivilegeGetResponse" + }, + "FamAccessControlPrivilegeResponse": { + "properties": { + "email_sending_status": { + "allOf": [ + { + "$ref": "#/components/schemas/EmailSendingStatus" + } + ], + "default": "NOT_REQUIRED" + }, + "assignments_detail": { + "items": { + "$ref": "#/components/schemas/FamAccessControlPrivilegeCreateResponse" + }, + "type": "array", + "title": "Assignments Detail" + } + }, + "type": "object", + "required": [ + "assignments_detail" + ], + "title": "FamAccessControlPrivilegeResponse" + }, + "FamAppAdminCreateRequest": { + "properties": { + "user_name": { + "type": "string", + "maxLength": 20, + "minLength": 3, + "title": "User Name" + }, + "user_guid": { + "type": "string", + "maxLength": 32, + "minLength": 32, + "title": "User Guid" + }, + "user_type_code": { + "$ref": "#/components/schemas/UserType" + }, + "application_id": { + "type": "integer", + "title": "Application Id" + } + }, + "type": "object", + "required": [ + "user_name", + "user_guid", + "user_type_code", + "application_id" + ], + "title": "FamAppAdminCreateRequest" + }, + "FamAppAdminGetResponse": { + "properties": { + "application_admin_id": { + "type": "integer", + "title": "Application Admin Id" + }, + "user_id": { + "type": "integer", + "title": "User Id" + }, + "application_id": { + "type": "integer", + "title": "Application Id" + }, + "user": { + "$ref": "#/components/schemas/FamUserInfoDto" + }, + "application": { + "$ref": "#/components/schemas/FamApplicationBase" + } + }, + "type": "object", + "required": [ + "application_admin_id", + "user_id", + "application_id", + "user", + "application" + ], + "title": "FamAppAdminGetResponse" + }, + "FamApplicationBase": { + "properties": { + "application_id": { + "type": "integer", + "title": "Application Id" + }, + "application_name": { + "type": "string", + "maxLength": 100, + "title": "Application Name" + }, + "application_description": { + "type": "string", + "maxLength": 200, + "title": "Application Description" + }, + "app_environment": { + "anyOf": [ + { + "$ref": "#/components/schemas/AppEnv" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "required": [ + "application_id", + "application_name", + "application_description" + ], + "title": "FamApplicationBase" + }, + "FamApplicationDto": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "name": { + "type": "string", + "maxLength": 100, + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string", + "maxLength": 200 + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "env": { + "anyOf": [ + { + "$ref": "#/components/schemas/AppEnv" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name" + ], + "title": "FamApplicationDto" + }, + "FamAuthGrantDto": { + "properties": { + "auth_key": { + "$ref": "#/components/schemas/AdminRoleAuthGroup" + }, + "grants": { + "items": { + "$ref": "#/components/schemas/FamGrantDetailDto" + }, + "type": "array", + "title": "Grants" + } + }, + "type": "object", + "required": [ + "auth_key", + "grants" + ], + "title": "FamAuthGrantDto" + }, + "FamForestClientBase": { + "properties": { + "client_name": { + "anyOf": [ + { + "type": "string", + "maxLength": 60 + }, + { + "type": "null" + } + ], + "title": "Client Name" + }, + "forest_client_number": { + "type": "string", + "maxLength": 8, + "title": "Forest Client Number" + } + }, + "type": "object", + "required": [ + "forest_client_number" + ], + "title": "FamForestClientBase" + }, + "FamGrantDetailDto": { + "properties": { + "application": { + "$ref": "#/components/schemas/FamApplicationDto" + }, + "roles": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/FamRoleDto" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Roles" + } + }, + "type": "object", + "required": [ + "application" + ], + "title": "FamGrantDetailDto" + }, + "FamRoleBase": { + "properties": { + "role_name": { + "type": "string", + "maxLength": 100, + "title": "Role Name" + }, + "role_type_code": { + "$ref": "#/components/schemas/RoleType" + } + }, + "type": "object", + "required": [ + "role_name", + "role_type_code" + ], + "title": "FamRoleBase" + }, + "FamRoleDto": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "name": { + "type": "string", + "maxLength": 100, + "title": "Name" + }, + "display_name": { + "anyOf": [ + { + "type": "string", + "maxLength": 100 + }, + { + "type": "null" + } + ], + "title": "Display Name" + }, + "description": { + "anyOf": [ + { + "type": "string", + "maxLength": 300 + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "type_code": { + "$ref": "#/components/schemas/RoleType" + }, + "forest_clients": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Forest Clients" + } + }, + "type": "object", + "required": [ + "id", + "name", + "description", + "type_code" + ], + "title": "FamRoleDto" + }, + "FamRoleWithClientDto": { + "properties": { + "role_id": { + "type": "integer", + "title": "Role Id" + }, + "role_name": { + "type": "string", + "maxLength": 100, + "title": "Role Name" + }, + "display_name": { + "anyOf": [ + { + "type": "string", + "maxLength": 100 + }, + { + "type": "null" + } + ], + "title": "Display Name" + }, + "description": { + "anyOf": [ + { + "type": "string", + "maxLength": 300 + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "client_number": { + "anyOf": [ + { + "$ref": "#/components/schemas/FamForestClientBase" + }, + { + "type": "null" + } + ] + }, + "parent_role": { + "anyOf": [ + { + "$ref": "#/components/schemas/FamRoleBase" + }, + { + "type": "null" + } + ] + }, + "application": { + "$ref": "#/components/schemas/FamApplicationBase" + } + }, + "type": "object", + "required": [ + "role_id", + "role_name", + "description", + "application" + ], + "title": "FamRoleWithClientDto" + }, + "FamUserInfoDto": { + "properties": { + "user_name": { + "type": "string", + "maxLength": 20, + "title": "User Name" + }, + "user_type": { + "$ref": "#/components/schemas/FamUserTypeDto" + }, + "first_name": { + "anyOf": [ + { + "type": "string", + "maxLength": 50 + }, + { + "type": "null" + } + ], + "title": "First Name" + }, + "last_name": { + "anyOf": [ + { + "type": "string", + "maxLength": 50 + }, + { + "type": "null" + } + ], + "title": "Last Name" + }, + "email": { + "anyOf": [ + { + "type": "string", + "maxLength": 250 + }, + { + "type": "null" + } + ], + "title": "Email" + } + }, + "type": "object", + "required": [ + "user_name", + "user_type" + ], + "title": "FamUserInfoDto" + }, + "FamUserTypeDto": { + "properties": { + "code": { + "$ref": "#/components/schemas/UserType" + }, + "description": { + "type": "string", + "maxLength": 35, + "title": "Description" + } + }, + "type": "object", + "required": [ + "code", + "description" + ], + "title": "FamUserTypeDto" + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "PageResultMetaSchema": { + "properties": { + "total": { + "type": "integer", + "title": "Total", + "description": "Total records counts for query conditions" + }, + "number_of_pages": { + "type": "integer", + "title": "Number Of Pages", + "description": "Total pages for query records" + }, + "page_number": { + "type": "integer", + "title": "Page Number", + "description": "Page number" + }, + "page_size": { + "type": "integer", + "title": "Page Size", + "description": "Number of records per page" + } + }, + "type": "object", + "required": [ + "total", + "number_of_pages", + "page_number", + "page_size" + ], + "title": "PageResultMetaSchema" + }, + "PagedResultsSchema_FamAccessControlPrivilegeGetResponse_": { + "properties": { + "meta": { + "$ref": "#/components/schemas/PageResultMetaSchema" + }, + "results": { + "items": { + "$ref": "#/components/schemas/FamAccessControlPrivilegeGetResponse" + }, + "type": "array", + "title": "Results", + "description": "Paged results" + } + }, + "type": "object", + "required": [ + "meta", + "results" + ], + "title": "PagedResultsSchema[FamAccessControlPrivilegeGetResponse]" + }, + "RoleType": { + "type": "string", + "enum": [ + "A", + "C" + ], + "title": "RoleType" + }, + "SortOrderEnum": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "title": "SortOrderEnum" + }, + "UserType": { + "type": "string", + "enum": [ + "I", + "B" + ], + "title": "UserType" + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + } + }, + "securitySchemes": { + "6jfveou69mgford233or30hmta": { + "type": "oauth2", + "flows": { + "authorizationCode": { + "scopes": { + + }, + "authorizationUrl": "https://dev-fam-user-pool-domain.auth.ca-central-1.amazoncognito.com/authorize", + "tokenUrl": "https://dev-fam-user-pool-domain.auth.ca-central-1.amazoncognito.com/token" + } + } + } + } + } +} \ No newline at end of file diff --git a/client-code-gen/gen/admin-management-api/.openapi-generator/FILES b/client-code-gen/gen/admin-management-api/.openapi-generator/FILES index 149f9209e..74c683f15 100755 --- a/client-code-gen/gen/admin-management-api/.openapi-generator/FILES +++ b/client-code-gen/gen/admin-management-api/.openapi-generator/FILES @@ -14,6 +14,7 @@ index.ts model/admin-role-auth-group.ts model/admin-user-access-response.ts model/app-env.ts +model/delegated-admin-sort-by-enum.ts model/email-sending-status.ts model/fam-access-control-privilege-create-request.ts model/fam-access-control-privilege-create-response.ts @@ -34,7 +35,10 @@ model/fam-user-type-dto.ts model/httpvalidation-error.ts model/index.ts model/location-inner.ts +model/page-result-meta-schema.ts +model/paged-results-schema-fam-access-control-privilege-get-response.ts model/role-type.ts +model/sort-order-enum.ts model/user-type.ts model/validation-error.ts package.json diff --git a/client-code-gen/gen/admin-management-api/api/famaccess-control-privileges-api.ts b/client-code-gen/gen/admin-management-api/api/famaccess-control-privileges-api.ts index b0e3246bd..deb9a00ea 100644 --- a/client-code-gen/gen/admin-management-api/api/famaccess-control-privileges-api.ts +++ b/client-code-gen/gen/admin-management-api/api/famaccess-control-privileges-api.ts @@ -22,13 +22,17 @@ import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObj // @ts-ignore import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base'; // @ts-ignore -import { FamAccessControlPrivilegeCreateRequest } from '../model'; +import { DelegatedAdminSortByEnum } from '../model'; // @ts-ignore -import { FamAccessControlPrivilegeGetResponse } from '../model'; +import { FamAccessControlPrivilegeCreateRequest } from '../model'; // @ts-ignore import { FamAccessControlPrivilegeResponse } from '../model'; // @ts-ignore import { HTTPValidationError } from '../model'; +// @ts-ignore +import { PagedResultsSchemaFamAccessControlPrivilegeGetResponse } from '../model'; +// @ts-ignore +import { SortOrderEnum } from '../model'; /** * FAMAccessControlPrivilegesApi - axios parameter creator * @export @@ -45,7 +49,7 @@ export const FAMAccessControlPrivilegesApiAxiosParamCreator = function (configur createAccessControlPrivilegeMany: async (famAccessControlPrivilegeCreateRequest: FamAccessControlPrivilegeCreateRequest, options: RawAxiosRequestConfig = {}): Promise => { // verify required parameter 'famAccessControlPrivilegeCreateRequest' is not null or undefined assertParamExists('createAccessControlPrivilegeMany', 'famAccessControlPrivilegeCreateRequest', famAccessControlPrivilegeCreateRequest) - const localVarPath = `/access_control_privileges`; + const localVarPath = `/access-control-privileges`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; @@ -85,7 +89,7 @@ export const FAMAccessControlPrivilegesApiAxiosParamCreator = function (configur deleteAccessControlPrivilege: async (accessControlPrivilegeId: number, options: RawAxiosRequestConfig = {}): Promise => { // verify required parameter 'accessControlPrivilegeId' is not null or undefined assertParamExists('deleteAccessControlPrivilege', 'accessControlPrivilegeId', accessControlPrivilegeId) - const localVarPath = `/access_control_privileges/{access_control_privilege_id}` + const localVarPath = `/access-control-privileges/{access_control_privilege_id}` .replace(`{${"access_control_privilege_id"}}`, encodeURIComponent(String(accessControlPrivilegeId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -114,16 +118,21 @@ export const FAMAccessControlPrivilegesApiAxiosParamCreator = function (configur }; }, /** - * Get Delegated Admin Privileges For an Application + * Get \'Delegated Admin Privileges\' for an application with pagination. * @summary Get Access Control Privileges By Application Id * @param {number} applicationId + * @param {number | null} [pageNumber] Page number + * @param {number | null} [pageSize] Number of records per page + * @param {string | null} [search] Search by keyword + * @param {SortOrderEnum | null} [sortOrder] Column sorting order by <br>Possible values: [asc, desc] + * @param {DelegatedAdminSortByEnum | null} [sortBy] Column to be sorted by <br>Possible values: [create_date, user_name, user_type_code, email, full_name, role_display_name, forest_client_number] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - getAccessControlPrivilegesByApplicationId: async (applicationId: number, options: RawAxiosRequestConfig = {}): Promise => { + getAccessControlPrivilegesByApplicationId: async (applicationId: number, pageNumber?: number | null, pageSize?: number | null, search?: string | null, sortOrder?: SortOrderEnum | null, sortBy?: DelegatedAdminSortByEnum | null, options: RawAxiosRequestConfig = {}): Promise => { // verify required parameter 'applicationId' is not null or undefined assertParamExists('getAccessControlPrivilegesByApplicationId', 'applicationId', applicationId) - const localVarPath = `/access_control_privileges`; + const localVarPath = `/access-control-privileges`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; @@ -143,6 +152,26 @@ export const FAMAccessControlPrivilegesApiAxiosParamCreator = function (configur localVarQueryParameter['application_id'] = applicationId; } + if (pageNumber !== undefined) { + localVarQueryParameter['pageNumber'] = pageNumber; + } + + if (pageSize !== undefined) { + localVarQueryParameter['pageSize'] = pageSize; + } + + if (search !== undefined) { + localVarQueryParameter['search'] = search; + } + + if (sortOrder !== undefined) { + localVarQueryParameter['sortOrder'] = sortOrder; + } + + if (sortBy !== undefined) { + localVarQueryParameter['sortBy'] = sortBy; + } + setSearchParams(localVarUrlObj, localVarQueryParameter); @@ -191,14 +220,19 @@ export const FAMAccessControlPrivilegesApiFp = function(configuration?: Configur return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** - * Get Delegated Admin Privileges For an Application + * Get \'Delegated Admin Privileges\' for an application with pagination. * @summary Get Access Control Privileges By Application Id * @param {number} applicationId + * @param {number | null} [pageNumber] Page number + * @param {number | null} [pageSize] Number of records per page + * @param {string | null} [search] Search by keyword + * @param {SortOrderEnum | null} [sortOrder] Column sorting order by <br>Possible values: [asc, desc] + * @param {DelegatedAdminSortByEnum | null} [sortBy] Column to be sorted by <br>Possible values: [create_date, user_name, user_type_code, email, full_name, role_display_name, forest_client_number] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async getAccessControlPrivilegesByApplicationId(applicationId: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAccessControlPrivilegesByApplicationId(applicationId, options); + async getAccessControlPrivilegesByApplicationId(applicationId: number, pageNumber?: number | null, pageSize?: number | null, search?: string | null, sortOrder?: SortOrderEnum | null, sortBy?: DelegatedAdminSortByEnum | null, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getAccessControlPrivilegesByApplicationId(applicationId, pageNumber, pageSize, search, sortOrder, sortBy, options); const localVarOperationServerIndex = configuration?.serverIndex ?? 0; const localVarOperationServerBasePath = operationServerMap['FAMAccessControlPrivilegesApi.getAccessControlPrivilegesByApplicationId']?.[localVarOperationServerIndex]?.url; return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); @@ -234,14 +268,19 @@ export const FAMAccessControlPrivilegesApiFactory = function (configuration?: Co return localVarFp.deleteAccessControlPrivilege(accessControlPrivilegeId, options).then((request) => request(axios, basePath)); }, /** - * Get Delegated Admin Privileges For an Application + * Get \'Delegated Admin Privileges\' for an application with pagination. * @summary Get Access Control Privileges By Application Id * @param {number} applicationId + * @param {number | null} [pageNumber] Page number + * @param {number | null} [pageSize] Number of records per page + * @param {string | null} [search] Search by keyword + * @param {SortOrderEnum | null} [sortOrder] Column sorting order by <br>Possible values: [asc, desc] + * @param {DelegatedAdminSortByEnum | null} [sortBy] Column to be sorted by <br>Possible values: [create_date, user_name, user_type_code, email, full_name, role_display_name, forest_client_number] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - getAccessControlPrivilegesByApplicationId(applicationId: number, options?: any): AxiosPromise> { - return localVarFp.getAccessControlPrivilegesByApplicationId(applicationId, options).then((request) => request(axios, basePath)); + getAccessControlPrivilegesByApplicationId(applicationId: number, pageNumber?: number | null, pageSize?: number | null, search?: string | null, sortOrder?: SortOrderEnum | null, sortBy?: DelegatedAdminSortByEnum | null, options?: any): AxiosPromise { + return localVarFp.getAccessControlPrivilegesByApplicationId(applicationId, pageNumber, pageSize, search, sortOrder, sortBy, options).then((request) => request(axios, basePath)); }, }; }; @@ -273,14 +312,19 @@ export interface FAMAccessControlPrivilegesApiInterface { deleteAccessControlPrivilege(accessControlPrivilegeId: number, options?: RawAxiosRequestConfig): AxiosPromise; /** - * Get Delegated Admin Privileges For an Application + * Get \'Delegated Admin Privileges\' for an application with pagination. * @summary Get Access Control Privileges By Application Id * @param {number} applicationId + * @param {number | null} [pageNumber] Page number + * @param {number | null} [pageSize] Number of records per page + * @param {string | null} [search] Search by keyword + * @param {SortOrderEnum | null} [sortOrder] Column sorting order by <br>Possible values: [asc, desc] + * @param {DelegatedAdminSortByEnum | null} [sortBy] Column to be sorted by <br>Possible values: [create_date, user_name, user_type_code, email, full_name, role_display_name, forest_client_number] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof FAMAccessControlPrivilegesApiInterface */ - getAccessControlPrivilegesByApplicationId(applicationId: number, options?: RawAxiosRequestConfig): AxiosPromise>; + getAccessControlPrivilegesByApplicationId(applicationId: number, pageNumber?: number | null, pageSize?: number | null, search?: string | null, sortOrder?: SortOrderEnum | null, sortBy?: DelegatedAdminSortByEnum | null, options?: RawAxiosRequestConfig): AxiosPromise; } @@ -316,15 +360,20 @@ export class FAMAccessControlPrivilegesApi extends BaseAPI implements FAMAccessC } /** - * Get Delegated Admin Privileges For an Application + * Get \'Delegated Admin Privileges\' for an application with pagination. * @summary Get Access Control Privileges By Application Id * @param {number} applicationId + * @param {number | null} [pageNumber] Page number + * @param {number | null} [pageSize] Number of records per page + * @param {string | null} [search] Search by keyword + * @param {SortOrderEnum | null} [sortOrder] Column sorting order by <br>Possible values: [asc, desc] + * @param {DelegatedAdminSortByEnum | null} [sortBy] Column to be sorted by <br>Possible values: [create_date, user_name, user_type_code, email, full_name, role_display_name, forest_client_number] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof FAMAccessControlPrivilegesApi */ - public getAccessControlPrivilegesByApplicationId(applicationId: number, options?: RawAxiosRequestConfig) { - return FAMAccessControlPrivilegesApiFp(this.configuration).getAccessControlPrivilegesByApplicationId(applicationId, options).then((request) => request(this.axios, this.basePath)); + public getAccessControlPrivilegesByApplicationId(applicationId: number, pageNumber?: number | null, pageSize?: number | null, search?: string | null, sortOrder?: SortOrderEnum | null, sortBy?: DelegatedAdminSortByEnum | null, options?: RawAxiosRequestConfig) { + return FAMAccessControlPrivilegesApiFp(this.configuration).getAccessControlPrivilegesByApplicationId(applicationId, pageNumber, pageSize, search, sortOrder, sortBy, options).then((request) => request(this.axios, this.basePath)); } } diff --git a/client-code-gen/gen/admin-management-api/model/delegated-admin-sort-by-enum.ts b/client-code-gen/gen/admin-management-api/model/delegated-admin-sort-by-enum.ts new file mode 100644 index 000000000..c31b83649 --- /dev/null +++ b/client-code-gen/gen/admin-management-api/model/delegated-admin-sort-by-enum.ts @@ -0,0 +1,36 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Forest Access Management - FAM - Admin Management API + * Forest Access Management Admin Management API used by the Forest Access Management application to define admin access to forest applications. + * + * The version of the OpenAPI document: 0.0.1 + * Contact: SIBIFSAF@victoria1.gov.bc.ca + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @enum {string} + */ + +export const DelegatedAdminSortByEnum = { + CreateDate: 'create_date', + UserName: 'user_name', + UserTypeCode: 'user_type_code', + Email: 'email', + FullName: 'full_name', + RoleDisplayName: 'role_display_name', + ForestClientNumber: 'forest_client_number' +} as const; + +export type DelegatedAdminSortByEnum = typeof DelegatedAdminSortByEnum[keyof typeof DelegatedAdminSortByEnum]; + + + diff --git a/client-code-gen/gen/admin-management-api/model/fam-access-control-privilege-get-response.ts b/client-code-gen/gen/admin-management-api/model/fam-access-control-privilege-get-response.ts index d3bfa71ff..2ab55a142 100644 --- a/client-code-gen/gen/admin-management-api/model/fam-access-control-privilege-get-response.ts +++ b/client-code-gen/gen/admin-management-api/model/fam-access-control-privilege-get-response.ts @@ -56,5 +56,11 @@ export interface FamAccessControlPrivilegeGetResponse { * @memberof FamAccessControlPrivilegeGetResponse */ 'role': FamRoleWithClientDto; + /** + * + * @type {string} + * @memberof FamAccessControlPrivilegeGetResponse + */ + 'create_date': string; } diff --git a/client-code-gen/gen/admin-management-api/model/fam-application-base.ts b/client-code-gen/gen/admin-management-api/model/fam-application-base.ts index 94c2b4a81..b45042039 100644 --- a/client-code-gen/gen/admin-management-api/model/fam-application-base.ts +++ b/client-code-gen/gen/admin-management-api/model/fam-application-base.ts @@ -23,6 +23,12 @@ import { AppEnv } from './app-env'; * @interface FamApplicationBase */ export interface FamApplicationBase { + /** + * + * @type {number} + * @memberof FamApplicationBase + */ + 'application_id': number; /** * * @type {string} diff --git a/client-code-gen/gen/admin-management-api/model/fam-forest-client-base.ts b/client-code-gen/gen/admin-management-api/model/fam-forest-client-base.ts index acd407fce..913465846 100644 --- a/client-code-gen/gen/admin-management-api/model/fam-forest-client-base.ts +++ b/client-code-gen/gen/admin-management-api/model/fam-forest-client-base.ts @@ -20,6 +20,12 @@ * @interface FamForestClientBase */ export interface FamForestClientBase { + /** + * + * @type {string} + * @memberof FamForestClientBase + */ + 'client_name'?: string | null; /** * * @type {string} diff --git a/client-code-gen/gen/admin-management-api/model/fam-role-dto.ts b/client-code-gen/gen/admin-management-api/model/fam-role-dto.ts index 81d1bff53..6316e7472 100644 --- a/client-code-gen/gen/admin-management-api/model/fam-role-dto.ts +++ b/client-code-gen/gen/admin-management-api/model/fam-role-dto.ts @@ -40,7 +40,13 @@ export interface FamRoleDto { * @type {string} * @memberof FamRoleDto */ - 'description': string; + 'display_name'?: string | null; + /** + * + * @type {string} + * @memberof FamRoleDto + */ + 'description': string | null; /** * * @type {RoleType} diff --git a/client-code-gen/gen/admin-management-api/model/fam-role-with-client-dto.ts b/client-code-gen/gen/admin-management-api/model/fam-role-with-client-dto.ts index d63384271..755001702 100644 --- a/client-code-gen/gen/admin-management-api/model/fam-role-with-client-dto.ts +++ b/client-code-gen/gen/admin-management-api/model/fam-role-with-client-dto.ts @@ -41,6 +41,18 @@ export interface FamRoleWithClientDto { * @memberof FamRoleWithClientDto */ 'role_name': string; + /** + * + * @type {string} + * @memberof FamRoleWithClientDto + */ + 'display_name'?: string | null; + /** + * + * @type {string} + * @memberof FamRoleWithClientDto + */ + 'description': string | null; /** * * @type {FamForestClientBase} diff --git a/client-code-gen/gen/admin-management-api/model/index.ts b/client-code-gen/gen/admin-management-api/model/index.ts index fbc6190a7..3f71bf433 100644 --- a/client-code-gen/gen/admin-management-api/model/index.ts +++ b/client-code-gen/gen/admin-management-api/model/index.ts @@ -1,6 +1,7 @@ export * from './admin-role-auth-group'; export * from './admin-user-access-response'; export * from './app-env'; +export * from './delegated-admin-sort-by-enum'; export * from './email-sending-status'; export * from './fam-access-control-privilege-create-request'; export * from './fam-access-control-privilege-create-response'; @@ -20,6 +21,9 @@ export * from './fam-user-info-dto'; export * from './fam-user-type-dto'; export * from './httpvalidation-error'; export * from './location-inner'; +export * from './page-result-meta-schema'; +export * from './paged-results-schema-fam-access-control-privilege-get-response'; export * from './role-type'; +export * from './sort-order-enum'; export * from './user-type'; export * from './validation-error'; diff --git a/client-code-gen/gen/admin-management-api/model/page-result-meta-schema.ts b/client-code-gen/gen/admin-management-api/model/page-result-meta-schema.ts new file mode 100644 index 000000000..eb02ea051 --- /dev/null +++ b/client-code-gen/gen/admin-management-api/model/page-result-meta-schema.ts @@ -0,0 +1,48 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Forest Access Management - FAM - Admin Management API + * Forest Access Management Admin Management API used by the Forest Access Management application to define admin access to forest applications. + * + * The version of the OpenAPI document: 0.0.1 + * Contact: SIBIFSAF@victoria1.gov.bc.ca + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface PageResultMetaSchema + */ +export interface PageResultMetaSchema { + /** + * Total records counts for query conditions + * @type {number} + * @memberof PageResultMetaSchema + */ + 'total': number; + /** + * Total pages for query records + * @type {number} + * @memberof PageResultMetaSchema + */ + 'number_of_pages': number; + /** + * Page number + * @type {number} + * @memberof PageResultMetaSchema + */ + 'page_number': number; + /** + * Number of records per page + * @type {number} + * @memberof PageResultMetaSchema + */ + 'page_size': number; +} + diff --git a/client-code-gen/gen/admin-management-api/model/paged-results-schema-fam-access-control-privilege-get-response.ts b/client-code-gen/gen/admin-management-api/model/paged-results-schema-fam-access-control-privilege-get-response.ts new file mode 100644 index 000000000..87ae2118f --- /dev/null +++ b/client-code-gen/gen/admin-management-api/model/paged-results-schema-fam-access-control-privilege-get-response.ts @@ -0,0 +1,42 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Forest Access Management - FAM - Admin Management API + * Forest Access Management Admin Management API used by the Forest Access Management application to define admin access to forest applications. + * + * The version of the OpenAPI document: 0.0.1 + * Contact: SIBIFSAF@victoria1.gov.bc.ca + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import { FamAccessControlPrivilegeGetResponse } from './fam-access-control-privilege-get-response'; +// May contain unused imports in some cases +// @ts-ignore +import { PageResultMetaSchema } from './page-result-meta-schema'; + +/** + * + * @export + * @interface PagedResultsSchemaFamAccessControlPrivilegeGetResponse + */ +export interface PagedResultsSchemaFamAccessControlPrivilegeGetResponse { + /** + * + * @type {PageResultMetaSchema} + * @memberof PagedResultsSchemaFamAccessControlPrivilegeGetResponse + */ + 'meta': PageResultMetaSchema; + /** + * Paged results + * @type {Array} + * @memberof PagedResultsSchemaFamAccessControlPrivilegeGetResponse + */ + 'results': Array; +} + diff --git a/client-code-gen/gen/admin-management-api/model/sort-order-enum.ts b/client-code-gen/gen/admin-management-api/model/sort-order-enum.ts new file mode 100644 index 000000000..8f260955d --- /dev/null +++ b/client-code-gen/gen/admin-management-api/model/sort-order-enum.ts @@ -0,0 +1,31 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Forest Access Management - FAM - Admin Management API + * Forest Access Management Admin Management API used by the Forest Access Management application to define admin access to forest applications. + * + * The version of the OpenAPI document: 0.0.1 + * Contact: SIBIFSAF@victoria1.gov.bc.ca + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @enum {string} + */ + +export const SortOrderEnum = { + Asc: 'asc', + Desc: 'desc' +} as const; + +export type SortOrderEnum = typeof SortOrderEnum[keyof typeof SortOrderEnum]; + + + diff --git a/client-code-gen/gen/admin-management-api/package-lock.json b/client-code-gen/gen/admin-management-api/package-lock.json index 695e3339c..3d3822dc0 100644 --- a/client-code-gen/gen/admin-management-api/package-lock.json +++ b/client-code-gen/gen/admin-management-api/package-lock.json @@ -12,9 +12,17 @@ "axios": "1.7.4" }, "devDependencies": { + "@types/node": "^12.11.5", "typescript": "^4.0" } }, + "node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true, + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", diff --git a/client-code-gen/gen/admin-management-api/package.json b/client-code-gen/gen/admin-management-api/package.json index 30e6e19a9..39eaf7a04 100755 --- a/client-code-gen/gen/admin-management-api/package.json +++ b/client-code-gen/gen/admin-management-api/package.json @@ -27,6 +27,7 @@ "axios": "1.7.4" }, "devDependencies": { + "@types/node": "^12.11.5", "typescript": "^4.0" } } diff --git a/frontend/src/services/fetchData.ts b/frontend/src/services/fetchData.ts index 73d8dd6f4..c397b5896 100644 --- a/frontend/src/services/fetchData.ts +++ b/frontend/src/services/fetchData.ts @@ -121,7 +121,7 @@ export const fetchDelegatedAdmins = async ( await AdminMgmtApiService.delegatedAdminApi.getAccessControlPrivilegesByApplicationId( applicationId ) - ).data; + ).data.results; delegatedAdmins.sort((first, second) => { const firstIsNew = isNewAccess( diff --git a/server/admin_management/api/app/constants.py b/server/admin_management/api/app/constants.py index 609d5e535..143da394a 100644 --- a/server/admin_management/api/app/constants.py +++ b/server/admin_management/api/app/constants.py @@ -1,22 +1,21 @@ from enum import Enum +from typing import TypeVar -APPLICATION_FAM = "FAM" -COGNITO_USERNAME_KEY = "username" - +# ----------------------- Generic Type Variable Declaration ----------------------- # +T = TypeVar('T') +# --------------------------------- Schema Enums --------------------------------- # class AppEnv(str, Enum): APP_ENV_TYPE_DEV = "DEV" APP_ENV_TYPE_TEST = "TEST" APP_ENV_TYPE_PROD = "PROD" - class ApiInstanceEnv(str, Enum): # Environment constant for connecting to external API (Forest Client API and IDIM Proxy API). # The integration with external API only has TEST or PROD on API instance. TEST = "TEST" PROD = "PROD" - class AwsTargetEnv(str, Enum): # "target_env" only exists on AWS (Injected from Gov AWS platform), for FAM web application. # It's lower case, Locally does not need this. @@ -25,22 +24,18 @@ class AwsTargetEnv(str, Enum): TEST = "test" PROD = "prod" - class UserType(str, Enum): IDIR = "I" BCEID = "B" - class RoleType(str, Enum): ROLE_TYPE_ABSTRACT = "A" ROLE_TYPE_CONCRETE = "C" - class FamForestClientStatusType(str, Enum): ACTIVE = "A" INACTIVE = "I" - class AdminRoleAuthGroup(str, Enum): """ FAM data model does not explicitly have these role group of admins. @@ -54,36 +49,15 @@ class AdminRoleAuthGroup(str, Enum): APP_ADMIN = "APP_ADMIN" DELEGATED_ADMIN = "DELEGATED_ADMIN" - -FOREST_CLIENT_STATUS = {"KEY": "clientStatusCode", "CODE_ACTIVE": "ACT"} - -IDIM_PROXY_ACCOUNT_TYPE_MAP = {UserType.IDIR: "Internal", UserType.BCEID: "Business"} - - class IdimSearchUserParamType(str, Enum): USER_GUID = "userGuid" USER_ID = "userId" - class EmailSendingStatus(str, Enum): NOT_REQUIRED = "NOT_REQUIRED" # does not require sending email. SENT_TO_EMAIL_SERVICE_SUCCESS = "SENT_TO_EMAIL_SERVICE_SUCCESS" # send to external service successful. SENT_TO_EMAIL_SERVICE_FAILURE = "SENT_TO_EMAIL_SERVICE_FAILURE" # technical/validation failure during sending to external service. - -# ------------------------------- Schema Constants ------------------------------- # -SYSTEM_ACCOUNT_NAME = "system" -USER_NAME_MAX_LEN = 20 -USER_NAME_MIN_LEN = 2 -FIRST_NAME_MAX_LEN = 50 -LAST_NAME_MAX_LEN = 50 -EMAIL_MAX_LEN = 250 -ROLE_NAME_MAX_LEN = 100 -CLIENT_NUMBER_MAX_LEN = 8 -CLIENT_NAME_MAX_LEN = 60 -APPLICATION_DESC_MAX_LEN = 200 -CREATE_USER_MAX_LEN = 100 - # Note! There is an issue for openapi generator to generate an enum with only 1 constant. # Since in future we plan to use "District", it is added (and can be used later) here # so openapi can generate it with no prolbme but for now it is a holder. @@ -101,6 +75,50 @@ class PrivilegeChangeTypeEnum(str, Enum): REVOKE = "REVOKE" UPDATE = "UPDATE" +class SortOrderEnum(str, Enum): + ASC = "asc" + DESC = "desc" + +class DelegatedAdminSortByEnum(str, Enum): + # Note: this is not the exact model column name, requires table column mapping. + CREATE_DATE = "create_date" + USER_NAME = "user_name" + DOMAIN = "user_type_code" + EMAIL = "email" + FULL_NAME = "full_name" # special case: first_name + last_name + ROLE_DISPLAY_NAME = "role_display_name" + FOREST_CLIENT_NUMBER = "forest_client_number" + +# ------------------------------- Schema Constants ------------------------------- # +APPLICATION_FAM = "FAM" +COGNITO_USERNAME_KEY = "username" + +FOREST_CLIENT_STATUS = {"KEY": "clientStatusCode", "CODE_ACTIVE": "ACT"} + +IDIM_PROXY_ACCOUNT_TYPE_MAP = {UserType.IDIR: "Internal", UserType.BCEID: "Business"} + +SYSTEM_ACCOUNT_NAME = "system" +USER_NAME_MAX_LEN = 20 +USER_NAME_MIN_LEN = 2 +FIRST_NAME_MAX_LEN = 50 +LAST_NAME_MAX_LEN = 50 +EMAIL_MAX_LEN = 250 +ROLE_NAME_MAX_LEN = 100 +CLIENT_NUMBER_MAX_LEN = 8 +CLIENT_NAME_MAX_LEN = 60 +APPLICATION_DESC_MAX_LEN = 200 +CREATE_USER_MAX_LEN = 100 +MIN_PAGE = 1 +DEFAULT_PAGE_SIZE = 50 +MIN_PAGE_SIZE = 10 + +# The intent is to have "max=100" per page, however frontend is not ready, so if need to return "all records" found +# and let frontend do the pagination, we could set it to 100000. +# >> MAX_PAGE_SIZE = 100 +MAX_PAGE_SIZE = 100000 +SEARCH_FIELD_MIN_LENGTH = 3 +SEARCH_FIELD_MAX_LENGTH = 30 + # ------- Error/Exception Code Constant ------- # Note, this is default error code but better use specific code category if possible. diff --git a/server/admin_management/api/app/datetime_format.py b/server/admin_management/api/app/datetime_format.py new file mode 100644 index 000000000..ecfd2db34 --- /dev/null +++ b/server/admin_management/api/app/datetime_format.py @@ -0,0 +1 @@ +TIMESTAMP_FORMAT_DEFAULT = "YYYY-MM-DD HH24:MI:SS" \ No newline at end of file diff --git a/server/admin_management/api/app/main.py b/server/admin_management/api/app/main.py index 1606afa1c..a314e9698 100644 --- a/server/admin_management/api/app/main.py +++ b/server/admin_management/api/app/main.py @@ -1,13 +1,6 @@ import logging.config import os.path -from fastapi import APIRouter, FastAPI -from fastapi.middleware.cors import CORSMiddleware -from mangum import Mangum -from pydantic import ValidationError -from requests import HTTPError -from starlette.responses import RedirectResponse - from api.app.exception_handlers import (requests_http_error_handler, unhandled_exception_handler, validation_exception_handler) @@ -15,6 +8,12 @@ router_admin_user_accesses, router_application_admin, router_smoke_test) from api.config.config import get_allow_origins, get_root_path +from fastapi import APIRouter, FastAPI +from fastapi.middleware.cors import CORSMiddleware +from mangum import Mangum +from pydantic import ValidationError +from requests import HTTPError +from starlette.responses import RedirectResponse logConfigFile = os.path.join( os.path.dirname(__file__), "..", "config", "logging.config" @@ -82,7 +81,7 @@ def custom_generate_unique_id(route: APIRouter): ) app.include_router( router_access_control_privilege.router, - prefix=apiPrefix + "/access_control_privileges", + prefix=apiPrefix + "/access-control-privileges", tags=["FAM Access Control Privileges"], ) app.include_router( diff --git a/server/admin_management/api/app/models/model.py b/server/admin_management/api/app/models/model.py index 7a36f2e3b..5eedb1017 100644 --- a/server/admin_management/api/app/models/model.py +++ b/server/admin_management/api/app/models/model.py @@ -5,6 +5,7 @@ Identity, Index, Integer, PrimaryKeyConstraint, String, UniqueConstraint, func, text) from sqlalchemy.dialects.postgresql import JSONB, TIMESTAMP +from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import (Mapped, declarative_base, mapped_column, relationship) @@ -93,6 +94,14 @@ class FamUser(Base): }, ) + # --- as hybrid Mapped attributes + @hybrid_property + def full_name(self): + if self.first_name is not None: + return self.first_name + " " + self.last_name + else: + return self.last_name + def __str__(self): return f"FamUser({self.user_id}, {self.user_name}, {self.user_type_code})" diff --git a/server/admin_management/api/app/repositories/access_control_privilege_repository.py b/server/admin_management/api/app/repositories/access_control_privilege_repository.py index 8eaee4e7c..25c6f16bb 100644 --- a/server/admin_management/api/app/repositories/access_control_privilege_repository.py +++ b/server/admin_management/api/app/repositories/access_control_privilege_repository.py @@ -1,16 +1,79 @@ import logging +from datetime import datetime from typing import List -from api.app.models.model import FamAccessControlPrivilege, FamRole -from api.app.schemas.schemas import FamAccessControlPrivilegeCreateDto +from api.app.constants import DelegatedAdminSortByEnum +from api.app.datetime_format import TIMESTAMP_FORMAT_DEFAULT +from api.app.models.model import (FamAccessControlPrivilege, FamForestClient, + FamRole, FamUser) +from api.app.repositories.simple_paginate_repository import \ + SimplePaginateRepository +from api.app.schemas.pagination import (DelegatedAdminPageParamsSchema, + PagedResultsSchema, PageParamsSchema) +from api.app.schemas.schemas import (FamAccessControlPrivilegeCreateDto, + FamAccessControlPrivilegeGetResponse) +from sqlalchemy import Column, ColumnElement, func, or_, select from sqlalchemy.orm import Session, joinedload LOGGER = logging.getLogger(__name__) -class AccessControlPrivilegeRepository: +class AccessControlPrivilegeRepository(SimplePaginateRepository): + """ + Repository class with db operations for Delegated Admin role assignment + (table=app_fam.fam_access_control_privilege). + + This class also inherits from "SimplePaginateRepository", an abstract base class which + provides functionality for simple pagination for query on fam_access_control_privilege. + """ def __init__(self, db: Session): self.db = db + super().__init__(db=db) + + def get_sort_by_column_mapping(self): + """ provides/overrides implementation for 'SimplePaginateRepository' """ + return { + DelegatedAdminSortByEnum.CREATE_DATE: FamAccessControlPrivilege.create_date, # default + DelegatedAdminSortByEnum.USER_NAME: FamUser.user_name, + DelegatedAdminSortByEnum.DOMAIN: FamUser.user_type_code, + DelegatedAdminSortByEnum.EMAIL: FamUser.email, + DelegatedAdminSortByEnum.FULL_NAME: FamUser.full_name, # this is a hybrid column + DelegatedAdminSortByEnum.ROLE_DISPLAY_NAME: FamRole.display_name, + DelegatedAdminSortByEnum.FOREST_CLIENT_NUMBER: FamForestClient.forest_client_number + } + + def get_filter_by_criteria(self, page_params: PageParamsSchema) -> ColumnElement[bool] | None: + """ + Provides/overrides implementation for 'SimplePaginateRepository'. + Based on 'search' keyword from page_params to build additional 'where' clause with + 'OR' sql condition, + e.g., (app_fam.fam_user.user_name ILIKE %(user_name_1)s OR + app_fam.fam_user.user_type_code ILIKE %(user_type_code_1)s OR + app_fam.fam_user.email ILIKE %(email_1)s OR ...) + to return for the query. + """ + search_keyword = page_params.search + filter_on_columns = self.get_sort_by_column_mapping().values() + + def operate_on_column(column: Column): + """ + Determines column type to apply sql operator/function for filtering. + """ + column_type = column.type.python_type + if column_type is str: + return column.ilike(f"%{search_keyword}%") + elif column_type is datetime: + return func.to_char(column, TIMESTAMP_FORMAT_DEFAULT).ilike(f"%{search_keyword}%") + + + return ( + or_( + # build where ... "OR" conditions for all mapped columns. + *list(map(lambda column: operate_on_column(column), filter_on_columns)) + ) + if search_keyword is not None + else None + ) def get_acp_by_user_id_and_role_id( self, user_id: int, role_id: int @@ -39,6 +102,34 @@ def get_acp_by_application_id( .all() ) + def get_paged_delegated_admins_assignment_by_application_id( + self, application_id: int, page_params: DelegatedAdminPageParamsSchema + ) -> PagedResultsSchema[FamAccessControlPrivilegeGetResponse]: + """ + Paginates app_fam.fam_access_control_privilege for the + application's Delegated Admin records. + Arguments: + application_id (int): The application's id, to find out the delegated admins + belong to this application. + + page_params (DelegatedAdminPageParamsSchema): pagination parameters for query to + return paged results. + + Returns: + PagedResultsSchema[FamAccessControlPrivilegeGetResponse]: A paged results containing + pagination metadata and a list of delegated admins assigned to this application. + """ + base_query = ( + select(FamAccessControlPrivilege) + .join(FamUser) + .join(FamRole) + .outerjoin(FamRole.client_number) + .filter(FamRole.application_id == application_id) + ) + return super().get_paginated_results( + base_query=base_query, page_params=page_params, ResultSchema=FamAccessControlPrivilegeGetResponse + ) + def create_access_control_privilege( self, fam_access_control_priviliege: FamAccessControlPrivilegeCreateDto ) -> FamAccessControlPrivilege: diff --git a/server/admin_management/api/app/repositories/simple_paginate_repository.py b/server/admin_management/api/app/repositories/simple_paginate_repository.py new file mode 100644 index 000000000..709190770 --- /dev/null +++ b/server/admin_management/api/app/repositories/simple_paginate_repository.py @@ -0,0 +1,112 @@ + +import logging +from abc import ABC, abstractmethod +from enum import StrEnum + +from api.app.constants import SortOrderEnum +from api.app.schemas.pagination import (PagedResultsSchema, PageParamsSchema, + PageResultMetaSchema) +from pydantic import BaseModel +from sqlalchemy import ColumnElement, Select, asc, desc, func, select +from sqlalchemy.orm import Session + +LOGGER = logging.getLogger(__name__) + +class SimplePaginateRepository(ABC): + """ + This class is an abstract base class which provides functionality for simple pagination. + Subclass needs to provides implementation for abstract methods. + """ + def __init__(self, db: Session): + self.db = db + + @abstractmethod + def get_sort_by_column_mapping(self) -> dict[StrEnum, any]: + """ Abstract methods, subclass needs to implement this. """ + pass + + @abstractmethod + def get_filter_by_criteria(self, page_params: PageParamsSchema) -> ColumnElement[bool] | None: + """ Abstract methods, subclass needs to implement this. """ + pass + + def get_paginated_results( + self, base_query: Select, page_params: PageParamsSchema, ResultSchema: type[BaseModel] + ) -> PagedResultsSchema[type[BaseModel]]: + """ + Paginate the query results. + Main implemented function for this abstract repository, it will apply 'filter (where clause)', 'order_by clause' + if needed and apply paging based on calculated 'offset' and 'limit' + + Arguments: + base_query (Select): A base Select query provided from subclass repository to be based on. + page_params (PageParamsSchema): pagination parameters for query to return paged results. + ResultSchema (type[BaseModel]): This is the return type Class and used for paged result conversion. + + Return: + Paged result with type 'PagedResultsSchema[type[BaseModel]]'. Other than paged results, + the pagniation metadata are also returned. + """ + LOGGER.debug(f"Obtaining paginated results with page params: {page_params}") + self.base_query = base_query + self.page_params = page_params + self.page = page_params.page + self.size = page_params.size + self.__limit = self.size + self.__offset = (self.page - 1) * self.size + + paged_query = self.__apply_filter_by(self.base_query, page_params) + paged_query = self.__apply_order_by(paged_query) + paged_query = paged_query.offset(self.__offset).limit(self.__limit) + total_counts = self.__get_total_count() + results = PagedResultsSchema[ResultSchema]( + meta = PageResultMetaSchema( + total=total_counts, + number_of_pages=self.__get_number_of_pages(total_counts), + page_number=self.page, + page_size=self.size + ), + results=[ResultSchema.model_validate(item) for item in self.db.scalars(paged_query)] + ) + return results + + def __get_total_count(self) -> int: + total_count_q = self.__apply_filter_by(self.base_query, self.page_params) + count = self.db.scalar( + select(func.count()).select_from(total_count_q.subquery()) + ) + return count + + def __get_number_of_pages(self, count: int) -> int: + rest = count % self.size + quotient = count // self.size + return quotient if not rest else quotient + 1 + + def __apply_order_by(self, q: Select) -> Select: + """ + Based on 'sort_by' and 'sort_order' page_params to build SQL "ORDER BY" + clause, e.g., ("ORDER BY app_fam.fam_user.user_name ASC") to return + for the query. + """ + sort_by = self.page_params.sort_by + sort_order = self.page_params.sort_order + column_mapping = self.get_sort_by_column_mapping() + mapped_column = ( + list(column_mapping.values())[0] # default sort_by column + if sort_by is None + else column_mapping.get(sort_by) + ) + + order_by_criteria = asc(mapped_column) if sort_order == SortOrderEnum.ASC else desc(mapped_column) + + LOGGER.debug(f"Applying order_by criteria: {order_by_criteria}") + if order_by_criteria is not None: + q = q.order_by(order_by_criteria) + return q + + def __apply_filter_by(self, q: Select, page_params: PageParamsSchema) -> Select: + filter_by_criteria = self.get_filter_by_criteria(page_params) + LOGGER.debug(f"Applying filter criteria: {filter_by_criteria}") + if filter_by_criteria is not None: + q = q.filter(filter_by_criteria) + return q \ No newline at end of file diff --git a/server/admin_management/api/app/routers/router_access_control_privilege.py b/server/admin_management/api/app/routers/router_access_control_privilege.py index 5a0b2bf41..f2c3ef38d 100644 --- a/server/admin_management/api/app/routers/router_access_control_privilege.py +++ b/server/admin_management/api/app/routers/router_access_control_privilege.py @@ -10,6 +10,8 @@ from api.app.routers.router_utils import ( access_control_privilege_service_instance, role_service_instance, user_service_instance) +from api.app.schemas.pagination import (DelegatedAdminPageParamsSchema, + PagedResultsSchema) from api.app.schemas.schemas import (FamAccessControlPrivilegeCreateRequest, FamAccessControlPrivilegeGetResponse, FamAccessControlPrivilegeResponse, @@ -119,18 +121,21 @@ def create_access_control_privilege_many( @router.get( "", - response_model=List[FamAccessControlPrivilegeGetResponse], + response_model=PagedResultsSchema[FamAccessControlPrivilegeGetResponse], status_code=200, dependencies=[Depends(authorize_by_app_id)], # only app admin can do this - description="Get Delegated Admin Privileges For an Application", + description="Get 'Delegated Admin Privileges' for an application with pagination.", ) def get_access_control_privileges_by_application_id( application_id: int, access_control_privilege_service: AccessControlPrivilegeService = Depends( access_control_privilege_service_instance ), + page_params: DelegatedAdminPageParamsSchema = Depends(), ): - return access_control_privilege_service.get_acp_by_application_id(application_id) + return access_control_privilege_service.get_paged_delegated_admin_assignment_by_application_id( + application_id, page_params + ) @router.delete( diff --git a/server/admin_management/api/app/schemas/pagination.py b/server/admin_management/api/app/schemas/pagination.py new file mode 100644 index 000000000..dc76359e8 --- /dev/null +++ b/server/admin_management/api/app/schemas/pagination.py @@ -0,0 +1,87 @@ + +from abc import ABC +from enum import StrEnum +from typing import Generic, List + +from api.app.constants import (DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE, MIN_PAGE, + MIN_PAGE_SIZE, SEARCH_FIELD_MAX_LENGTH, + SEARCH_FIELD_MIN_LENGTH, + DelegatedAdminSortByEnum, SortOrderEnum, T) +from fastapi import Query +from pydantic import BaseModel, Field + +""" +Schema objects for the backend pagination, sorting, filtering related purpose. +""" + +""" +Note: FastAPI does not seem to work well with Pydantic for Query Parameter + Swagger. +To use Pydantic class in "router's endpoint" as FastAPI query parameter, it needs to wrap the property with "Field(Query(...))" +like below example in order to get validation correct and with 'description' shows up at Swagger: + page_number: int | None = Field(Query(default=1, ge=1, description="page number to get the paged data")) + +More over, the endpoint also need to use "Depends()" for function argument, like below exampe: + def get_fam_application_user_role_assignment( + application_id: int, + page_params: PageParamsSchema = Depends(), + ... + ) + +And, in this case, the validation error default from Pydantic will not be 400, it will be 422 (from Pydantic) + +Ref: https://stackoverflow.com/questions/75998227/how-to-define-query-parameters-using-pydantic-model-in-fastapi +""" + +class PageParamsSchema(BaseModel, ABC): + """ + Abstract class for request query params for backend API pagination, sorting and filtering. + This is the base schema for common fields. Endpoints need to extend this class and override + 'sort_by' for specific needs. + """ + page: int | None = Field(Query( + default=MIN_PAGE, ge=MIN_PAGE, description="Page number", alias="pageNumber" + )) + size: int | None = Field(Query( + default=DEFAULT_PAGE_SIZE, ge=MIN_PAGE_SIZE, le=MAX_PAGE_SIZE, description="Number of records per page", alias="pageSize" + )) + + search: str | None = Field(Query( + default=None, min_length=SEARCH_FIELD_MIN_LENGTH, max_length=SEARCH_FIELD_MAX_LENGTH, description="Search by keyword" + )) + + sort_order: SortOrderEnum | None = Field(Query( + default=SortOrderEnum.DESC, alias="sortOrder", + description=( + f'Column sorting order by
Possible values: [{", ".join([enum for enum in SortOrderEnum])}]' + ) + )) + + sort_by: StrEnum | None = None + + +class DelegatedAdminPageParamsSchema(PageParamsSchema): + """ + Pagination parameteres schema object for request query parameters used by Delegated Admin frontend view table. + """ + sort_by: DelegatedAdminSortByEnum | None = Field(Query( + default=DelegatedAdminSortByEnum.CREATE_DATE, alias="sortBy", + description=( + f'Column to be sorted by
Possible values: [{", ".join([enum for enum in DelegatedAdminSortByEnum])}]' + ) + )) + + +class PageResultMetaSchema(BaseModel): + total: int = Field(description='Total records counts for query conditions') + number_of_pages: int = Field(description='Total pages for query records') + page_number: int = Field(description='Page number') + page_size: int = Field(description='Number of records per page') + + +class PagedResultsSchema(BaseModel, Generic[T]): + """ + API pagination return schema. + Use Python generice type for return type. + """ + meta: PageResultMetaSchema + results: List[T] = Field(description='Paged results') \ No newline at end of file diff --git a/server/admin_management/api/app/schemas/schemas.py b/server/admin_management/api/app/schemas/schemas.py index a51bc53e6..8d30a8e99 100644 --- a/server/admin_management/api/app/schemas/schemas.py +++ b/server/admin_management/api/app/schemas/schemas.py @@ -1,4 +1,5 @@ import logging +from datetime import datetime from typing import List, Literal, Optional, Union from api.app.constants import (APPLICATION_DESC_MAX_LEN, CLIENT_NAME_MAX_LEN, @@ -259,6 +260,7 @@ class FamAccessControlPrivilegeGetResponse(BaseModel): role_id: int user: FamUserInfoDto role: FamRoleWithClientDto + create_date: datetime model_config = ConfigDict(from_attributes=True) diff --git a/server/admin_management/api/app/services/access_control_privilege_service.py b/server/admin_management/api/app/services/access_control_privilege_service.py index d70e6543b..22de4a430 100644 --- a/server/admin_management/api/app/services/access_control_privilege_service.py +++ b/server/admin_management/api/app/services/access_control_privilege_service.py @@ -8,6 +8,8 @@ from api.app.integration.gc_notify import GCNotifyEmailService from api.app.repositories.access_control_privilege_repository import \ AccessControlPrivilegeRepository +from api.app.schemas.pagination import (DelegatedAdminPageParamsSchema, + PagedResultsSchema) from api.app.schemas.schemas import (FamAccessControlPrivilegeCreateDto, FamAccessControlPrivilegeCreateRequest, FamAccessControlPrivilegeCreateResponse, @@ -35,6 +37,26 @@ def __init__(self, db: Session): self.permission_audit_service = PermissionAuditService(db) self.access_control_privilege_repository = AccessControlPrivilegeRepository(db) + def get_paged_delegated_admin_assignment_by_application_id( + self, application_id: int, page_params: DelegatedAdminPageParamsSchema + ) -> PagedResultsSchema[FamAccessControlPrivilegeGetResponse]: + """ + Service method to get access control privilege (a.k.a Delegated Admin) by application id. + Arguments: + application_id (int): The application's id, to find out the delegated admins + belong to this application. + + page_params (DelegatedAdminPageParamsSchema): pagination parameters for query to + return paged results. + + Returns: + PagedResultsSchema[FamAccessControlPrivilegeGetResponse]: A paged results containing + pagination metadata and a list of delegated admins assigned to this application. + """ + return self.access_control_privilege_repository.get_paged_delegated_admins_assignment_by_application_id( + application_id=application_id, page_params=page_params + ) + def get_acp_by_user_id_and_role_id(self, user_id: int, role_id: int): return self.access_control_privilege_repository.get_acp_by_user_id_and_role_id( user_id, role_id @@ -275,4 +297,4 @@ def send_email_notification( f"Failure sending email to the new delegated admin {target_user.email}." ) LOGGER.debug(f"Failure reason : {e}.") - return famConstants.EmailSendingStatus.SENT_TO_EMAIL_SERVICE_FAILURE + return famConstants.EmailSendingStatus.SENT_TO_EMAIL_SERVICE_FAILURE \ No newline at end of file diff --git a/server/admin_management/tests/routers/test_router_access_control_privilege.py b/server/admin_management/tests/routers/test_router_access_control_privilege.py index 3a8e06b83..428f99c16 100644 --- a/server/admin_management/tests/routers/test_router_access_control_privilege.py +++ b/server/admin_management/tests/routers/test_router_access_control_privilege.py @@ -22,7 +22,7 @@ TEST_USER_BUSINESS_GUID_BCEID) LOGGER = logging.getLogger(__name__) -endPoint = f"{apiPrefix}/access_control_privileges" +endPoint = f"{apiPrefix}/access-control-privileges" def test_create_access_control_privilege_many( @@ -142,7 +142,7 @@ def test_get_access_control_privileges_by_application_id( ) assert response.status_code == HTTPStatus.OK assert response.json() is not None - origin_admins_length = len(response.json()) + origin_admins_length = len(response.json()["results"]) # override router guard dependencies override_get_verified_target_user() @@ -162,7 +162,7 @@ def test_get_access_control_privileges_by_application_id( ) assert response.status_code == HTTPStatus.OK assert response.json() is not None - admins_length = len(response.json()) + admins_length = len(response.json()["results"]) assert admins_length == origin_admins_length + 1 # test get with non exists application id diff --git a/server/admin_management/tests/services/test_permission_audit_service.py b/server/admin_management/tests/services/test_permission_audit_service.py index 957113cf8..c1a1e1357 100644 --- a/server/admin_management/tests/services/test_permission_audit_service.py +++ b/server/admin_management/tests/services/test_permission_audit_service.py @@ -1,5 +1,6 @@ import copy import logging +from datetime import datetime from http import HTTPStatus from typing import List @@ -345,7 +346,8 @@ def verify_delegated_admin_revoked_privilege_details( user_type_relation=FamUserTypeDto(user_type_code=UserType.BCEID, description='BCEID')), role=FamRoleWithClientDto(role_name='FOM_REVIEWER', application=FamApplicationBase(application_id=2, application_name='FOM_DEV', application_description='Forest Operations Map (DEV)'), - role_id=999, display_name='Reviewer', role_purpose='Provides the privilege to review all FOMs in the system', client_number=None, parent_role=None)), + role_id=999, display_name='Reviewer', role_purpose='Provides the privilege to review all FOMs in the system', client_number=None, parent_role=None), + create_date=datetime(2024, 11, 1, 19, 44, 47)), 'error_message': None } ) @@ -362,7 +364,8 @@ def verify_delegated_admin_revoked_privilege_details( role_id=127, display_name='Submitter', role_purpose='Provides the privilege to submit a FOM (on behalf of a specific forest client)', client_number=FamForestClientBase(client_name=None, forest_client_number="00001012", status=None), parent_role=FamRoleBase(role_name="FOM_SUBMITTER", role_type_code="A", - application=FamApplicationBase(application_id=2, application_name='FOM_DEV', application_description='Forest Operations Map (DEV)')))), + application=FamApplicationBase(application_id=2, application_name='FOM_DEV', application_description='Forest Operations Map (DEV)'))), + create_date=datetime(2024, 11, 1, 19, 44, 47)), 'error_message': None } ) diff --git a/server/backend/api/app/constants.py b/server/backend/api/app/constants.py index 017e92906..0ebdfa976 100644 --- a/server/backend/api/app/constants.py +++ b/server/backend/api/app/constants.py @@ -2,9 +2,40 @@ from enum import Enum from typing import TypeVar -# FAM application name in database -APPLICATION_FAM = "FAM" +# ----------------------- Generic Type Variable Declaration ----------------------- # +T = TypeVar('T') + +# --------------------------------- Schema Enums ---------------------------------- # +class PrivilegeChangeTypeEnum(str, Enum): + GRANT = "GRANT" + REVOKE = "REVOKE" + UPDATE = "UPDATE" + +# Note! There is an issue for openapi generator to generate an enum with only 1 constant. +# Since in future we plan to use "District", it is added (and can be used later) here +# so openapi can generate it with no prolbme but for now it is a holder. +class PrivilegeDetailsScopeTypeEnum(str, Enum): + CLIENT = "Client" + DISTRICT = "District" + + +class PrivilegeDetailsPermissionTypeEnum(str, Enum): + END_USER = "End User" + DELEGATED_ADMIN = "Delegated Admin" + APPLICATION_ADMIN = "Application Admin" +class SortOrderEnum(str, Enum): + ASC = "asc" + DESC = "desc" + +class UserRoleSortByEnum(str, Enum): + # Note: this is not the exact model column name, requires table column mapping. + USER_NAME = "user_name" + DOMAIN = "user_type_code" + EMAIL = "email" + FULL_NAME = "full_name" # special case: first_name + last_name + ROLE_DISPLAY_NAME = "role_display_name" + FOREST_CLIENT_NUMBER = "forest_client_number" class UserType(str, Enum): IDIR = "I" @@ -45,25 +76,6 @@ class FamForestClientStatusType(str, Enum): ACTIVE = "A" INACTIVE = "I" - -DESCRIPTION_ACTIVE = "Active" -DESCRIPTION_INACTIVE = "Inactive" - -# Constans for FAM to coneniently refer to Forest Client API return json object -# keys/values. -FOREST_CLIENT_STATUS = {"KEY": "clientStatusCode", "CODE_ACTIVE": "ACT"} - -FAM_PROXY_API_USER = "fam_proxy_api" - -COGNITO_USERNAME_KEY = "username" - -# The most current terms and conditions. Note, when terms and conditions gets updated -# at frontend, this also needs to be updated and in-sync. -CURRENT_TERMS_AND_CONDITIONS_VERSION = "1" - -IDIM_PROXY_ACCOUNT_TYPE_MAP = {UserType.IDIR: "Internal", UserType.BCEID: "Business"} - - class IdimSearchUserParamType(str, Enum): USER_GUID = "userGuid" USER_ID = "userId" @@ -76,23 +88,7 @@ class EmailSendingStatus(str, Enum): ) SENT_TO_EMAIL_SERVICE_FAILURE = "SENT_TO_EMAIL_SERVICE_FAILURE" # technical/validation failure during sending to external service. - -# ------- Error/Exception Code Constant ------- - -# Note, this is default error code but better use specific code category if possible. -ERROR_CODE_INVALID_OPERATION = "invalid_operation" -ERROR_CODE_INVALID_APPLICATION_ID = "invalid_application_id" -ERROR_CODE_SELF_GRANT_PROHIBITED = "self_grant_prohibited" -ERROR_CODE_INVALID_ROLE_ID = "invalid_role_id" -ERROR_CODE_REQUESTER_NOT_EXISTS = "requester_not_exists" -ERROR_CODE_EXTERNAL_USER_ACTION_PROHIBITED = "external_user_action_prohibited" -ERROR_CODE_DIFFERENT_ORG_GRANT_PROHIBITED = "different_org_grant_prohibited" -ERROR_CODE_MISSING_KEY_ATTRIBUTE = "missing_key_attribute" -ERROR_CODE_INVALID_REQUEST_PARAMETER = "invalid_request_parameter" -ERROR_CODE_TERMS_CONDITIONS_REQUIRED = "terms_condition_required" -ERROR_CODE_UNKNOWN_STATE = "unknown_state" - -# ------------------------------- Schema Constants ------------------------------- # +# -------------------------------- Schema Constants ------------------------------- # SYSTEM_ACCOUNT_NAME = "system" USER_NAME_MAX_LEN = 20 FIRST_NAME_MAX_LEN = 50 @@ -115,37 +111,36 @@ class EmailSendingStatus(str, Enum): SEARCH_FIELD_MIN_LENGTH = 3 SEARCH_FIELD_MAX_LENGTH = 30 -# ----------------------- Generic Type Variable Declaration ----------------------- # -T = TypeVar('T') +# FAM application name in database +APPLICATION_FAM = "FAM" -# --------------------------------- Schema Enums --------------------------------- # -class PrivilegeChangeTypeEnum(str, Enum): - GRANT = "GRANT" - REVOKE = "REVOKE" - UPDATE = "UPDATE" +DESCRIPTION_ACTIVE = "Active" +DESCRIPTION_INACTIVE = "Inactive" -# Note! There is an issue for openapi generator to generate an enum with only 1 constant. -# Since in future we plan to use "District", it is added (and can be used later) here -# so openapi can generate it with no prolbme but for now it is a holder. -class PrivilegeDetailsScopeTypeEnum(str, Enum): - CLIENT = "Client" - DISTRICT = "District" +# Constans for FAM to coneniently refer to Forest Client API return json object +# keys/values. +FOREST_CLIENT_STATUS = {"KEY": "clientStatusCode", "CODE_ACTIVE": "ACT"} +FAM_PROXY_API_USER = "fam_proxy_api" -class PrivilegeDetailsPermissionTypeEnum(str, Enum): - END_USER = "End User" - DELEGATED_ADMIN = "Delegated Admin" - APPLICATION_ADMIN = "Application Admin" +COGNITO_USERNAME_KEY = "username" -class SortOrderEnum(str, Enum): - ASC = "asc" - DESC = "desc" +# The most current terms and conditions. Note, when terms and conditions gets updated +# at frontend, this also needs to be updated and in-sync. +CURRENT_TERMS_AND_CONDITIONS_VERSION = "1" -class UserRoleSortByEnum(str, Enum): - # Note: this is not the exact model column name, requires table column mapping. - USER_NAME = "user_name" - DOMAIN = "user_type_code" - EMAIL = "email" - FULL_NAME = "full_name" # special case: first_name + last_name - ROLE_DISPLAY_NAME = "role_display_name" - FOREST_CLIENT_NUMBER = "forest_client_number" +IDIM_PROXY_ACCOUNT_TYPE_MAP = {UserType.IDIR: "Internal", UserType.BCEID: "Business"} + +# ------------------------- Error/Exception Code Constant ------------------------- # +# Note, this is default error code but better use specific code category if possible. +ERROR_CODE_INVALID_OPERATION = "invalid_operation" +ERROR_CODE_INVALID_APPLICATION_ID = "invalid_application_id" +ERROR_CODE_SELF_GRANT_PROHIBITED = "self_grant_prohibited" +ERROR_CODE_INVALID_ROLE_ID = "invalid_role_id" +ERROR_CODE_REQUESTER_NOT_EXISTS = "requester_not_exists" +ERROR_CODE_EXTERNAL_USER_ACTION_PROHIBITED = "external_user_action_prohibited" +ERROR_CODE_DIFFERENT_ORG_GRANT_PROHIBITED = "different_org_grant_prohibited" +ERROR_CODE_MISSING_KEY_ATTRIBUTE = "missing_key_attribute" +ERROR_CODE_INVALID_REQUEST_PARAMETER = "invalid_request_parameter" +ERROR_CODE_TERMS_CONDITIONS_REQUIRED = "terms_condition_required" +ERROR_CODE_UNKNOWN_STATE = "unknown_state" \ No newline at end of file