async def migrate_database(resources_repo=Depends(get_repository(ResourceRepository)), shared_services_migration=Depends(get_repository(SharedServiceMigration)), workspace_migration=Depends(get_repository(WorkspaceMigration))): try: migrations = list() logging.info("PR 1030.") resources_repo.rename_field_name('resourceTemplateName', 'templateName') resources_repo.rename_field_name('resourceTemplateVersion', 'templateVersion') resources_repo.rename_field_name('resourceTemplateParameters', 'properties') migrations.append(Migration(issueNumber="PR 1030", status="Executed")) logging.info("PR 1031.") resources_repo.rename_field_name('workspaceType', 'templateName') resources_repo.rename_field_name('workspaceServiceType', 'templateName') resources_repo.rename_field_name('userResourceType', 'templateName') migrations.append(Migration(issueNumber="PR 1031", status="Executed")) logging.info("PR 1717. - Shared services") migration_status = "Executed" if shared_services_migration.deleteDuplicatedSharedServices() else "Skipped" migrations.append(Migration(issueNumber="PR 1717", status=migration_status)) logging.info("PR 1726. - Authentication needs to be in properties so we can update them") migration_status = "Executed" if workspace_migration.moveAuthInformationToProperties() else "Skipped" migrations.append(Migration(issueNumber="PR 1726", status=migration_status)) return MigrationOutList(migrations=migrations) except Exception as e: logging.error(f"Failed to migrate database: {e}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
async def create_workspace( workspace_create: WorkspaceInCreate, response: Response, user=Depends(get_current_admin_user), workspace_repo=Depends(get_repository(WorkspaceRepository)), resource_template_repo=Depends(get_repository(ResourceTemplateRepository)), operations_repo=Depends(get_repository(OperationRepository)) ) -> OperationInResponse: try: # TODO: This requires Directory.ReadAll ( Application.Read.All ) to be enabled in the Azure AD application to enable a users workspaces to be listed. This should be made optional. auth_info = extract_auth_information(workspace_create.properties) workspace, resource_template = workspace_repo.create_workspace_item( workspace_create, auth_info, user.id) except (ValidationError, ValueError) as e: logging.error(f"Failed to create workspace model instance: {e}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) operation = await save_and_deploy_resource( resource=workspace, resource_repo=workspace_repo, operations_repo=operations_repo, resource_template_repo=resource_template_repo, user=user, resource_template=resource_template) response.headers["Location"] = construct_location_header(operation) return OperationInResponse(operation=operation)
async def create_user_resource( response: Response, user_resource_create: UserResourceInCreate, user_resource_repo=Depends(get_repository(UserResourceRepository)), resource_template_repo=Depends(get_repository(ResourceTemplateRepository)), operations_repo=Depends(get_repository(OperationRepository)), user=Depends(get_current_workspace_owner_or_researcher_user), workspace=Depends(get_deployed_workspace_by_id_from_path), workspace_service=Depends(get_deployed_workspace_service_by_id_from_path) ) -> OperationInResponse: try: user_resource, resource_template = user_resource_repo.create_user_resource_item( user_resource_create, workspace.id, workspace_service.id, workspace_service.templateName, user.id) except (ValidationError, ValueError) as e: logging.error(f"Failed create user resource model instance: {e}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) operation = await save_and_deploy_resource( resource=user_resource, resource_repo=user_resource_repo, operations_repo=operations_repo, resource_template_repo=resource_template_repo, user=user, resource_template=resource_template) response.headers["Location"] = construct_location_header(operation) return OperationInResponse(operation=operation)
async def delete_shared_service( response: Response, user=Depends(get_current_admin_user), shared_service=Depends(get_shared_service_by_id_from_path), operations_repo=Depends(get_repository(OperationRepository)), shared_service_repo=Depends(get_repository(SharedServiceRepository)), resource_template_repo=Depends(get_repository(ResourceTemplateRepository)) ) -> OperationInResponse: if shared_service.isEnabled: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=strings.SHARED_SERVICE_NEEDS_TO_BE_DISABLED_BEFORE_DELETION) resource_template = resource_template_repo.get_template_by_name_and_version( shared_service.templateName, shared_service.templateVersion, ResourceType.SharedService) operation = await send_uninstall_message( resource=shared_service, resource_repo=shared_service_repo, operations_repo=operations_repo, resource_type=ResourceType.SharedService, resource_template_repo=resource_template_repo, user=user, resource_template=resource_template) response.headers["Location"] = construct_location_header(operation) return OperationInResponse(operation=operation)
async def create_shared_service( response: Response, shared_service_input: SharedServiceInCreate, user=Depends(get_current_admin_user), shared_services_repo=Depends(get_repository(SharedServiceRepository)), resource_template_repo=Depends(get_repository(ResourceTemplateRepository)), operations_repo=Depends(get_repository(OperationRepository)) ) -> OperationInResponse: try: shared_service, resource_template = shared_services_repo.create_shared_service_item( shared_service_input) except (ValidationError, ValueError) as e: logging.error(f"Failed create shared service model instance: {e}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) except DuplicateEntity as e: logging.error(f"Shared service already exists: {e}") raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e)) operation = await save_and_deploy_resource( resource=shared_service, resource_repo=shared_services_repo, operations_repo=operations_repo, resource_template_repo=resource_template_repo, user=user, resource_template=resource_template) response.headers["Location"] = construct_location_header(operation) return OperationInResponse(operation=operation)
async def delete_user_resource( response: Response, user=Depends(get_current_workspace_owner_or_researcher_user), user_resource=Depends(get_user_resource_by_id_from_path), workspace_service=Depends(get_workspace_service_by_id_from_path), user_resource_repo=Depends(get_repository(UserResourceRepository)), operations_repo=Depends(get_repository(OperationRepository)), resource_template_repo=Depends(get_repository(ResourceTemplateRepository)) ) -> OperationInResponse: validate_user_is_workspace_owner_or_resource_owner(user, user_resource) if user_resource.isEnabled: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=strings.USER_RESOURCE_NEEDS_TO_BE_DISABLED_BEFORE_DELETION) resource_template = resource_template_repo.get_template_by_name_and_version( user_resource.templateName, user_resource.templateVersion, ResourceType.UserResource, workspace_service.templateName) operation = await send_uninstall_message( resource=user_resource, resource_repo=user_resource_repo, operations_repo=operations_repo, resource_type=ResourceType.UserResource, resource_template_repo=resource_template_repo, user=user, resource_template=resource_template) response.headers["Location"] = construct_location_header(operation) return OperationInResponse(operation=operation)
async def patch_user_resource( user_resource_patch: ResourcePatch, response: Response, user=Depends(get_current_workspace_owner_or_researcher_user), user_resource=Depends(get_user_resource_by_id_from_path), workspace_service=Depends(get_workspace_service_by_id_from_path), user_resource_repo=Depends(get_repository(UserResourceRepository)), resource_template_repo=Depends(get_repository(ResourceTemplateRepository)), operations_repo=Depends(get_repository(OperationRepository)), etag: str = Header(...) ) -> OperationInResponse: validate_user_is_workspace_owner_or_resource_owner(user, user_resource) try: patched_user_resource, resource_template = user_resource_repo.patch_user_resource( user_resource, user_resource_patch, etag, resource_template_repo, workspace_service.templateName, user) operation = await send_resource_request_message( resource=patched_user_resource, operations_repo=operations_repo, resource_repo=user_resource_repo, user=user, resource_template=resource_template, resource_template_repo=resource_template_repo, action=RequestAction.Upgrade) response.headers["Location"] = construct_location_header(operation) return OperationInResponse(operation=operation) except CosmosAccessConditionFailedError: raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=strings.ETAG_CONFLICT) except ValidationError as v: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=v.message)
async def get_deployed_workspace_by_id_from_path( workspace_id: UUID4 = Path(...), workspaces_repo=Depends(get_repository(WorkspaceRepository)), operations_repo=Depends(get_repository(OperationRepository)) ) -> Workspace: try: return workspaces_repo.get_deployed_workspace_by_id( workspace_id, operations_repo) except EntityDoesNotExist: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=strings.WORKSPACE_DOES_NOT_EXIST) except ResourceIsNotDeployed: raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=strings.WORKSPACE_IS_NOT_DEPLOYED)
async def get_openapi_json(workspace_id: str, request: Request, workspace_repo=Depends( get_repository(WorkspaceRepository))): global openapi_definitions if openapi_definitions[workspace_id] is None: openapi_definitions[workspace_id] = get_openapi( title=f"{config.PROJECT_NAME} - Workspace {workspace_id}", description=config.API_DESCRIPTION, version=config.VERSION, routes=workspace_router.routes, tags=workspace_tags_metadata) workspace = workspace_repo.get_workspace_by_id(workspace_id) scope = {get_scope(workspace): "List and Get TRE Workspaces"} openapi_definitions[workspace_id]['components']['securitySchemes'][ 'oauth2']['flows']['authorizationCode']['scopes'] = scope # Add an example into every workspace_id path parameter so users don't have to cut and paste them in. for route in openapi_definitions[workspace_id]['paths'].values(): for verb in route.values(): # We now have a list of parameters for each route for parameter in verb['parameters']: if (parameter['name'] == 'workspace_id'): parameter['schema']['example'] = workspace_id return openapi_definitions[workspace_id]
async def retrieve_workspace_service_operations_by_workspace_service_id( workspace_service=Depends(get_workspace_service_by_id_from_path), operations_repo=Depends(get_repository(OperationRepository)) ) -> OperationInList: return OperationInList( operations=operations_repo.get_operations_by_resource_id( resource_id=workspace_service.id))
async def get_user_resource_templates_for_service_template( service_template_name: str, template_repo=Depends(get_repository(ResourceTemplateRepository)) ) -> ResourceTemplateInformationInList: template_infos = template_repo.get_templates_information( ResourceType.UserResource, service_template_name) return ResourceTemplateInformationInList(templates=template_infos)
async def retrieve_users_active_workspace_services( workspace=Depends(get_workspace_by_id_from_path), workspace_services_repo=Depends( get_repository(WorkspaceServiceRepository)) ) -> WorkspaceServicesInList: workspace_services = workspace_services_repo.get_active_workspace_services_for_workspace( workspace.id) return WorkspaceServicesInList(workspaceServices=workspace_services)
async def retrieve_term( terms_to_retrieve: TermRetrieve = Body(..., embed=True), ontology_repo: OntologyRepository = Depends( get_repository(OntologyRepository)), ) -> dict: result = await ontology_repo.retrieve_terms( terms_to_retrieve=terms_to_retrieve) return {'message': 'Retrieval task completed successfully'}
async def create_new_cleaning( new_cleaning: CleaningCreate = Body(..., embed=True), cleanings_repo: CleaningsRepository = Depends( get_repository(CleaningsRepository)), ) -> CleaningPublic: created_cleaning = await cleanings_repo.create_cleaning( new_cleaning=new_cleaning) return created_cleaning
async def retrieve_user_resource_operations_by_user_resource_id( user_resource=Depends(get_user_resource_by_id_from_path), user=Depends(get_current_workspace_owner_or_researcher_user), operations_repo=Depends(get_repository(OperationRepository)) ) -> OperationInList: validate_user_is_workspace_owner_or_resource_owner(user, user_resource) return OperationInList( operations=operations_repo.get_operations_by_resource_id( resource_id=user_resource.id))
async def get_operation_by_id_from_path( operation_id: UUID4 = Path(...), operations_repo=Depends(get_repository(OperationRepository)) ) -> Operation: try: return operations_repo.get_operation_by_id(operation_id=operation_id) except EntityDoesNotExist: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=strings.OPERATION_DOES_NOT_EXIST)
async def get_current_workspace_service_template_by_name( service_template_name: str, is_update: bool = False, template_repo=Depends(get_repository(ResourceTemplateRepository)) ) -> WorkspaceServiceTemplateInResponse: template = get_current_template_by_name(service_template_name, template_repo, ResourceType.WorkspaceService, is_update=is_update) return parse_obj_as(WorkspaceServiceTemplateInResponse, template)
async def register_workspace_service_template( template_input: WorkspaceServiceTemplateInCreate, template_repo=Depends(get_repository(ResourceTemplateRepository)) ) -> ResourceTemplateInResponse: try: return template_repo.create_and_validate_template( template_input, ResourceType.WorkspaceService) except EntityVersionExist: raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=strings.WORKSPACE_TEMPLATE_VERSION_EXISTS)
async def register_new_user(new_user: UserCreate = Body(..., embed=True), user_repo: UsersRepository = Depends( get_repository( UsersRepository))) -> UserPublic: created_user = await user_repo.register_new_user(new_user=new_user) access_token = AccessToken( access_token=auth_service.create_access_token_for_user( user=created_user), token_type='bearer') return UserPublic(**created_user.dict(), access_token=access_token)
async def get_workspace_service_template_by_name_from_path( service_template_name: str = Path(...), template_repo=Depends(get_repository(ResourceTemplateRepository)) ) -> ResourceTemplate: try: return template_repo.get_current_template( service_template_name, ResourceType.WorkspaceService) except EntityDoesNotExist: raise HTTPException( status_code=HTTP_404_NOT_FOUND, detail=strings.WORKSPACE_SERVICE_TEMPLATE_DOES_NOT_EXIST)
async def get_workspace_service_by_id_from_path( workspace_id: UUID4 = Path(...), service_id: UUID4 = Path(...), workspace_services_repo=Depends( get_repository(WorkspaceServiceRepository))) -> WorkspaceService: try: return workspace_services_repo.get_workspace_service_by_id( workspace_id, service_id) except EntityDoesNotExist: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=strings.WORKSPACE_SERVICE_DOES_NOT_EXIST)
async def get_user_resource_by_id_from_path( workspace_id: UUID4 = Path(...), service_id: UUID4 = Path(...), resource_id: UUID4 = Path(...), user_resource_repo=Depends(get_repository(UserResourceRepository)) ) -> UserResource: try: return user_resource_repo.get_user_resource_by_id( workspace_id, service_id, resource_id) except EntityDoesNotExist: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=strings.USER_RESOURCE_DOES_NOT_EXIST)
async def get_current_user_resource_template_by_name( service_template_name: str, user_resource_template_name: str, is_update: bool = False, template_repo=Depends(get_repository(ResourceTemplateRepository)) ) -> UserResourceTemplateInResponse: template = get_current_template_by_name(user_resource_template_name, template_repo, ResourceType.UserResource, service_template_name, is_update=is_update) return parse_obj_as(UserResourceTemplateInResponse, template)
async def get_cleaning_by_id( id: int, cleanings_repo: CleaningsRepository = Depends( get_repository(CleaningsRepository)) ) -> CleaningPublic: cleaning = await cleanings_repo.get_cleaning_by_id(id=id) if not cleaning: raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="No cleaning found with that id.") return cleaning
async def invoke_action_on_workspace_service( response: Response, action: str, user=Depends(get_current_workspace_owner_user), workspace_service=Depends(get_workspace_service_by_id_from_path), resource_template_repo=Depends(get_repository(ResourceTemplateRepository)), operations_repo=Depends(get_repository(OperationRepository)), workspace_service_repo=Depends(get_repository(WorkspaceServiceRepository)) ) -> OperationInResponse: operation = await send_custom_action_message( resource=workspace_service, resource_repo=workspace_service_repo, custom_action=action, resource_type=ResourceType.WorkspaceService, operations_repo=operations_repo, resource_template_repo=resource_template_repo, user=user) response.headers["Location"] = construct_location_header(operation) return OperationInResponse(operation=operation)
async def update_cleaning_by_id( id: int = Path(..., ge=1, title="The ID of the cleaning to update."), cleaning_update: CleaningUpdate = Body(..., embed=True), cleanings_repo: CleaningsRepository = Depends( get_repository(CleaningsRepository)), ) -> CleaningPublic: updated_cleaning = await cleanings_repo.update_cleaning( id=id, cleaning_update=cleaning_update) if not updated_cleaning: raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="No cleaning found with that id.") return updated_cleaning
async def delete_cleaning_by_id( id: int = Path(..., ge=1, title="The ID of the cleaning to delete."), cleanings_repo: CleaningsRepository = Depends( get_repository(CleaningsRepository)), ) -> int: deleted_id = await cleanings_repo.delete_cleaning_by_id(id=id) if not deleted_id: raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="No cleaning found with that id.") return deleted_id
async def get_current_shared_service_template_by_name( shared_service_template_name: str, template_repo=Depends(get_repository(ResourceTemplateRepository)) ) -> SharedServiceTemplateInResponse: try: template = get_current_template_by_name(shared_service_template_name, template_repo, ResourceType.SharedService) return parse_obj_as(SharedServiceTemplateInResponse, template) except EntityDoesNotExist: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=strings.SHARED_SERVICE_TEMPLATE_DOES_NOT_EXIST)
async def get_user_from_token( *, token: str = Depends(oauth2_scheme), user_repo: UsersRepository = Depends(get_repository(UsersRepository)), ) -> Optional[UserInDB]: try: username = auth_service.get_username_from_token( token=token, secret_key=str(SECRET_KEY)) user = await user_repo.get_user_by_username(username=username) except Exception as e: raise e return user
async def invoke_action_on_user_resource( response: Response, action: str, user_resource=Depends(get_user_resource_by_id_from_path), workspace_service=Depends(get_workspace_service_by_id_from_path), resource_template_repo=Depends(get_repository(ResourceTemplateRepository)), user_resource_repo=Depends(get_repository(UserResourceRepository)), operations_repo=Depends(get_repository(OperationRepository)), user=Depends(get_current_workspace_owner_or_researcher_user) ) -> OperationInResponse: validate_user_is_workspace_owner_or_resource_owner(user, user_resource) operation = await send_custom_action_message( resource=user_resource, resource_repo=user_resource_repo, custom_action=action, resource_type=ResourceType.UserResource, operations_repo=operations_repo, resource_template_repo=resource_template_repo, user=user, parent_service_name=workspace_service.templateName) response.headers["Location"] = construct_location_header(operation) return OperationInResponse(operation=operation)