2 Commits d027c10a18 ... 0d4fce41f8

Auteur SHA1 Bericht Datum
  athrainsky 0d4fce41f8 v6 ok 10 maanden geleden
  athrainsky dbcd082074 v6 ok 10 maanden geleden

+ 15 - 0
pom.xml

@@ -68,6 +68,21 @@
 			<artifactId>spring-security-web</artifactId>
 			<version>6.1.5</version>
 		</dependency>
+		<dependency>
+			<groupId>net.sf.supercsv</groupId>
+			<artifactId>super-csv</artifactId>
+			<version>2.4.0</version>
+		</dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-csv</artifactId>
+            <version>1.1</version>
+        </dependency>
+		<dependency>
+			<groupId>com.opencsv</groupId>
+			<artifactId>opencsv</artifactId>
+			<version>5.7.1</version>
+		</dependency>
     </dependencies>
 
 	<build>

+ 12 - 21
qc.txt

@@ -1,27 +1,18 @@
 insert  into `user`(`user_id`,`name`,`password`,`username`) values 
 (1,'test user','$2a$10$Y8LgDPJiAsbw7n5pURhGVOmi5.LWpfJaX7ZgSDbjsQXEnsCFPdhB2','user')
 
-owner
-- owner dapat mengubah nama, deskripsi  project
-- owner dapat menghapus project
-- owner dapat menambah, mengubah dan menghapus platform project
-- owner dapat menambahkan dan menghapus member project
-- owner dapat menambah dan menghapus comment
-- owner dapat menambah, mengubah dan menghapus bug
+Membuat rest api untuk fitur maintenance:
 
-programmer
-- dev dapat mengubah dev_status di bug
-- dev dapat menambah comment
+- Backup 
+: file backup format csv
+: bisa pilih tabel yang akan di backup (jika file lebih dari 1, masukan dalam file zip)
 
-qc
-- qc dapat mengubah status, level, image_url di bug
-- qc dapat menambah comment
-- qc dapat menambah bug
+- Restore
+: melakukan restore data dengan file csv backup
+: tambahkan parameter replace (yes/no) untuk action mereplace data yang sama
 
-admin
-- Admin dapat mengubah nama, deskripsi  project
-- Admin dapat menambah, mengubah dan menghapus platform project
-- Admin dapat menambahkan dan menghapus member project
-- Admin tidak dapat menghapus member dengan role Admin
-- admin dapat menambah dan menghapus comment
-- admin dapat menambah, mengubah dan menghapus bug
+noted:
+- hanya user admin yang bisa melakukan fitur maintenance
+- bikin controller baru /maintenance
+
+validasi kalo data tidak ketemu

+ 19 - 19
src/main/kotlin/com/swagger/rest/controllers/BugController.kt

@@ -105,9 +105,9 @@ class BugController(
                         platform = it.platform!!.name,
                         goodday_url = it.goodday_url,
                         image_url = it.image_url,
-                        level = Enum.Level.values()[it.level],
-                        status = Enum.Status.values()[it.status],
-                        dev_status = Enum.Dev_Status.values()[it.dev_status]
+                        level = Enum.Level.values()[it.level].name,
+                        status = Enum.Status.values()[it.status].name,
+                        dev_status = Enum.Dev_Status.values()[it.dev_status].name
                     )
                 }
                 val ret = output.content
@@ -181,9 +181,9 @@ class BugController(
                         platform = it.platform!!.name,
                         goodday_url = it.goodday_url,
                         image_url = it.image_url,
-                        level = Enum.Level.values()[it.level],
-                        status = Enum.Status.values()[it.status],
-                        dev_status = Enum.Dev_Status.values()[it.dev_status]
+                        level = Enum.Level.values()[it.level].name,
+                        status = Enum.Status.values()[it.status].name,
+                        dev_status = Enum.Dev_Status.values()[it.dev_status].name
                     )
                 }
                 val ret = output.content
@@ -246,7 +246,7 @@ class BugController(
                     orders.add(Sort.Order(getSortDirection(sort2[1]), sort2[0]))
                 }
                 val pagingSort: Pageable = PageRequest.of(page, limit, Sort.by(orders))
