Skip to content

Commit

Permalink
refactor: add request model for add todo
Browse files Browse the repository at this point in the history
  • Loading branch information
eoaksnes committed Nov 15, 2022
1 parent 3b0e0bb commit d055c27
Show file tree
Hide file tree
Showing 11 changed files with 77 additions and 29 deletions.
2 changes: 2 additions & 0 deletions api/src/features/todo/shared_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from entities.TodoItem import TodoItem


# An alternative solution is to use Pydantic BaseModel
# and to duplicate all the fields except user_id
@filter_fields(name="TodoItem")
class TodoItemResponseModel(TodoItem):
class Config:
Expand Down
5 changes: 3 additions & 2 deletions api/src/features/todo/todo_feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from data_providers.repository_interfaces.TodoRepositoryInterface import (
TodoRepositoryInterface,
)
from features.todo.use_cases.add_todo import AddTodoRequest
from features.todo.use_cases.delete_todo_by_id import DeleteTodoResponse

from .shared_models import TodoItemResponseModel
Expand All @@ -25,11 +26,11 @@
@router.post("", operation_id="create", response_model=TodoItemResponseModel)
@create_response(JSONResponse)
def add_todo(
title: str,
data: AddTodoRequest,
user: User = Depends(auth_with_jwt),
todo_repository: TodoRepositoryInterface = Depends(get_todo_repository),
):
return add_todo_use_case(title=title, user_id=user.user_id, todo_repository=todo_repository).dict()
return add_todo_use_case(data=data, user_id=user.user_id, todo_repository=todo_repository).dict()


@router.get("/{id}", operation_id="get_by_id", response_model=TodoItemResponseModel)
Expand Down
12 changes: 6 additions & 6 deletions api/src/features/todo/use_cases/add_todo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@
from data_providers.repository_interfaces.TodoRepositoryInterface import (
TodoRepositoryInterface,
)
from entities.TodoItem import TodoItem, title_field
from entities.TodoItem import TodoItem
from features.todo.shared_models import TodoItemResponseModel


# An alternative solution is to use Pydantic BaseModel
# and to duplicate the title and is_completed fields
@filter_fields(name="AddTodo")
class AddTodoRequestModel(TodoItem):
class AddTodoRequest(TodoItem):
class Config:
include = ["title"]


def add_todo_use_case(
user_id: str,
todo_repository: TodoRepositoryInterface,
title: str = title_field,
data: AddTodoRequest, user_id: str, todo_repository: TodoRepositoryInterface
) -> TodoItemResponseModel:
todo_item = TodoItem(id=str(uuid.uuid4()), title=title, user_id=user_id)
todo_item = TodoItem(id=str(uuid.uuid4()), title=data.title, user_id=user_id)
todo_repository.create(todo_item)
return TodoItemResponseModel.parse_obj(todo_item)
2 changes: 2 additions & 0 deletions api/src/features/todo/use_cases/update_todo.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from features.todo.shared_models import TodoItemResponseModel


# An alternative solution is to use Pydantic BaseModel
# and to duplicate the title and is_completed fields
@filter_fields(name="UpdateTodo")
class UpdateTodoRequest(TodoItem):
class Config:
Expand Down
7 changes: 7 additions & 0 deletions api/src/tests/unit/common/test_entity_mapper.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from dataclasses import dataclass

from pydantic import BaseModel

from common.entity_mapper import filter_fields
Expand All @@ -8,6 +10,11 @@ class MyEntity(BaseModel):
b: str


@dataclass
class InventoryItem:
a: int


def test_include_fields():
@filter_fields()
class MyModel(MyEntity):
Expand Down
8 changes: 5 additions & 3 deletions api/src/tests/unit/features/todo/use_cases/test_add_todo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
from data_providers.repository_interfaces.TodoRepositoryInterface import (
TodoRepositoryInterface,
)
from features.todo.use_cases.add_todo import add_todo_use_case
from features.todo.use_cases.add_todo import AddTodoRequest, add_todo_use_case


def test_add_with_valid_title_should_return_todo(todo_repository: TodoRepositoryInterface):
title = "new todo"
result = add_todo_use_case(title=title, user_id="xyz", todo_repository=todo_repository)
data = AddTodoRequest(title=title)
result = add_todo_use_case(data=data, user_id="xyz", todo_repository=todo_repository)
assert result.title == title


