Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[테스트 데이터 생성기] CH 03. CLIP 02. 뷰 만들기: 내 스키마 페이지 #61

Merged
merged 6 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import uno.fastcampus.testdata.domain.constant.ExportFileType;
import uno.fastcampus.testdata.domain.constant.MockDataType;
import uno.fastcampus.testdata.dto.request.TableSchemaExportRequest;
import uno.fastcampus.testdata.dto.request.TableSchemaRequest;
import uno.fastcampus.testdata.dto.response.SchemaFieldResponse;
import uno.fastcampus.testdata.dto.response.SimpleTableSchemaResponse;
import uno.fastcampus.testdata.dto.response.TableSchemaResponse;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;

Expand All @@ -28,8 +31,11 @@ public class TableSchemaController {
private final ObjectMapper mapper;

@GetMapping("/table-schema")
public String tableSchema(Model model) {
var tableSchema = defaultTableSchema();
public String tableSchema(
@RequestParam(required = false) String schemaName,
Model model
) {
var tableSchema = defaultTableSchema(schemaName);

model.addAttribute("tableSchema", tableSchema);
model.addAttribute("mockDataTypes", MockDataType.toObjects());
Expand All @@ -51,13 +57,17 @@ public String createOrUpdateTableSchema(
}

@GetMapping("/table-schema/my-schemas")
public String mySchemas() {
public String mySchemas(Model model) {
var tableSchemas = mySampleSchemas();

model.addAttribute("tableSchemas", tableSchemas);

return "my-schemas";
}

@PostMapping("/table-schema/my-schemas/{schemaName}")
public String deleteMySchema(@PathVariable String schemaName) {
return "redirect:/my-schemas";
return "redirect:/table-schema/my-schemas";
}

@GetMapping("/table-schema/export")
Expand All @@ -69,18 +79,27 @@ public ResponseEntity<String> exportTableSchema(TableSchemaExportRequest tableSc
}


private TableSchemaResponse defaultTableSchema() {
private TableSchemaResponse defaultTableSchema(String schemaName) {
return new TableSchemaResponse(
"schema_name",
schemaName != null ? schemaName : "schema_name",
"Uno",
List.of(
new SchemaFieldResponse("fieldName1", MockDataType.STRING, 1, 0, null, null),
new SchemaFieldResponse("fieldName2", MockDataType.NUMBER, 2, 10, null, null),
new SchemaFieldResponse("fieldName3", MockDataType.NAME, 3, 20, null, null)
new SchemaFieldResponse("id", MockDataType.ROW_NUMBER, 1, 0, null, null),
new SchemaFieldResponse("name", MockDataType.NAME, 2, 10, null, null),
new SchemaFieldResponse("age", MockDataType.NUMBER, 3, 20, null, null),
new SchemaFieldResponse("my_car", MockDataType.CAR, 4, 50, null, null)
)
);
}

private static List<SimpleTableSchemaResponse> mySampleSchemas() {
return List.of(
new SimpleTableSchemaResponse("schema_name1", "Uno", LocalDate.of(2024, 1, 1).atStartOfDay()),
new SimpleTableSchemaResponse("schema_name2", "Uno", LocalDate.of(2024, 2, 2).atStartOfDay()),
new SimpleTableSchemaResponse("schema_name3", "Uno", LocalDate.of(2024, 3, 3).atStartOfDay())
);
}

private String json(Object object) {
try {
return mapper.writeValueAsString(object);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package uno.fastcampus.testdata.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class UserAccountController {

@GetMapping("/my-account")
public String myAccount() {
public String myAccount(Model model) {
model.addAttribute("nickname", "홍길동");
model.addAttribute("email", "홍길동@email.com");

return "my-account";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

import uno.fastcampus.testdata.dto.TableSchemaDto;

import java.time.LocalDateTime;

public record SimpleTableSchemaResponse(
String schemaName,
String userId
String userId,
LocalDateTime modifiedAt
) {

public static SimpleTableSchemaResponse fromDto(TableSchemaDto dto) {
return new SimpleTableSchemaResponse(dto.schemaName(), dto.userId());
return new SimpleTableSchemaResponse(dto.schemaName(), dto.userId(), dto.modifiedAt());
}

}
53 changes: 49 additions & 4 deletions src/main/resources/templates/my-account.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,55 @@
<!DOCTYPE html>
<html lang="ko">
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>내 정보</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>테스트 데이터 생성기 - 내 정보</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css">
</head>
<body>
내 정보 페이지
<body class="bg-gray-100 text-gray-900 flex flex-col min-h-screen">

<header class="bg-white shadow">
<div class="container mx-auto px-4 py-6 flex justify-between items-center">
<a th:href="@{/}" class="text-2xl font-bold text-sky-600">테스트 데이터 생성기</a>
<nav>
<a th:href="@{/table-schema/my-schemas}" class="text-gray-600 hover:text-sky-600 mx-2">내 스키마</a>
<a th:href="@{/my-account}" class="text-gray-600 hover:text-sky-600 mx-2">내 정보</a>
<a th:href="'#'" class="text-gray-600 hover:text-sky-600 mx-2">로그인</a>
</nav>
</div>
</header>

<main class="container mx-auto px-4 py-8 flex-grow">
<section class="mb-8">
<h1 class="text-4xl font-bold text-gray-600 mb-6 text-left">내 정보</h1>
</section>

<section class="bg-white shadow rounded-lg p-6 mb-6">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">닉네임</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">이메일</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-600" th:text="${nickname}">-</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600" th:text="${email}">-</td>
</tr>
</tbody>
</table>
</div>
</section>
</main>

<footer class="bg-white shadow w-full mt-auto">
<div class="container mx-auto px-4 py-6 text-center text-gray-600">
© 2024 FastCampus, Inc
</div>
</footer>

</body>
</html>
88 changes: 84 additions & 4 deletions src/main/resources/templates/my-schemas.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,90 @@
<!DOCTYPE html>
<html lang="ko">
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>내 테이블 스키마 목록</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>테스트 데이터 생성기 - 내 스키마 목록</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/list.js/2.3.1/list.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css">
<style>
.sort.asc::after {
content: "\0025BC";
padding-left: 3px;
}
.sort.desc::after {
content: "\0025B2";
padding-left: 3px;
}
</style>
</head>
<body>
내 테이블 스키마 목록 페이지
<body class="bg-gray-100 text-gray-900 flex flex-col min-h-screen">

<header class="bg-white shadow">
<div class="container mx-auto px-4 py-6 flex justify-between items-center">
<a th:href="@{/}" class="text-2xl font-bold text-sky-600">테스트 데이터 생성기</a>
<nav>
<a th:href="@{/table-schema/my-schemas}" class="text-gray-600 hover:text-sky-600 mx-2">내 스키마</a>
<a th:href="@{/my-account}" class="text-gray-600 hover:text-sky-600 mx-2">내 정보</a>
<a th:href="'#'" class="text-gray-600 hover:text-sky-600 mx-2">로그인</a>
</nav>
</div>
</header>

<main class="container mx-auto px-4 py-8 flex-grow">
<section class="mb-8">
<h1 class="text-4xl font-bold text-gray-600 mb-6 text-left">내 스키마</h1>
<div class="flex justify-start space-x-4">
<a th:href="@{/table-schema}" class="bg-sky-600 text-white py-2 px-4 rounded hover:bg-sky-700">스키마 만들기</a>
</div>
</section>

<section id="my-schemas" class="bg-white shadow rounded-lg p-6 mb-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold text-gray-700">스키마 목록</h2>
<div class="flex space-x-2">
<input type="search" placeholder="스키마 이름 검색.." class="search border-gray-300 rounded-md shadow-sm px-2 py-1">
<button data-sort="schema-name" class="sort bg-sky-600 text-white py-1 px-4 rounded hover:bg-sky-700">이름순 정렬</button>
</div>
</div>

<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-7/12">Name</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-2/12">Last Modified</th>
<th class="px-6 py-3 w-1/12 text-right">
<i class="fas fa-trash-alt text-gray-500"></i>
</th>
</tr>
</thead>
<tbody class="list bg-white divide-y divide-gray-200">
<tr th:each="tableSchema : ${tableSchemas}">
<td class="schema-name px-6 py-4 whitespace-nowrap text-sm font-medium text-blue-600 hover:underline">
<a th:href="@{/table-schema(schemaName=${tableSchema.schemaName})}" th:text="${tableSchema.schemaName}">sample_schema</a>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500" th:text="${tableSchema.modifiedAt}">-</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<form th:action="@{/table-schema/my-schemas/{schemaName}(schemaName=${tableSchema.schemaName})}" method="post" class="inline">
<button type="submit" class="text-red-600"><i class="fas fa-trash-alt"></i></button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</section>
</main>

<footer class="bg-white shadow w-full mt-auto">
<div class="container mx-auto px-4 py-6 text-center text-gray-600">
© 2024 FastCampus, Inc
</div>
</footer>

<script>
const mySchema = new List('my-schemas', { valueNames: [ 'schema-name' ]});
</script>
</body>
</html>
4 changes: 2 additions & 2 deletions src/main/resources/templates/table-schema.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
<div class="container mx-auto px-4 py-6 flex justify-between items-center">
<a th:href="@{/}" class="text-2xl font-bold text-sky-600">테스트 데이터 생성기</a>
<nav>
<a th:href="'#'" class="text-gray-600 hover:text-sky-600 mx-2">내 스키마</a>
<a th:href="'#'" class="text-gray-600 hover:text-sky-600 mx-2">내 정보</a>
<a th:href="@{/table-schema/my-schemas}" class="text-gray-600 hover:text-sky-600 mx-2">내 스키마</a>
<a th:href="@{/my-account}" class="text-gray-600 hover:text-sky-600 mx-2">내 정보</a>
<a th:href="'#'" class="text-gray-600 hover:text-sky-600 mx-2">로그인</a>
</nav>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.util.List;

import static org.hamcrest.Matchers.containsString;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
Expand Down Expand Up @@ -48,6 +49,27 @@ void givenNothing_whenRequesting_thenShowsTableSchemaView() throws Exception {
.andExpect(view().name("table-schema"));
}

@DisplayName("[GET] 테이블 스키마 조회, 로그인 + 특정 테이블 스키마 (정상)")
@Test
void givenAuthenticatedUserAndSchemaName_whenRequesting_thenShowsTableSchemaView() throws Exception {
// Given
var schemaName = "test_schema";

// When & Then
mvc.perform(
get("/table-schema")
.queryParam("schemaName", schemaName)
)
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_HTML))
.andExpect(model().attributeExists("tableSchema"))
// .andExpect(model().attribute("tableSchema", hasProperty("schemaName", is(schemaName)))) // dto가 record여서 불가능한 방식
.andExpect(model().attributeExists("mockDataTypes"))
.andExpect(model().attributeExists("fileTypes"))
.andExpect(content().string(containsString(schemaName))) // html 전체 검사하므로 정확하지 않은 테스트 방식
.andExpect(view().name("table-schema"));
}

@DisplayName("[POST] 테이블 스키마 생성, 변경 (정상)")
@Test
void givenTableSchemaRequest_whenCreatingOrUpdating_thenRedirectsToTableSchemaView() throws Exception {
Expand Down Expand Up @@ -83,6 +105,7 @@ void givenAuthenticatedUser_whenRequesting_thenShowsMySchemaView() throws Except
mvc.perform(get("/table-schema/my-schemas"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_HTML))
.andExpect(model().attributeExists("tableSchemas"))
.andExpect(view().name("my-schemas"));
}

Expand All @@ -98,7 +121,7 @@ void givenAuthenticatedUserAndSchemaName_whenDeleting_thenRedirectsToTableSchema
.with(csrf())
)
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/my-schemas"));
.andExpect(redirectedUrl("/table-schema/my-schemas"));
}

@DisplayName("[GET] 테이블 스키마 파일 다운로드 (정상)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ void givenAuthenticatedUser_whenRequesting_thenShowsMyAccountView() throws Excep
mvc.perform(get("/my-account"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_HTML))
.andExpect(model().attributeExists("nickname"))
.andExpect(model().attributeExists("email"))
.andExpect(view().name("my-account"));
}

Expand Down