-                val spec = Specification<Bug> { root, query, builder ->
+                val spec = Specification<Bug> { root, _, builder ->
                     val list: MutableList<Predicate> = mutableListOf()
                     list.add(builder.equal(root.get<Platform>("platform").get<Long>("id"), id))
                     builder.and(*list.toTypedArray())
@@ -262,9 +262,9 @@ class BugController(
                         platform = it.platform!!.name,
                         goodday_url = it.goodday_url,
                         image_url = it.image_url,
-                        level = Enum.Level.values()[it.level],
-                        status = Enum.Status.values()[it.status],
-                        dev_status = Enum.Dev_Status.values()[it.dev_status]
+                        level = Enum.Level.values()[it.level].name,
+                        status = Enum.Status.values()[it.status].name,
+                        dev_status = Enum.Dev_Status.values()[it.dev_status].name
                     )
                 }
                 val ret = output.content
@@ -298,9 +298,9 @@ class BugController(
                 platform = it.platform!!.name,
                 goodday_url = it.goodday_url,
                 image_url = it.image_url,
-                level = Enum.Level.values()[it.level],
-                status = Enum.Status.values()[it.status],
-                dev_status = Enum.Dev_Status.values()[it.dev_status]
+                level = Enum.Level.values()[it.level].name,
+                status = Enum.Status.values()[it.status].name,
+                dev_status = Enum.Dev_Status.values()[it.dev_status].name
             )
         }
         return if (bugData.isPresent) {
@@ -366,9 +366,9 @@ class BugController(
                             platform = save.platform!!.name,
                             goodday_url = save.goodday_url,
                             image_url = save.image_url,
-                            level = Enum.Level.values()[save.level],
-                            status = Enum.Status.values()[save.status],
-                            dev_status = Enum.Dev_Status.values()[save.dev_status]
+                            level = Enum.Level.values()[save.level].name,
+                            status = Enum.Status.values()[save.status].name,
+                            dev_status = Enum.Dev_Status.values()[save.dev_status].name
                         )
                         ResponseEntity(output, HttpStatus.CREATED)
                     } else {
@@ -484,9 +484,9 @@ class BugController(
                                     platform = save.platform!!.name,
                                     goodday_url = save.goodday_url,
                                     image_url = save.image_url,
-                                    level = Enum.Level.values()[save.level],
-                                    status = Enum.Status.values()[save.status],
-                                    dev_status = Enum.Dev_Status.values()[save.dev_status]
+                                    level = Enum.Level.values()[save.level].name,
+                                    status = Enum.Status.values()[save.status].name,
+                                    dev_status = Enum.Dev_Status.values()[save.dev_status].name
                                 )
                                 ResponseEntity<Any>(output, HttpStatus.OK)
                             } else {

+ 287 - 0
src/main/kotlin/com/swagger/rest/controllers/MaintenanceController.kt

@@ -0,0 +1,287 @@
+package com.swagger.rest.controllers
+
+import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
+import com.swagger.rest.models.*
+import com.swagger.rest.models.Enum
+import com.swagger.rest.repositories.*
+import jakarta.servlet.http.HttpServletResponse
+import org.springframework.http.HttpStatus
+import org.springframework.http.ResponseEntity
+import org.springframework.security.core.context.SecurityContextHolder
+import org.springframework.web.bind.annotation.*
+import org.springframework.web.multipart.MultipartFile
+import org.supercsv.io.CsvBeanWriter
+import org.supercsv.io.ICsvBeanWriter
+import org.supercsv.prefs.CsvPreference
+import java.io.IOException
+import java.text.SimpleDateFormat
+import java.util.*
+import kotlin.Any
+import kotlin.Boolean
+import kotlin.String
+import kotlin.Throws
+import kotlin.Unit
+import kotlin.arrayOf
+import kotlin.toString
+
+
+@RestController
+@RequestMapping("/api/v1")
+class MaintenanceController(
+    private val projectRepository: ProjectRepository,
+    private val userRepository: UserRepository,
+    private val platformRepository: PlatformRepository,
+    private val memberRepository: MemberRepository,
+    private val bugRepository: BugRepository,
+    private val commentRepository: CommentRepository
+) {
+
+    @GetMapping("/maintenance")
+    @Throws(IOException::class)
+    fun exportToCSV(response: HttpServletResponse, @RequestParam tableName: String) {
+        val userId = userRepository.getUserByUsername(SecurityContextHolder.getContext().authentication.name)
+        val valid = memberRepository.validAdmin(userId.id.toString())
+        val tableList = listOf("project", "platform", "user", "project_member", "bug", "comment")
+//        if (valid == 0) {
+//            ResponseEntity<Any>(HttpStatus.FORBIDDEN)
+//        } else {
+            if (!tableList.contains(tableName) || tableName.isEmpty()) {
+                ResponseEntity<Any>(HttpStatus.BAD_REQUEST)
+            } else {
+
+                val list = when (tableName) {
+                    "user" -> userRepository.findAll()
+                    "project" -> projectRepository.findAll().map {
+                        ProjectOutput(
+                            id = it.id, name = it.name, description = it.description, owner = it.owner!!.id.toString()
+                        )
+                    }
+
+                    "platform" -> platformRepository.findAll().map {
+                        PlatformOutput(
+                            id = it.id, name = it.name, project = it.project!!.id.toString()
+                        )
+                    }
+
+                    "project_member" -> memberRepository.findAll().map {
+                        MemberOutput(
+                            id = it.id,
+                            project = it.project!!.id.toString(),
+                            user = it.user!!.id.toString(),
+                            role = Enum.Member.values()[it.role].ordinal.toString()
+                        )
+                    }
+
+                    "bug" -> bugRepository.findAll().map {
+                        BugOutput(
+                            id = it.id,
+                            created = SimpleDateFormat("dd-MMM-yy HH:mm:ss").format(it.created),
+                            description = it.description,
+                            qc = it.qc!!.id.toString(),
+                            dev = it.dev!!.id.toString(),
+                            platform = it.platform!!.id.toString(),
+                            goodday_url = it.goodday_url,
+                            image_url = it.image_url,
+                            level = Enum.Level.values()[it.level].ordinal.toString(),
+                            status = Enum.Status.values()[it.status].ordinal.toString(),
+                            dev_status = Enum.Dev_Status.values()[it.dev_status].ordinal.toString()
+                        )
+                    }
+
+                    "comment" -> commentRepository.findAll().map {
+                        CommentOutput(
+                            id = it.id,
+                            bug = it.bug!!.id.toString(),
+                            creator = it.creator!!.id.toString(),
+                            created = SimpleDateFormat("dd-MMM-yy HH:mm:ss").format(it.created),
+                            content = it.content
+                        )
+                    }
+
+                    else -> listOf()
+                }
+                val csvHeader = when (tableName) {
+                    "user" -> arrayOf("User ID", "Username", "Password", "Name")
+                    "project" -> arrayOf("Project ID", "Description", "Name", "Owner")
+                    "platform" -> arrayOf("Platform ID", "Name", "Project")
+                    "project_member" -> arrayOf("Member ID", "Role", "Project", "User")
+                    "comment" -> arrayOf("Bug ID", "Content", "Created", "Bug", "Creator")
+                    "bug" -> arrayOf(
+                        "Bug ID",
+                        "created",
+                        "Description",
+                        "Dev Status",
+                        "Goodday Url",
+                        "Image Url",
+                        "Level",
+                        "Status",
+                        "Dev",
+                        "Platform",
+                        "Qc"
+                    )
+
+                    else -> arrayOf()
+                }
+                val nameMapping = when (tableName) {
+                    "user" -> arrayOf("id", "username", "password", "name")
+                    "project" -> arrayOf("id", "description", "name", "owner")
+                    "platform" -> arrayOf("id", "name", "project")
+                    "project_member" -> arrayOf("id", "role", "project", "user")
+                    "comment" -> arrayOf("id", "content", "created", "bug", "creator")
+                    "bug" -> arrayOf(
+                        "id",
+                        "created",
+                        "description",
+                        "dev_status",
+                        "goodday_url",
+                        "image_url",
+                        "level",
+                        "status",
+                        "dev",
+                        "platform",
+                        "qc"
+                    )
+
+                    else -> arrayOf()
+                }
+                val csvWriter: ICsvBeanWriter = CsvBeanWriter(response.writer, CsvPreference.STANDARD_PREFERENCE)
+                csvWriter.writeHeader(*csvHeader)
+                for (table in list) {
+                    csvWriter.write(table, *nameMapping)
+                }
+                csvWriter.close()
+//            }
+        }
+        //todo multi / zip download?
+    }
+
+    @PostMapping("/maintenance")
+    fun import(@RequestParam file: MultipartFile, @RequestParam tableName: String, @RequestParam overwrite: Boolean) {
+        val userId = userRepository.getUserByUsername(SecurityContextHolder.getContext().authentication.name)
+        val valid = memberRepository.validAdmin(userId.id.toString())
+        val tableList = listOf("project", "platform", "user", "project_member", "bug", "comment")
+//        if (valid == 0) {
+//            ResponseEntity<Any>(HttpStatus.FORBIDDEN)
+//        } else {
+            if (!tableList.contains(tableName) || tableName.isEmpty()) {
+                ResponseEntity<Any>(HttpStatus.BAD_REQUEST)
+            } else {
+                val nameMapping = when (tableName) {
+                    "user" -> arrayOf("id", "username", "password", "name")
+                    "project" -> arrayOf("id", "description", "name", "owner")
+                    "platform" -> arrayOf("id", "name", "project")
+                    "project_member" -> arrayOf("id", "role", "project", "user")
+                    "comment" -> arrayOf("id", "content", "created", "bug", "creator")
+                    "bug" -> arrayOf(
+                        "id",
+                        "created",
+                        "description",
+                        "dev_status",
+                        "goodday_url",
+                        "image_url",
+                        "level",
+                        "status",
+                        "dev",
+                        "platform",
+                        "qc"
+                    )
+
+                    else -> arrayOf()
+                }
+                val content = String(file.bytes)
+                val lines = content.lines()
+                val mapData: MutableList<MutableMap<String, Any>> = mutableListOf()
+                lines.forEachIndexed { ind, t ->
+                    if (ind == 0 && arrayOf(t) !== nameMapping) {
+                        ResponseEntity<Any>(HttpStatus.BAD_REQUEST)
+                    } else if (t !== "" && ind > 0) {
+                        val commas = t.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)".toRegex()).toTypedArray()
+                        val map: MutableMap<String, Any> = mutableMapOf()
+                        nameMapping.forEachIndexed { index, s ->
+                            map[s] = commas[index]
+                        }
+                        mapData.add(map)
+                    }
+                }
+                val existing = when (tableName) {
+                    "project" -> projectRepository.getId()
+                    "platform" -> platformRepository.getId()
+                    "user" -> userRepository.getId()
+                    "project_member" -> memberRepository.getId()
+                    "bug" -> bugRepository.getId()
+                    "comment" -> commentRepository.getId()
+                    else -> listOf()
+                }
+                mapData.removeIf { p -> existing.contains(p["id"].toString().toLong()) && !overwrite }
+                val unit = when (tableName) {
+                    "project" -> mapData.forEach {
+                        val save = Project()
+                        save.id = it["id"].toString().toLong()
+                        save.name = it["name"].toString()
+                        save.description = it["description"].toString()
+                        save.owner = userRepository.findById(it["owner"].toString().toLong()).get()
+                        projectRepository.save(save)
+                    }
+
+                    "user" -> mapData.forEach {
+                        val save = jacksonObjectMapper().readValue(
+                            jacksonObjectMapper().writeValueAsString(it), User::class.java
+                        )
+                        userRepository.save(save)
+                    }
+
+                    "platform" -> mapData.forEach {
+                        val save = Platform()
+                        save.id = it["id"].toString().toLong()
+                        save.name = it["name"].toString()
+                        save.project = projectRepository.findById(it["project"].toString().toLong()).get()
+                        platformRepository.save(save)
+                    }
+
+                    "project_member" -> mapData.forEach {
+                        val save = ProjectMember()
+                        save.id = it["id"].toString().toLong()
+                        save.role = Enum.Member.values()[Integer.parseInt(it["role"].toString())].ordinal
+                        save.project = projectRepository.findById(it["project"].toString().toLong()).get()
+                        save.user = userRepository.findById(it["user"].toString().toLong()).get()
+                        memberRepository.save(save)
+                    }
+
+                    "comment" -> mapData.forEach {
+                        val save = Comment()
+                        save.id = it["id"].toString().toLong()
+                        save.content = it["content"].toString()
+                        save.created =
+                            SimpleDateFormat("dd-MMM-yy HH:mm:ss", Locale.ENGLISH).parse(it["created"].toString())
+                        save.bug = bugRepository.findById(it["bug"].toString().toLong()).get()
+                        save.creator = userRepository.findById(it["creator"].toString().toLong()).get()
+                        commentRepository.save(save)
+                    }
+
+                    "bug" -> mapData.forEach {
+                        val save = Bug()
+                        save.id = it["id"].toString().toLong()
+                        save.created =
+                            SimpleDateFormat("dd-MMM-yy HH:mm:ss", Locale.ENGLISH).parse(it["created"].toString())
+                        save.description = it["description"].toString()
+                        save.dev_status =
+                            Enum.Dev_Status.values()[Integer.parseInt(it["dev_status"].toString())].ordinal
+                        save.goodday_url = it["goodday_url"].toString()
+                        save.image_url = it["image_url"].toString()
+                        save.level = Enum.Level.values()[Integer.parseInt(it["level"].toString())].ordinal
+                        save.status = Enum.Status.values()[Integer.parseInt(it["status"].toString())].ordinal
+                        save.dev = userRepository.findById(it["dev"].toString().toLong()).get()
+                        save.platform = platformRepository.findById(it["platform"].toString().toLong()).get()
+                        save.qc = userRepository.findById(it["qc"].toString().toLong()).get()
+                        bugRepository.save(save)
+                    }
+
+                    else -> Unit
+                }
+                ResponseEntity<Any>(HttpStatus.OK)
+//            }
+        }
+    }
+}
+
+

