From 23dbecb4d177ee50e29972baa6c3027cd1786875 Mon Sep 17 00:00:00 2001 From: vovastelmashchuk Date: Tue, 13 Feb 2024 22:30:14 +0200 Subject: [PATCH] Implement create new project by rest api --- backend/api.http | 33 ++++++++++++ backend/build.gradle.kts | 2 +- backend/src/main/java/com/nestapp/Main.kt | 3 +- .../java/com/nestapp/nest_api/NestRestApi.kt | 5 +- .../{ProjectRest.kt => ProjectController.kt} | 15 +++--- .../nestapp/projects/ProjectsRepository.kt | 10 ++++ .../AllProjects.kt} | 4 +- .../nestapp/projects/rest/CreateProject.kt | 51 +++++++++++++++++++ .../projects/{ => rest}/ProjectDetails.kt | 6 ++- nest2dvue/src/views/ProjectList.vue | 4 +- 10 files changed, 119 insertions(+), 14 deletions(-) rename backend/src/main/java/com/nestapp/projects/{ProjectRest.kt => ProjectController.kt} (88%) rename backend/src/main/java/com/nestapp/projects/{AllProjectsResponse.kt => rest/AllProjects.kt} (94%) create mode 100644 backend/src/main/java/com/nestapp/projects/rest/CreateProject.kt rename backend/src/main/java/com/nestapp/projects/{ => rest}/ProjectDetails.kt (90%) diff --git a/backend/api.http b/backend/api.http index b788929..fcc11d6 100644 --- a/backend/api.http +++ b/backend/api.http @@ -62,6 +62,23 @@ Content-Type: application/json "plate_height": "1000" } +### nest big box v2 + +POST localhost:8080/api/nest +Content-Type: application/json + +{ + "project_id": "big_box_v2", + "file_counts": { + "big_box_all+0": 1, + "big_box_all+1": 1, + "big_box_all+2": 1 + }, + "plate_width": "1000", + "plate_height": "1000" +} + + ### Get all projects GET localhost:8080/api/projects @@ -74,4 +91,20 @@ Content-Disposition: form-data; name="file"; filename="big_box_all.dxf" < test-data/big_box/big_box.dxf +### Add file to project big box V2 +POST nest2d.online/api/project/big_box_v2/add_file +Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW + +------WebKitFormBoundary7MA4YWxkTrZu0gW +Content-Disposition: form-data; name="file"; filename="short_side.dxf" + +< test-data/big_box_v2/short_side.dxf + +### Create the project +POST nest2d.online/api/project +Content-Type: application/json + +{ + "name": "Big Box V2" +} diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index bc36444..f71da99 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -6,7 +6,7 @@ plugins { } group = "com.nestapp" -version = "0.3.2" +version = "0.3.3" application { mainClass.set("com.nestapp.Main") diff --git a/backend/src/main/java/com/nestapp/Main.kt b/backend/src/main/java/com/nestapp/Main.kt index 6f6b57c..8824435 100644 --- a/backend/src/main/java/com/nestapp/Main.kt +++ b/backend/src/main/java/com/nestapp/Main.kt @@ -79,14 +79,13 @@ internal object Main { route("/api") { projectsRest( configuration, - File("mount/projects"), projectsRepository, SvgFromDxf() ) nestRestApi( + configuration, projectsRepository, nestedRepository, - File("mount/projects") ) get("/version") { diff --git a/backend/src/main/java/com/nestapp/nest_api/NestRestApi.kt b/backend/src/main/java/com/nestapp/nest_api/NestRestApi.kt index 1e39209..f262ca2 100644 --- a/backend/src/main/java/com/nestapp/nest_api/NestRestApi.kt +++ b/backend/src/main/java/com/nestapp/nest_api/NestRestApi.kt @@ -1,5 +1,6 @@ package com.nestapp.nest_api +import com.nestapp.Configuration import com.nestapp.files.dxf.DxfPartPlacement import com.nestapp.files.dxf.DxfApi import com.nestapp.projects.FileId @@ -23,9 +24,9 @@ import java.awt.Rectangle import java.io.File fun Route.nestRestApi( + configuration: Configuration, projectsRepository: ProjectsRepository, nestedRepository: NestedRepository, - projectsFolder: File, ) { post("/nest") { val nestInput = call.receive() @@ -35,7 +36,7 @@ fun Route.nestRestApi( } val id = nestedRepository.getNextId() - val result = nest(id, nestInput, projectsRepository, projectsFolder) + val result = nest(id, nestInput, projectsRepository, configuration.projectsFolder) nestedRepository.addNested(result) val nestedOutput = NestedOutput(id = id) diff --git a/backend/src/main/java/com/nestapp/projects/ProjectRest.kt b/backend/src/main/java/com/nestapp/projects/ProjectController.kt similarity index 88% rename from backend/src/main/java/com/nestapp/projects/ProjectRest.kt rename to backend/src/main/java/com/nestapp/projects/ProjectController.kt index 60dd304..4342669 100644 --- a/backend/src/main/java/com/nestapp/projects/ProjectRest.kt +++ b/backend/src/main/java/com/nestapp/projects/ProjectController.kt @@ -2,6 +2,9 @@ package com.nestapp.projects import com.nestapp.Configuration import com.nestapp.files.SvgFromDxf +import com.nestapp.projects.rest.allProjects +import com.nestapp.projects.rest.createProject +import com.nestapp.projects.rest.projectDetails import io.ktor.http.ContentDisposition import io.ktor.http.HttpHeaders import io.ktor.http.HttpStatusCode @@ -22,12 +25,12 @@ import java.io.File fun Route.projectsRest( configuration: Configuration, - projectsFolder: File, projectsRepository: ProjectsRepository, svgFromDxf: SvgFromDxf, ) { allProjects(configuration, projectsRepository) projectDetails(configuration, projectsRepository) + createProject(configuration, projectsRepository) get("/preview/{project_id}/{file_id}") { val project = project(projectsRepository) @@ -35,7 +38,7 @@ fun Route.projectsRest( val fileId = FileId(call.parameters["file_id"] ?: throw Exception("file_id not found")) val svgFileName = project.files[fileId]?.svgFile ?: throw Exception("file not found") - val svgFile = File(projectsFolder, "${project.id.value}/${fileId.value}/${svgFileName}") + val svgFile = File(configuration.projectsFolder, "${project.id.value}/${fileId.value}/${svgFileName}") call.response.header( HttpHeaders.ContentDisposition, @@ -47,17 +50,17 @@ fun Route.projectsRest( call.respondFile(svgFile) } - fileUploader(projectsRepository, projectsFolder, svgFromDxf) + fileUploader(configuration, projectsRepository, svgFromDxf) } private fun Route.fileUploader( + configuration: Configuration, projectsRepository: ProjectsRepository, - projectsFolder: File, svgFromDxf: SvgFromDxf, ) { post("/project/{project_id}/add_file") { val project = project(projectsRepository) - val projectFolder = File(projectsFolder, project.id.value) + val projectFolder = File(configuration.projectsFolder, project.id.value) val multipartData = call.receiveMultipart() multipartData.forEachPart { part -> @@ -75,7 +78,7 @@ private fun Route.fileUploader( File(fileFolder, fileName).writeBytes(fileBytes) val projectFile = addFileToProject( - projectsFolder = projectsFolder, + projectsFolder = configuration.projectsFolder, projectId = project.id, fileId = FileId(fileId), svgFromDxf = svgFromDxf, diff --git a/backend/src/main/java/com/nestapp/projects/ProjectsRepository.kt b/backend/src/main/java/com/nestapp/projects/ProjectsRepository.kt index ce801db..d2fcacb 100644 --- a/backend/src/main/java/com/nestapp/projects/ProjectsRepository.kt +++ b/backend/src/main/java/com/nestapp/projects/ProjectsRepository.kt @@ -41,6 +41,16 @@ class ProjectsRepository( return getProjectRoot().projects } + fun add(project: Project) { + val root = getProjectRoot() + val projects = root.projects.toMutableMap() + if (projects.containsKey(project.id)) { + throw IllegalArgumentException("Project with id ${project.id} already exists") + } + projects[project.id] = project + saveProjectRoot(root.copy(projects = projects.toMap())) + } + @OptIn(ExperimentalSerializationApi::class) @Synchronized private fun getProjectRoot(): ProjectsRoot { diff --git a/backend/src/main/java/com/nestapp/projects/AllProjectsResponse.kt b/backend/src/main/java/com/nestapp/projects/rest/AllProjects.kt similarity index 94% rename from backend/src/main/java/com/nestapp/projects/AllProjectsResponse.kt rename to backend/src/main/java/com/nestapp/projects/rest/AllProjects.kt index 56d16dd..a2c586b 100644 --- a/backend/src/main/java/com/nestapp/projects/AllProjectsResponse.kt +++ b/backend/src/main/java/com/nestapp/projects/rest/AllProjects.kt @@ -1,6 +1,8 @@ -package com.nestapp.projects +package com.nestapp.projects.rest import com.nestapp.Configuration +import com.nestapp.projects.ProjectId +import com.nestapp.projects.ProjectsRepository import io.ktor.http.ContentDisposition import io.ktor.http.HttpHeaders import io.ktor.http.HttpStatusCode diff --git a/backend/src/main/java/com/nestapp/projects/rest/CreateProject.kt b/backend/src/main/java/com/nestapp/projects/rest/CreateProject.kt new file mode 100644 index 0000000..8789d58 --- /dev/null +++ b/backend/src/main/java/com/nestapp/projects/rest/CreateProject.kt @@ -0,0 +1,51 @@ +package com.nestapp.projects.rest + +import com.nestapp.Configuration +import com.nestapp.projects.Project +import com.nestapp.projects.ProjectId +import com.nestapp.projects.ProjectsRepository +import io.ktor.http.HttpStatusCode +import io.ktor.server.application.call +import io.ktor.server.request.receive +import io.ktor.server.response.respond +import io.ktor.server.routing.Route +import io.ktor.server.routing.post +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import java.io.File +import java.util.Locale + +fun Route.createProject( + configuration: Configuration, + projectsRepository: ProjectsRepository, +) { + post("/project") { + val request = call.receive() + val project = Project( + id = createProjectId(request.name), + name = request.name, + files = emptyMap(), + ) + + projectsRepository.add(project) + + File(configuration.projectsFolder, project.id.value).mkdirs() + + call.respond(HttpStatusCode.Created, project) + } +} + +fun createProjectId(inputString: String): ProjectId { + if (inputString.isBlank()) { + throw IllegalArgumentException("Project name cannot be blank") + } + val filteredString = inputString.filter { it.isLetter() || it.isWhitespace() || it.isDigit() } + val entityId = filteredString.replace(" ", "_").lowercase(Locale.getDefault()) + return ProjectId(entityId) +} + +@Serializable +data class CreateProjectRequest( + @SerialName("name") + val name: String, +) diff --git a/backend/src/main/java/com/nestapp/projects/ProjectDetails.kt b/backend/src/main/java/com/nestapp/projects/rest/ProjectDetails.kt similarity index 90% rename from backend/src/main/java/com/nestapp/projects/ProjectDetails.kt rename to backend/src/main/java/com/nestapp/projects/rest/ProjectDetails.kt index d5dc5b4..315be95 100644 --- a/backend/src/main/java/com/nestapp/projects/ProjectDetails.kt +++ b/backend/src/main/java/com/nestapp/projects/rest/ProjectDetails.kt @@ -1,6 +1,10 @@ -package com.nestapp.projects +package com.nestapp.projects.rest import com.nestapp.Configuration +import com.nestapp.projects.FileId +import com.nestapp.projects.Project +import com.nestapp.projects.ProjectId +import com.nestapp.projects.ProjectsRepository import io.ktor.http.HttpStatusCode import io.ktor.server.application.ApplicationCall import io.ktor.server.application.call diff --git a/nest2dvue/src/views/ProjectList.vue b/nest2dvue/src/views/ProjectList.vue index bf4fec0..9c342b5 100644 --- a/nest2dvue/src/views/ProjectList.vue +++ b/nest2dvue/src/views/ProjectList.vue @@ -70,7 +70,9 @@ onMounted(async () => { } h2 { - margin-top: 0; + margin-bottom: 20px; /* Space below paragraphs */ + color: var(--color-text); overflow-wrap: break-word; + white-space: normal; }