def test_add_with_empty_title_should_throw_validation_error(todo_repository: TodoRepositoryInterface):
with pytest.raises(ValidationError):
title = ""
add_todo_use_case(title=title, user_id="xyz", todo_repository=todo_repository)
data = AddTodoRequest(title=title)
add_todo_use_case(data=data, user_id="xyz", todo_repository=todo_repository)
1 change: 1 addition & 0 deletions web/src/api/generated/.openapi-generator/FILES
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ common.ts
configuration.ts
git_push.sh
index.ts
models/add-todo.ts
models/delete-todo-response.ts
models/error-response.ts
models/index.ts
Expand Down
35 changes: 18 additions & 17 deletions web/src/api/generated/api/todos-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObj
// @ts-ignore
import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base';
// @ts-ignore
import { AddTodo } from '../models';
// @ts-ignore
import { DeleteTodoResponse } from '../models';
// @ts-ignore
import { ErrorResponse } from '../models';
Expand All @@ -37,13 +39,13 @@ export const TodosApiAxiosParamCreator = function (configuration?: Configuration
/**
*
* @summary Add Todo
* @param {string} title
* @param {AddTodo} addTodo
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
create: async (title: string, options: any = {}): Promise<RequestArgs> => {
// verify required parameter 'title' is not null or undefined
assertParamExists('create', 'title', title)
create: async (addTodo: AddTodo, options: any = {}): Promise<RequestArgs> => {
// verify required parameter 'addTodo' is not null or undefined
assertParamExists('create', 'addTodo', addTodo)
const localVarPath = `/todos`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
Expand All @@ -60,15 +62,14 @@ export const TodosApiAxiosParamCreator = function (configuration?: Configuration
// oauth required
await setOAuthToObject(localVarHeaderParameter, "OAuth2AuthorizationCodeBearer", [], configuration)

if (title !== undefined) {
localVarQueryParameter['title'] = title;
}



localVarHeaderParameter['Content-Type'] = 'application/json';

setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
localVarRequestOptions.data = serializeDataIfNeeded(addTodo, localVarRequestOptions, configuration)

return {
url: toPathString(localVarUrlObj),
Expand Down Expand Up @@ -242,12 +243,12 @@ export const TodosApiFp = function(configuration?: Configuration) {
/**
*
* @summary Add Todo
* @param {string} title
* @param {AddTodo} addTodo
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async create(title: string, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<TodoItem>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.create(title, options);
async create(addTodo: AddTodo, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<TodoItem>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.create(addTodo, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
Expand Down Expand Up @@ -307,12 +308,12 @@ export const TodosApiFactory = function (configuration?: Configuration, basePath
/**
*
* @summary Add Todo
* @param {string} title
* @param {AddTodo} addTodo
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
create(title: string, options?: any): AxiosPromise<TodoItem> {
return localVarFp.create(title, options).then((request) => request(axios, basePath));
create(addTodo: AddTodo, options?: any): AxiosPromise<TodoItem> {
return localVarFp.create(addTodo, options).then((request) => request(axios, basePath));
},
/**
*
Expand Down Expand Up @@ -367,13 +368,13 @@ export class TodosApi extends BaseAPI {
/**
*
* @summary Add Todo
* @param {string} title
* @param {AddTodo} addTodo
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof TodosApi
*/
public create(title: string, options?: any) {
return TodosApiFp(this.configuration).create(title, options).then((request) => request(this.axios, this.basePath));
public create(addTodo: AddTodo, options?: any) {
return TodosApiFp(this.configuration).create(addTodo, options).then((request) => request(this.axios, this.basePath));
}

/**
Expand Down
31 changes: 31 additions & 0 deletions web/src/api/generated/models/add-todo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* tslint:disable */
/* eslint-disable */
/**
* Template FastAPI React
* ### Description A RESTful API for handling todo items. Anyone in Equinor are authorized to use the API. * Click **Authorize** to login and start testing. ### Resources * [Docs](https://equinor.github.io/template-fastapi-react/) * [Github](https://github.com/equinor/template-fastapi-react) For questions about usage or expanding the API, create issue on Github or see docs.
*
* The version of the OpenAPI document: 1.2.1
*
*
* 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 AddTodo
*/
export interface AddTodo {
/**
*
* @type {string}
* @memberof AddTodo
*/
title: string;
}


1 change: 1 addition & 0 deletions web/src/api/generated/models/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './add-todo';
export * from './delete-todo-response';
export * from './error-response';
export * from './todo-item';
Expand Down
2 changes: 1 addition & 1 deletion web/src/hooks/useTodos.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const useTodos = (): {
const addItem = (title: string) => {
setLoading(true)
todoAPI
.create(title)
.create({ title: title })
.then((response) => {
const item: TodoItem = response.data
setTodos([...todos, item])
Expand Down

0 comments on commit d055c27

Please sign in to comment.