+ 3 - 3
src/main/kotlin/com/swagger/rest/controllers/MemberController.kt

@@ -79,7 +79,7 @@ class MemberController(
                         id = it.id,
                         project = it.project!!.description,
                         user = it.user!!.name,
-                        role = Enum.Member.values()[it.role]
+                        role = Enum.Member.values()[it.role].name
                     )
                 }
                 val ret: List<MemberOutput> = output.content
@@ -108,7 +108,7 @@ class MemberController(
                 id = it.id,
                 project = it.project!!.description,
                 user = it.user!!.name,
-                role = Enum.Member.values()[it.role]
+                role = Enum.Member.values()[it.role].name
             )
         }
         return if (memberData.isPresent) {
@@ -173,7 +173,7 @@ class MemberController(
                                     id = save.id,
                                     project = save.project!!.description,
                                     user = save.user!!.name,
-                                    role = Enum.Member.values()[save.role]
+                                    role = Enum.Member.values()[save.role].name
                                 )
                                 ResponseEntity(output, HttpStatus.CREATED)
                             } else {

+ 17 - 5
src/main/kotlin/com/swagger/rest/controllers/UserController.kt

@@ -4,6 +4,7 @@ import com.swagger.rest.models.User
 import com.swagger.rest.models.UserInput
 import com.swagger.rest.repositories.MemberRepository
 import com.swagger.rest.repositories.UserRepository
+import jakarta.servlet.http.HttpServletResponse
 import org.springframework.data.domain.PageRequest
 import org.springframework.data.domain.Pageable
 import org.springframework.data.domain.Sort
@@ -12,6 +13,16 @@ import org.springframework.http.ResponseEntity
 import org.springframework.security.core.context.SecurityContextHolder
 import org.springframework.security.crypto.password.PasswordEncoder
 import org.springframework.web.bind.annotation.*
+import org.supercsv.io.CsvBeanWriter
+import org.supercsv.io.ICsvBeanWriter
+import org.supercsv.prefs.CsvPreference
+import java.io.IOException
+import java.text.DateFormat
+import java.text.SimpleDateFormat
+import java.util.*
+import kotlin.collections.ArrayList
+import kotlin.collections.HashMap
+
 
 @RestController
 @RequestMapping("/api/v1")
@@ -112,7 +123,7 @@ class UserController(
     }
 
     @PutMapping("/users/{id}")
-    fun updateUserById(@PathVariable("id") id: Long, @RequestBody user: User): ResponseEntity<out Any?> {
+    fun updateUserById(@PathVariable("id") id: Long, @RequestBody user: User): ResponseEntity<out Any> {
         val userData = userRepository.findById(id)
         val found = userRepository.findByUsername(user.username).size
         return if (user.username.isNotBlank()) {
@@ -123,7 +134,7 @@ class UserController(
                     val saveUser = userData.get()
                     saveUser.username = user.username.trim()
                     saveUser.name = user.name
-                    ResponseEntity<Any?>(userRepository.save(saveUser), HttpStatus.OK)
+                    ResponseEntity<Any>(userRepository.save(saveUser), HttpStatus.OK)
                 } else {
                     ResponseEntity<User>(HttpStatus.CONFLICT)
                 }
@@ -131,7 +142,7 @@ class UserController(
                 ResponseEntity<User>(HttpStatus.NOT_FOUND)
             }
         } else {
-            ResponseEntity<User?>(HttpStatus.BAD_REQUEST)
+            ResponseEntity<User>(HttpStatus.BAD_REQUEST)
         }
     }
 
@@ -164,7 +175,7 @@ class UserController(
     }
 
     @PutMapping("/users/{id}/password")
-    fun updatePassword(@PathVariable("id") id: Long, @RequestBody userInput: UserInput): ResponseEntity<out Any?> {
+    fun updatePassword(@PathVariable("id") id: Long, @RequestBody userInput: UserInput): ResponseEntity<out Any> {
         val userData = userRepository.findById(id)
         return if (userInput.newPassword!!.isNotBlank() || userInput.oldPassword!!.isNotBlank()) {
             if (userInput.newPassword!!.length > 100) {
@@ -173,7 +184,7 @@ class UserController(
                 if (passwordEncoder.matches(userInput.oldPassword, userData.get().password)) {
                     val saveUser = userData.get()
                     saveUser.password = passwordEncoder.encode(userInput.newPassword)
-                    ResponseEntity<Any?>(userRepository.save(saveUser), HttpStatus.OK)
+                    ResponseEntity<Any>(userRepository.save(saveUser), HttpStatus.OK)
                 } else {
                     ResponseEntity<User>(HttpStatus.FORBIDDEN)
                 }
@@ -184,4 +195,5 @@ class UserController(
             ResponseEntity<User>(HttpStatus.BAD_REQUEST)
         }
     }
+
 }

+ 3 - 3
src/main/kotlin/com/swagger/rest/models/BugOutput.kt

@@ -18,10 +18,10 @@ class BugOutput (
 
     var image_url: String = "",
 
-    var level: Enum.Level? = null,
+    var level: String? = null,
 
-    var status: Enum.Status? = null,
+    var status: String? = null,
 
-    var dev_status: Enum.Dev_Status? = null
+    var dev_status: String? = null
 
 )

+ 1 - 1
src/main/kotlin/com/swagger/rest/models/MemberOutput.kt

@@ -8,6 +8,6 @@ class MemberOutput(
 
     var user: String? = "",
 
-    var role: Enum.Member? = null
+    var role: String? = null
 
 )

+ 1 - 0
src/main/kotlin/com/swagger/rest/models/Project.kt

@@ -2,6 +2,7 @@ package com.swagger.rest.models
 
 import com.fasterxml.jackson.annotation.JsonIgnore
 import jakarta.persistence.*
+import lombok.Data
 
 
 @Entity

+ 14 - 0
src/main/kotlin/com/swagger/rest/models/ProjectOutput.kt

@@ -0,0 +1,14 @@
+package com.swagger.rest.models
+
+
+class ProjectOutput(
+
+    var id: Long = 0,
+
+    var name: String = "",
+
+    var description: String? = null,
+
+    var owner: String? = null
+) {
+}

+ 11 - 10
src/main/kotlin/com/swagger/rest/models/User.kt

@@ -2,46 +2,47 @@ package com.swagger.rest.models
 
 import com.fasterxml.jackson.annotation.JsonIgnore
 import com.fasterxml.jackson.annotation.JsonProperty
+import com.opencsv.bean.CsvBindByName
 import jakarta.persistence.*
 
 
 @Entity
 @Table(name = "user")
-class User {
+class User(
 
     @Id
     @Column(name = "user_id", updatable = false, nullable = false)
     @GeneratedValue(strategy = GenerationType.IDENTITY)
-    var id: Long = 0
+    var id: Long = 0,
 
     @Column(name = "username", length = 100)
-    var username: String = ""
+    var username: String = "",
 
     @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
     @Column(name = "password", length = 100)
-    var password: String = ""
+    var password: String = "",
 
     @Column(name = "name", length = 255)
-    var name: String = ""
+    var name: String = "",
 
     @OneToMany(mappedBy = "user", cascade = [CascadeType.ALL])
     @JsonIgnore
-    var user_id: List<ProjectMember> = mutableListOf()
+    var user_id: List<ProjectMember> = mutableListOf(),
 
     @OneToMany(mappedBy = "owner", cascade = [CascadeType.ALL])
     @JsonIgnore
-    var owner: List<Project> = mutableListOf()
+    var owner: List<Project> = mutableListOf(),
 
     @OneToMany(mappedBy = "qc", cascade = [CascadeType.ALL])
     @JsonIgnore
-    var qc: List<Bug> = mutableListOf()
+    var qc: List<Bug> = mutableListOf(),
 
     @OneToMany(mappedBy = "dev", cascade = [CascadeType.ALL])
     @JsonIgnore
-    var dev: List<Bug> = mutableListOf()
+    var dev: List<Bug> = mutableListOf(),
 
     @OneToMany(mappedBy = "creator", cascade = [CascadeType.ALL])
     @JsonIgnore
     var creator: List<Comment> = mutableListOf()
-
+) {
 }

+ 4 - 0
src/main/kotlin/com/swagger/rest/repositories/BugRepository.kt

@@ -1,6 +1,7 @@
 package com.swagger.rest.repositories
 
 import com.swagger.rest.models.Bug
+import com.swagger.rest.models.User
 import org.springframework.data.domain.Page
 import org.springframework.data.domain.Pageable
 import org.springframework.data.jpa.repository.JpaRepository
@@ -15,4 +16,7 @@ interface BugRepository : JpaRepository<Bug, Long>, JpaSpecificationExecutor<Bug
     @Query("SELECT * FROM bug WHERE platform IN (SELECT platform_id FROM platform WHERE project_id=?1)", nativeQuery = true)
     fun findByProject(project: String, pageable: Pageable): Page<Bug>
 
+    @Query("SELECT bug_id FROM bug", nativeQuery = true)
+    fun getId():List<Long>
+
 }

+ 5 - 0
src/main/kotlin/com/swagger/rest/repositories/CommentRepository.kt

@@ -1,6 +1,7 @@
 package com.swagger.rest.repositories
 
 import com.swagger.rest.models.Comment
+import com.swagger.rest.models.User
 import org.springframework.data.domain.Page
 import org.springframework.data.domain.Pageable
 import org.springframework.data.jpa.repository.JpaRepository
@@ -17,4 +18,8 @@ interface CommentRepository : JpaRepository<Comment, Long>, JpaSpecificationExec
 
     @Query("SELECT COUNT(0) FROM COMMENT WHERE comment_id=?2 AND bug IN (SELECT bug_id FROM bug WHERE platform IN (SELECT platform FROM platform WHERE project_id IN (SELECT project_id FROM project WHERE OWNER=?1 UNION SELECT project_id FROM project_member WHERE role=2 AND user_id=?1)))", nativeQuery = true)
     fun validOwnerAdmin(user: String, id: Long): Int
+
+    @Query("SELECT comment_id FROM comment", nativeQuery = true)
+    fun getId():List<Long>
+
 }

+ 6 - 0
src/main/kotlin/com/swagger/rest/repositories/MemberRepository.kt

@@ -1,6 +1,7 @@
 package com.swagger.rest.repositories
 
 import com.swagger.rest.models.ProjectMember
+import com.swagger.rest.models.User
 import org.springframework.data.jpa.repository.JpaRepository
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor
 import org.springframework.data.jpa.repository.Query
@@ -31,4 +32,9 @@ interface MemberRepository : JpaRepository<ProjectMember, Long>, JpaSpecificatio
     @Query("SELECT COUNT(0) FROM project_member WHERE project_id IN (SELECT project_id FROM platform WHERE NAME=?2) AND user_id=?1 AND role=?3", nativeQuery = true)
     fun validRoleByPlatform(user: String, platform: String, role: String): Int
 
+    @Query("SELECT member_id FROM project_member", nativeQuery = true)
+    fun getId():List<Long>
+
+    @Query("SELECT COUNT(0) FROM project_member WHERE user_id=?1 AND role=2", nativeQuery = true)
+    fun validAdmin(user: String): Int
 }

+ 4 - 0
src/main/kotlin/com/swagger/rest/repositories/PlatformRepository.kt

@@ -1,6 +1,7 @@
 package com.swagger.rest.repositories
 
 import com.swagger.rest.models.Platform
+import com.swagger.rest.models.User
 import org.springframework.data.domain.Page
 import org.springframework.data.domain.Pageable
 import org.springframework.data.jpa.repository.JpaRepository
@@ -32,4 +33,7 @@ interface PlatformRepository : JpaRepository<Platform, Long>, JpaSpecificationEx
     )
     fun findPlatform2(user: String, platform: String): Int
 
+    @Query("SELECT platform_id FROM platform", nativeQuery = true)
+    fun getId():List<Long>
+
 }

+ 4 - 0
src/main/kotlin/com/swagger/rest/repositories/ProjectRepository.kt

@@ -1,6 +1,7 @@
 package com.swagger.rest.repositories
 
 import com.swagger.rest.models.Project
+import com.swagger.rest.models.User
 import org.springframework.data.domain.Page
 import org.springframework.data.domain.Pageable
 import org.springframework.data.jpa.repository.JpaRepository
@@ -24,4 +25,7 @@ interface ProjectRepository : JpaRepository<Project, Long>, JpaSpecificationExec
     @Query("SELECT COUNT(0) FROM project WHERE project_id=?1 AND owner=?2", nativeQuery = true)
     fun validOwner(project: String?, owner: String?): Int
 
+    @Query("SELECT a.project_id FROM project a", nativeQuery = true)
+    fun getId():List<Long>
+
 }

+ 3 - 0
src/main/kotlin/com/swagger/rest/repositories/UserRepository.kt

@@ -13,4 +13,7 @@ interface UserRepository : JpaRepository<User, Long> {
     @Query("SELECT COUNT(0) FROM user where user_id=?1 AND username=?2", nativeQuery = true)
     fun countByUser(userId: String, username: String): Int
 
+    @Query("SELECT user_id FROM user", nativeQuery = true)
+    fun getId():List<Long>
+
 }

+ 84 - 16
swagger3 project.yml

@@ -3,7 +3,6 @@ info:
   title: Project API
   description: |
     Latihan Project API 
-    
     - CRUD Project 
     - Platform Relationship 
     - Auth 
@@ -24,6 +23,8 @@ tags:
   description: everything bug
 - name: comments
   description: everything comment
+- name: maintenance
+  description: everything maintenance
 paths:
   /projects:
     get:
@@ -623,11 +624,11 @@ paths:
                 image_url:
                   example: image.com
                 level:
-                  example: major
+                  example: MAJOR
                 status:
-                  example: onprogress
+                  example: ONPROGRESS
                 dev_status:
-                  example: notstart
+                  example: NOTSTART
       responses:
         200:
           $ref: '#/components/responses/getSingleBug'
@@ -753,6 +754,73 @@ paths:
           $ref: '#/components/responses/404'
       security: 
       - testAuth: []
+  /maintenance:
+    get:
+      tags: 
+      - maintenance
+      summary: backup data
+      description: export from table
+      operationId: exportToCSV
+      parameters: 
+        - name: tableName
+          in: query
+          description: table name (bug, comment, project, platform, project_member, user) (multi table gunakan ;)
+          schema:
+            type: string
+      responses:
+        200:
+          description: export success
+          content:
+            text/csv:
+              example: string
+        400:
+          $ref: '#/components/responses/400'
+        401:
+          $ref: '#/components/responses/UnauthorizedError'
+        403:
+          $ref: '#/components/responses/403'
+      security: 
+        - testAuth: []
+    post:
+      tags: 
+      - maintenance
+      summary: restore data
+      description: import to table
+      operationId: importFromCSV
+      parameters: 
+        - name: tableName
+          in: query
+          description: table name (bug, comment, project, platform, project_member, user) (multi table gunakan ;)
+          schema:
+            type: string
+        - name: overwrite
+          in: query
+          description: overwrite table value 
+          schema:
+            type: boolean
+      requestBody:
+        description: file to import
+        required: true
+        content:
+          multipart/form-data:
+            schema:
+              type: object
+              properties:
+                file:
+                  type: string
+                  format: binary
+      responses:
+        200:
+          description: import success
+          content:
+            text/csv:
+              example: string
+        401:
+          $ref: '#/components/responses/UnauthorizedError'
+        403:
+          $ref: '#/components/responses/403'
+      security: 
+      - testAuth: []
 components:
   schemas:
     Project:
@@ -1069,11 +1137,11 @@ components:
               image_url:
                 example: image.com
               level:
-                example: minor
+                example: MINOR
               status:
-                example: onprogress
+                example: ONPROGRESS
               dev_status:
-                example: done
+                example: DONE
     successAddBug:
       description: record succesfully added
       content:
@@ -1097,11 +1165,11 @@ components:
               image_url:
                 example: image.com
               level:
-                example: minor
+                example: MINOR
               status:
-                example: onprogress
+                example: ONPROGRESS
               dev_status:
-                example: notstart
+                example: NOTSTART
     getArrayBug:
       description: successful operation
       content:
@@ -1135,11 +1203,11 @@ components:
                         image_url:
                           example: image.com
                         level:
-                          example: minor
+                          example: MINOR
                         status:
-                          example: onprogress
+                          example: ONPROGRESS
                         dev_status:
-                          example: done
+                          example: DONE
     getArrayProject:
       description: successful operation
       content:
@@ -1474,11 +1542,11 @@ components:
               image_url:
                 example: image.com
               level:
-                example: note
+                example: NOTE
               status:
-                example: onprogress
+                example: ONPROGRESS
               dev_status:
-                example: notstart
+                example: NOTSTART
     Comment:
       description: Comment object
       required: true