From 52a7d142252508c472c701581b76a11462c60cae Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Mon, 27 Nov 2023 15:12:50 +0800 Subject: [PATCH] Feature/save request (#842) * New route for saving and getting successfully dispatched requests Signed-off-by: Aaron Chong * Announce failed to dispatch instead of create task Signed-off-by: Aaron Chong * Generate api-client Signed-off-by: Aaron Chong * Use api task request instead of custom ttm pydantic model Signed-off-by: Aaron Chong * Revert alert text change Signed-off-by: Aaron Chong --------- Signed-off-by: Aaron Chong --- packages/api-client/lib/openapi/api.ts | 84 +++++++++++++++++++ packages/api-client/lib/version.ts | 2 +- packages/api-client/schema/index.ts | 30 +++++++ .../models/tortoise_models/__init__.py | 1 + .../models/tortoise_models/tasks.py | 5 ++ .../api_server/repositories/tasks.py | 13 +++ .../api_server/routes/tasks/tasks.py | 17 +++- .../api_server/routes/tasks/test_tasks.py | 13 +++ 8 files changed, 161 insertions(+), 4 deletions(-) diff --git a/packages/api-client/lib/openapi/api.ts b/packages/api-client/lib/openapi/api.ts index 4d1f47daa..64a1ed2a4 100644 --- a/packages/api-client/lib/openapi/api.ts +++ b/packages/api-client/lib/openapi/api.ts @@ -7678,6 +7678,47 @@ export const TasksApiAxiosParamCreator = function (configuration?: Configuration options: localVarRequestOptions, }; }, + /** + * + * @summary Get Task Request + * @param {string} taskId task_id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getTaskRequestTasksTaskIdRequestGet: async ( + taskId: string, + options: AxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'taskId' is not null or undefined + assertParamExists('getTaskRequestTasksTaskIdRequestGet', 'taskId', taskId); + const localVarPath = `/tasks/{task_id}/request`.replace( + `{${'task_id'}}`, + encodeURIComponent(String(taskId)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * Available in socket.io * @summary Get Task State @@ -8656,6 +8697,23 @@ export const TasksApiFp = function (configuration?: Configuration) { ); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @summary Get Task Request + * @param {string} taskId task_id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getTaskRequestTasksTaskIdRequestGet( + taskId: string, + options?: AxiosRequestConfig, + ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getTaskRequestTasksTaskIdRequestGet( + taskId, + options, + ); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * Available in socket.io * @summary Get Task State @@ -9114,6 +9172,18 @@ export const TasksApiFactory = function ( .getTaskLogTasksTaskIdLogGet(taskId, between, options) .then((request) => request(axios, basePath)); }, + /** + * + * @summary Get Task Request + * @param {string} taskId task_id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getTaskRequestTasksTaskIdRequestGet(taskId: string, options?: any): AxiosPromise { + return localVarFp + .getTaskRequestTasksTaskIdRequestGet(taskId, options) + .then((request) => request(axios, basePath)); + }, /** * Available in socket.io * @summary Get Task State @@ -9518,6 +9588,20 @@ export class TasksApi extends BaseAPI { .then((request) => request(this.axios, this.basePath)); } + /** + * + * @summary Get Task Request + * @param {string} taskId task_id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof TasksApi + */ + public getTaskRequestTasksTaskIdRequestGet(taskId: string, options?: AxiosRequestConfig) { + return TasksApiFp(this.configuration) + .getTaskRequestTasksTaskIdRequestGet(taskId, options) + .then((request) => request(this.axios, this.basePath)); + } + /** * Available in socket.io * @summary Get Task State diff --git a/packages/api-client/lib/version.ts b/packages/api-client/lib/version.ts index 115944cbc..b14e3132a 100644 --- a/packages/api-client/lib/version.ts +++ b/packages/api-client/lib/version.ts @@ -3,6 +3,6 @@ import { version as rmfModelVer } from 'rmf-models'; export const version = { rmfModels: rmfModelVer, - rmfServer: '60cfa28fb26433cb33254ba1522fc00466fe7146', + rmfServer: 'd6099b09fb9f13e06e029014f35a8ff1c4810abf', openapiGenerator: '6.2.1', }; diff --git a/packages/api-client/schema/index.ts b/packages/api-client/schema/index.ts index 259fbfb4d..8f8820739 100644 --- a/packages/api-client/schema/index.ts +++ b/packages/api-client/schema/index.ts @@ -538,6 +538,36 @@ export default { }, }, }, + '/tasks/{task_id}/request': { + get: { + tags: ['Tasks'], + summary: 'Get Task Request', + operationId: 'get_task_request_tasks__task_id__request_get', + parameters: [ + { + description: 'task_id', + required: true, + schema: { title: 'Task Id', type: 'string', description: 'task_id' }, + name: 'task_id', + in: 'path', + }, + ], + responses: { + '200': { + description: 'Successful Response', + content: { + 'application/json': { schema: { $ref: '#/components/schemas/TaskRequest' } }, + }, + }, + '422': { + description: 'Validation Error', + content: { + 'application/json': { schema: { $ref: '#/components/schemas/HTTPValidationError' } }, + }, + }, + }, + }, + }, '/tasks': { get: { tags: ['Tasks'], diff --git a/packages/api-server/api_server/models/tortoise_models/__init__.py b/packages/api-server/api_server/models/tortoise_models/__init__.py index 8b1faae37..279d8120b 100644 --- a/packages/api-server/api_server/models/tortoise_models/__init__.py +++ b/packages/api-server/api_server/models/tortoise_models/__init__.py @@ -26,6 +26,7 @@ TaskEventLogPhasesLog, TaskFavorite, TaskFavoritePydantic, + TaskRequest, TaskState, ) from .user import * diff --git a/packages/api-server/api_server/models/tortoise_models/tasks.py b/packages/api-server/api_server/models/tortoise_models/tasks.py index fc76a75eb..20be6d63b 100644 --- a/packages/api-server/api_server/models/tortoise_models/tasks.py +++ b/packages/api-server/api_server/models/tortoise_models/tasks.py @@ -12,6 +12,11 @@ from .log import LogMixin +class TaskRequest(Model): + id_ = CharField(255, pk=True, source_field="id") + request = JSONField() + + class TaskState(Model): id_ = CharField(255, pk=True, source_field="id") data = JSONField() diff --git a/packages/api-server/api_server/repositories/tasks.py b/packages/api-server/api_server/repositories/tasks.py index 6e9eae2c9..281a653f2 100644 --- a/packages/api-server/api_server/repositories/tasks.py +++ b/packages/api-server/api_server/repositories/tasks.py @@ -15,12 +15,14 @@ Pagination, Phases, TaskEventLog, + TaskRequest, TaskState, User, ) from api_server.models import tortoise_models as ttm from api_server.models.rmf_api.log_entry import Tier from api_server.models.rmf_api.task_state import Category, Id, Phase +from api_server.models.tortoise_models import TaskRequest as DbTaskRequest from api_server.models.tortoise_models import TaskState as DbTaskState from api_server.query import add_pagination from api_server.rmf_io import task_events @@ -30,6 +32,17 @@ class TaskRepository: def __init__(self, user: User): self.user = user + async def save_task_request(self, task_id: str, task_request: TaskRequest) -> None: + await DbTaskRequest.update_or_create( + {"request": task_request.json()}, id_=task_id + ) + + async def get_task_request(self, task_id: str) -> Optional[TaskRequest]: + result = await DbTaskRequest.get_or_none(id_=task_id) + if result is None: + return None + return TaskRequest(**result.request) + async def save_task_state(self, task_state: TaskState) -> None: await DbTaskState.update_or_create( { diff --git a/packages/api-server/api_server/routes/tasks/tasks.py b/packages/api-server/api_server/routes/tasks/tasks.py index f5221c3ba..a8636ef45 100644 --- a/packages/api-server/api_server/routes/tasks/tasks.py +++ b/packages/api-server/api_server/routes/tasks/tasks.py @@ -21,6 +21,17 @@ router = FastIORouter(tags=["Tasks"]) +@router.get("/{task_id}/request", response_model=mdl.TaskRequest) +async def get_task_request( + task_repo: TaskRepository = Depends(task_repo_dep), + task_id: str = Path(..., description="task_id"), +): + result = await task_repo.get_task_request(task_id) + if result is None: + raise HTTPException(status_code=404) + return result + + @router.get("", response_model=List[mdl.TaskState]) async def query_task_states( task_repo: TaskRepository = Depends(task_repo_dep), @@ -144,9 +155,9 @@ async def post_dispatch_task( ) if not resp.__root__.success: return RawJSONResponse(resp.json(), 400) - await task_repo.save_task_state( - cast(mdl.TaskDispatchResponseItem, resp.__root__).state - ) + task_state = cast(mdl.TaskDispatchResponseItem, resp.__root__).state + await task_repo.save_task_state(task_state) + await task_repo.save_task_request(task_state.booking.id, request.request) return resp.__root__ diff --git a/packages/api-server/api_server/routes/tasks/test_tasks.py b/packages/api-server/api_server/routes/tasks/test_tasks.py index a9a542ab6..5dee13e0a 100644 --- a/packages/api-server/api_server/routes/tasks/test_tasks.py +++ b/packages/api-server/api_server/routes/tasks/test_tasks.py @@ -254,6 +254,19 @@ def test_success(self): self.assertEqual(200, resp.status_code, resp.content) self.assertEqual(task_id, resp.json()["booking"]["id"]) + def test_task_request_exist(self): + task_id = str(uuid4()) + with patch.object(tasks_service(), "call") as mock: + mock.return_value = f'{{ "success": true, "state": {{ "booking": {{ "id": "{task_id}" }} }} }}' + resp = self.post_task_request() + self.assertEqual(200, resp.status_code, resp.content) + + # check that the task request is in the database + resp = self.client.get(f"/tasks/{task_id}/request") + self.assertEqual(200, resp.status_code, resp.content) + self.assertEqual("test", resp.json()["category"]) + self.assertEqual("description", resp.json()["description"]) + def test_fail_with_multiple_errors(self): # fails with multiple errors with patch.object(tasks_service(), "call") as mock: