Procházet zdrojové kódy

1. Semua api yang menghasilkan list/array ditambahkan pagging
2. /projects/{projectId}/member ditambahkan filter untuk role
3. project hanya bisa dilihat oleh owner dan member saja

athrainsky před 11 měsíci
rodič
revize
2330a5ae27

+ 6 - 5
qc.txt

@@ -1,6 +1,7 @@
-	return role bukan angka
-	return project tidak berulang
-	user_id object disebut user saja
-
 insert  into `user`(`user_id`,`name`,`password`,`username`) values 
-(1,'test user','$2a$10$Y8LgDPJiAsbw7n5pURhGVOmi5.LWpfJaX7ZgSDbjsQXEnsCFPdhB2','user')
+(1,'test user','$2a$10$Y8LgDPJiAsbw7n5pURhGVOmi5.LWpfJaX7ZgSDbjsQXEnsCFPdhB2','user')
+
+	- Semua api yang menghasilkan list/array ditambahkan pagging
+	- /projects/{projectId}/member ditambahkan filter untuk role
+	- project hanya bisa dilihat oleh owner dan member saja
+

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

@@ -7,6 +7,9 @@ import com.swagger.rest.models.ProjectMember
 import com.swagger.rest.repositories.MemberRepository
 import com.swagger.rest.repositories.ProjectRepository
 import com.swagger.rest.repositories.UserRepository
+import org.springframework.data.domain.PageRequest
+import org.springframework.data.domain.Pageable
+import org.springframework.data.domain.Sort
 import org.springframework.http.HttpStatus
 import org.springframework.http.ResponseEntity
 import org.springframework.security.core.context.SecurityContextHolder
@@ -19,22 +22,64 @@ class MemberController(
     private val projectRepository: ProjectRepository,
     private val userRepository: UserRepository
 ) {
+    private fun getSortDirection(direction: String): Sort.Direction {
+        if (direction == "asc") {
+            return Sort.Direction.ASC
+        } else if (direction == "desc") {
+            return Sort.Direction.DESC
+        }
+        return Sort.Direction.ASC
+    }
 
     @GetMapping("/projects/{id}/member")
-    fun getMemberByProject(@PathVariable("id") id: Long): Any {
-        val memberData = memberRepository.findByProject(id.toString())
-        val output = memberData.map {
-            MemberOutput(
-                id = it.id,
-                project = it.project!!.description,
-                user = it.user!!.name,
-                role = Enum.Member.values()[it.role]
-            )
+    fun getMemberByProject(
+        @PathVariable("id") id: Long,
+        @RequestParam(defaultValue = 0.toString()) page: Int,
+        @RequestParam(defaultValue = 3.toString()) limit: Int,
+        @RequestParam(defaultValue = "member_id, desc") sort: Array<String>,
+        @RequestParam(required = false) role: String?
+    ): Any {
+        val orders: MutableList<Sort.Order> = ArrayList()
+        if (sort[0].contains(",")) {
+            for (sortOrder in sort) {
+                val sort_ = sortOrder.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+                orders.add(Sort.Order(getSortDirection(sort_[1]), sort_[0]))
+            }
+        } else {
+            orders.add(Sort.Order(Sort.Direction.DESC, sort[0]))
         }
-        return if (memberData.isNotEmpty()) {
-            ResponseEntity(output, HttpStatus.OK)
+        val pagingSort: Pageable = PageRequest.of(page, limit, Sort.by(orders as List<Sort.Order>))
+        if (enumValues<Enum.Member>().any { it.name == role!!.uppercase() }) {
+            val memberData = if (role == null) {
+                memberRepository.findByProject1(
+                    id.toString(), pagingSort
+                )
+            } else {
+                memberRepository.findByProject2(
+                    id.toString(), Enum.Member.valueOf(role.uppercase()).ordinal.toString(), pagingSort
+                )
+            }
+            val output = memberData.map {
+                MemberOutput(
+                    id = it.id,
+                    project = it.project!!.description,
+                    user = it.user!!.name,
+                    role = Enum.Member.values()[it.role]
+                )
+            }
+            val ret: List<MemberOutput?> = output.content
+            val response: MutableMap<String, Any> = HashMap()
+            response["currentPage"] = output.number
+            response["totalRecord"] = output.totalElements
+            response["totalPage"] = output.totalPages
+            response["results"] = ret
+            return if (ret.isNotEmpty()) {
+                ResponseEntity(response, HttpStatus.OK)
+            } else {
+                arrayOf<String>()
+            }
         } else {
-            arrayOf<String>()
+             return ResponseEntity<ProjectMember>(HttpStatus.BAD_REQUEST)
         }
     }
 

+ 40 - 7
src/main/kotlin/com/swagger/rest/controllers/PlatformController.kt

@@ -6,6 +6,10 @@ import com.swagger.rest.repositories.MemberRepository
 import com.swagger.rest.repositories.PlatformRepository
 import com.swagger.rest.repositories.ProjectRepository
 import com.swagger.rest.repositories.UserRepository
+import org.springframework.data.domain.Page
+import org.springframework.data.domain.PageRequest
+import org.springframework.data.domain.Pageable
+import org.springframework.data.domain.Sort
 import org.springframework.http.HttpStatus
 import org.springframework.http.ResponseEntity
 import org.springframework.security.core.context.SecurityContextHolder
@@ -20,22 +24,51 @@ class PlatformController(
     private val memberRepository: MemberRepository,
     private val userRepository: UserRepository
 ) {
+    private fun getSortDirection(direction: String): Sort.Direction {
+        if (direction == "asc") {
+            return Sort.Direction.ASC
+        } else if (direction == "desc") {
+            return Sort.Direction.DESC
+        }
+        return Sort.Direction.ASC
+    }
 
     @GetMapping("/platforms")
-    fun getPlatformByProjectId(@RequestParam(required = false) project: String?): Any? {
+    fun getPlatformByProjectId(
+        @RequestParam(required = false) project: String?,
+        @RequestParam(defaultValue = 0.toString()) page: Int,
+        @RequestParam(defaultValue = 3.toString()) limit: Int,
+        @RequestParam(defaultValue = "id, desc") sort: Array<String>
+    ): Any {
         return try {
-            val platforms: List<Platform> = if (project == null) {
-                platformRepository.findAll()
+            val orders: MutableList<Sort.Order> = ArrayList()
+            if (sort[0].contains(",")) {
+                for (sortOrder in sort) {
+                    val sort_ = sortOrder.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+                    orders.add(Sort.Order(getSortDirection(sort_[1]), sort_[0]))
+                }
+            } else {
+                orders.add(Sort.Order(Sort.Direction.DESC, sort[0]))
+            }
+            val pagingSort: Pageable = PageRequest.of(page, limit, Sort.by(orders as List<Sort.Order>))
+            val platforms: Page<Platform> = if (project == null) {
+                platformRepository.findAll(pagingSort)
             } else {
-                platformRepository.findByProject(project)
+                platformRepository.findByProject(project, pagingSort)
             }
-            if (platforms.isNotEmpty()) {
-                ResponseEntity<List<Platform>?>(platforms, HttpStatus.OK)
+            val ret: List<Platform?> = platforms.content
+            val response: MutableMap<String, Any> = HashMap()
+            response["currentPage"] = platforms.number
+            response["totalRecord"] = platforms.totalElements
+            response["totalPage"] = platforms.totalPages
+            response["results"] = ret
+            if (ret.isNotEmpty()) {
+                ResponseEntity(response, HttpStatus.OK)
             } else {
                 arrayOf<String>()
             }
         } catch (e: Exception) {
-            ResponseEntity<List<Platform>?>(null, HttpStatus.INTERNAL_SERVER_ERROR)
+            ResponseEntity(null, HttpStatus.INTERNAL_SERVER_ERROR)
         }
     }
 

+ 53 - 11
src/main/kotlin/com/swagger/rest/controllers/ProjectController.kt

@@ -5,6 +5,9 @@ import com.swagger.rest.repositories.MemberRepository
 import com.swagger.rest.repositories.PlatformRepository
 import com.swagger.rest.repositories.ProjectRepository
 import com.swagger.rest.repositories.UserRepository
+import org.springframework.data.domain.PageRequest
+import org.springframework.data.domain.Pageable
+import org.springframework.data.domain.Sort
 import org.springframework.http.HttpStatus
 import org.springframework.http.ResponseEntity
 import org.springframework.security.core.context.SecurityContextHolder
@@ -19,29 +22,68 @@ class ProjectController(
     private val userRepository: UserRepository,
     private val memberRepository: MemberRepository
 ) {
+    private fun getSortDirection(direction: String): Sort.Direction {
+        if (direction == "asc") {
+            return Sort.Direction.ASC
+        } else if (direction == "desc") {
+            return Sort.Direction.DESC
+        }
+        return Sort.Direction.ASC
+    }
 
     @GetMapping("/projects")
-    fun getProject(@RequestParam(required = false) name: String?): Any {
+    fun getProject(
+        @RequestParam(required = false) name: String?,
+        @RequestParam(defaultValue = 0.toString()) page: Int,
+        @RequestParam(defaultValue = 3.toString()) limit: Int,
+        @RequestParam(defaultValue = "id, desc") sort: Array<String>
+    ): Any {
         return try {
-            val projects: List<Project> =
-                if (name == null) projectRepository.findAll() else projectRepository.findByNameContaining(
-                    name
+            val orders: MutableList<Sort.Order> = ArrayList()
+            if (sort[0].contains(",")) {
+                for (sortOrder in sort) {
+                    val sort_ = sortOrder.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+                    orders.add(Sort.Order(getSortDirection(sort_[1]), sort_[0]))
+                }
+            } else {
+//                orders.add(Sort.Order(getSortDirection(sort[1]), sort[0]))
+                orders.add(Sort.Order(Sort.Direction.DESC, sort[0]))
+            }
+            val pagingSort: Pageable = PageRequest.of(page, limit, Sort.by(orders as List<Sort.Order>))
+            val projects =
+                if (name == null) projectRepository.findAll(pagingSort) else projectRepository.findByNameContaining(
+                    name, pagingSort
                 )
-            if (projects.isEmpty()) {
+            val ret = projects.content
+            val response: MutableMap<String, Any> = HashMap()
+            response["currentPage"] = projects.number
+            response["totalRecord"] = projects.totalElements
+            response["totalPage"] = projects.totalPages
+            response["results"] = ret
+            if (ret.isEmpty()) {
                 arrayOf<String>()
-            } else ResponseEntity<List<Project>?>(projects, HttpStatus.OK)
+            } else {
+                ResponseEntity(response, HttpStatus.OK)
+            }
         } catch (e: Exception) {
-            ResponseEntity<List<Project>?>(null, HttpStatus.INTERNAL_SERVER_ERROR)
+            ResponseEntity(null, HttpStatus.INTERNAL_SERVER_ERROR)
         }
     }
 
     @GetMapping("/projects/{id}")
     fun getProjectById(@PathVariable("id") id: Long): ResponseEntity<Project> {
+        val userId = userRepository.getUserByUsername(SecurityContextHolder.getContext().authentication.name)
+        val validOwner = projectRepository.validOwner(id.toString(), userId.id.toString())
+        val validMember = memberRepository.validMember(id.toString(), userId.id.toString())
         val projectData: Optional<Project?> = projectRepository.findById(id)
-        return if (projectData.isPresent) {
-            ResponseEntity<Project>(projectData.get(), HttpStatus.OK)
+        return if (validOwner > 0 || validMember > 0) {
+            if (projectData.isPresent) {
+                ResponseEntity<Project>(projectData.get(), HttpStatus.OK)
+            } else {
+                ResponseEntity<Project>(HttpStatus.NOT_FOUND)
+            }
         } else {
-            ResponseEntity<Project>(HttpStatus.NOT_FOUND)
+            ResponseEntity<Project>(HttpStatus.FORBIDDEN)
         }
     }
 
@@ -106,7 +148,7 @@ class ProjectController(
     @DeleteMapping("/projects/{id}")
     fun deleteProject(@PathVariable("id") id: Long): ResponseEntity<HttpStatus> {
         val find = projectRepository.findById(id)
-        val used = platformRepository.findByProject(id.toString()).size
+        val used = platformRepository.findByProjectId(id.toString()).size
         val userId = userRepository.getUserByUsername(SecurityContextHolder.getContext().authentication.name)
         val validOwner = projectRepository.validOwner(id.toString(), userId.id.toString())
         return try {

+ 45 - 9
src/main/kotlin/com/swagger/rest/controllers/UserController.kt

@@ -1,14 +1,20 @@
 package com.swagger.rest.controllers
 
+import com.swagger.rest.models.MemberOutput
 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 org.springframework.data.domain.PageRequest
+import org.springframework.data.domain.Pageable
+import org.springframework.data.domain.Sort
 import org.springframework.http.HttpStatus
 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 java.util.ArrayList
+import java.util.HashMap
 
 @RestController
 @RequestMapping("/api/v1")
@@ -18,21 +24,51 @@ class UserController(
     private val memberRepository: MemberRepository
 ) {
 
+    private fun getSortDirection(direction: String): Sort.Direction {
+        if (direction == "asc") {
+            return Sort.Direction.ASC
+        } else if (direction == "desc") {
+            return Sort.Direction.DESC
+        }
+        return Sort.Direction.ASC
+    }
+
     @GetMapping("/users")
-    fun getUser(@RequestParam(required = false) username: String?): Any {
+    fun getUser(
+        @RequestParam(required = false) username: String?,
+        @RequestParam(defaultValue = 0.toString()) page: Int,
+        @RequestParam(defaultValue = 3.toString()) limit: Int,
+        @RequestParam(defaultValue = "id, desc") sort: Array<String>
+    ): Any {
         return try {
-            val users: List<User> = if (username == null) {
-                userRepository.findAll()
+            val orders: MutableList<Sort.Order> = ArrayList()
+            if (sort[0].contains(",")) {
+                for (sortOrder in sort) {
+                    val sort_ = sortOrder.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+                    orders.add(Sort.Order(getSortDirection(sort_[1]), sort_[0]))
+                }
+            } else {
+                orders.add(Sort.Order(Sort.Direction.DESC, sort[0]))
+            }
+            val pagingSort: Pageable = PageRequest.of(page, limit, Sort.by(orders as List<Sort.Order>))
+            val users = if (username == null) {
+                userRepository.findAll(pagingSort)
             } else {
-                userRepository.findByUsernameContaining(username)
+                userRepository.findByUsernameContaining(username, pagingSort)
             }
-            if (users.isNotEmpty()) {
-                ResponseEntity<List<User>?>(users, HttpStatus.OK)
+            val ret: List<User?> = users.content
+            val response: MutableMap<String, Any> = HashMap()
+            response["currentPage"] = users.number
+            response["totalRecord"] = users.totalElements
+            response["totalPage"] = users.totalPages
+            response["results"] = ret
+            if (ret.isNotEmpty()) {
+                ResponseEntity(response, HttpStatus.OK)
             } else {
                 arrayOf<String>()
             }
         } catch (e: Exception) {
-            ResponseEntity<List<User>?>(null, HttpStatus.INTERNAL_SERVER_ERROR)
+            ResponseEntity(null, HttpStatus.INTERNAL_SERVER_ERROR)
         }
     }
 
@@ -49,7 +85,7 @@ class UserController(
     @PostMapping("/users")
     fun addUser(@RequestBody user: UserInput): ResponseEntity<User> {
         return try {
-            val found = userRepository.findByUsernameContaining(user.username).size
+            val found = userRepository.findByUsername(user.username!!).size
             if (user.username!!.isNotBlank()) {
                 if (user.username!!.length > 100 || user.password!!.length > 100 || user.name!!.length > 255) {
                     ResponseEntity<User>(HttpStatus.PAYLOAD_TOO_LARGE)
@@ -73,7 +109,7 @@ class UserController(
     @PutMapping("/users/{id}")
     fun updateUserById(@PathVariable("id") id: Long, @RequestBody user: User): ResponseEntity<out Any?> {
         val userData = userRepository.findById(id)
-        val found = userRepository.findByUsernameContaining(user.username).size
+        val found = userRepository.findByUsername(user.username).size
         return if (user.username.isNotBlank()) {
             if (user.username.length > 100 || user.name.length > 255) {
                 ResponseEntity<User>(HttpStatus.PAYLOAD_TOO_LARGE)

+ 9 - 3
src/main/kotlin/com/swagger/rest/repositories/MemberRepository.kt

@@ -1,12 +1,17 @@
 package com.swagger.rest.repositories
 
 import com.swagger.rest.models.ProjectMember
+import org.springframework.data.domain.Page
+import org.springframework.data.domain.Pageable
 import org.springframework.data.jpa.repository.JpaRepository
 import org.springframework.data.jpa.repository.Query
 
 interface MemberRepository : JpaRepository<ProjectMember, Long> {
-    @Query("SELECT f.* FROM project_member f WHERE project_id=?1", nativeQuery = true)
-    fun findByProject(project: String?): List<ProjectMember>
+    @Query("SELECT * FROM project_member WHERE project_id=?1", nativeQuery = true)
+    fun findByProject1(project: String?, pageable: Pageable?): Page<ProjectMember>
+
+    @Query("SELECT * FROM project_member WHERE project_id=?1 AND role=?2", nativeQuery = true)
+    fun findByProject2(project: String?, role: String, pageable: Pageable?): Page<ProjectMember>
 
     @Query("SELECT f.* FROM project_member f WHERE project_id=?1 and role=?2 and user_id=?3", nativeQuery = true)
     fun findDup(project: String?, role: String?, user: String?): List<ProjectMember>
@@ -20,5 +25,6 @@ interface MemberRepository : JpaRepository<ProjectMember, Long> {
     @Query("SELECT COUNT(0) FROM project_member WHERE project_id=?1 AND user_id=?2 AND role<>2 AND role=?3", nativeQuery = true)
     fun targetRole(project: String?, user: String?, role: String?): Int
 
-
+    @Query("SELECT COUNT(0) FROM project_member WHERE project_id=?1 AND user_id=?2", nativeQuery = true)
+    fun validMember(project: String?, user: String?): Int
 }

+ 5 - 1
src/main/kotlin/com/swagger/rest/repositories/PlatformRepository.kt

@@ -1,6 +1,8 @@
 package com.swagger.rest.repositories
 
 import com.swagger.rest.models.Platform
+import org.springframework.data.domain.Page
+import org.springframework.data.domain.Pageable
 import org.springframework.data.jpa.repository.JpaRepository
 import org.springframework.data.jpa.repository.Query
 
@@ -9,6 +11,8 @@ interface PlatformRepository : JpaRepository<Platform, Long> {
     fun findByName(name: String?, project: String?): List<Platform>
 
     @Query("SELECT f.* FROM platform f WHERE project_id=?1", nativeQuery = true)
-    fun findByProject(project: String?): List<Platform>
+    fun findByProject(project: String?, pageable: Pageable?): Page<Platform>
 
+    @Query("SELECT f.* FROM platform f WHERE project_id=?1", nativeQuery = true)
+    fun findByProjectId(project: String?): List<Platform>
 }

+ 3 - 1
src/main/kotlin/com/swagger/rest/repositories/ProjectRepository.kt

@@ -1,12 +1,14 @@
 package com.swagger.rest.repositories
 
 import com.swagger.rest.models.Project
+import org.springframework.data.domain.Page
+import org.springframework.data.domain.Pageable
 import org.springframework.data.jpa.repository.JpaRepository
 import org.springframework.data.jpa.repository.Query
 
 
 interface ProjectRepository : JpaRepository<Project, Long> {
-    fun findByNameContaining(name: String?): List<Project>
+    fun findByNameContaining(name: String?, pageable: Pageable?): Page<Project>
 
     fun findByName(name: String?): Project
 

+ 5 - 1
src/main/kotlin/com/swagger/rest/repositories/UserRepository.kt

@@ -1,6 +1,8 @@
 package com.swagger.rest.repositories
 
 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
 import org.springframework.data.jpa.repository.Query
 
@@ -8,7 +10,9 @@ interface UserRepository:JpaRepository<User, Long> {
     @Query("SELECT u FROM User u WHERE u.username = :username")
     fun getUserByUsername(username: String): User
 
-    fun findByUsernameContaining(username:String?):List<User>
+    fun findByUsername(username: String?): List<User>
+
+    fun findByUsernameContaining(username:String?, pageable: Pageable?): Page<User>
 
     @Query("SELECT COUNT(0) FROM user where user_id=?1 AND username=?2", nativeQuery = true)
     fun countByUser(userId: String, username: String): Int

+ 151 - 72
swagger3 project.yml

@@ -8,7 +8,8 @@ info:
     - Platform Relationship 
     - Auth 
     - Membership & Ownership
-  version: 4.0.0
+    - Rights & Paging
+  version: 5.0.0
 servers:
 - url: http://localhost:8080/api/v1
 tags:
@@ -26,6 +27,10 @@ paths:
       summary: find all project
       description: return all project
       operationId: getProject
+      parameters: 
+        - $ref: '#/components/parameters/page'
+        - $ref: '#/components/parameters/limit'
+        - $ref: '#/components/parameters/sortQuery'
       responses:
         200:
           $ref: '#/components/responses/getArrayProject'
@@ -68,6 +73,8 @@ paths:
           $ref: '#/components/responses/getSingleProject'
         401:
           $ref: '#/components/responses/UnauthorizedError'
+        403:
+          $ref: '#/components/responses/403'
         404:
           $ref: '#/components/responses/404'
       security: 
@@ -125,9 +132,19 @@ paths:
       operationId: getMemberByProject
       parameters: 
         - $ref: '#/components/parameters/projectPath'
+        - $ref: '#/components/parameters/roleQuery'
+        - $ref: '#/components/parameters/page'
+        - $ref: '#/components/parameters/limit'
+        - name: sort
+          in: query
+          description: sort direction (default 'member_id, desc')
+          schema:
+            type: string
       responses:
         200:
           $ref: '#/components/responses/getArrayMember'
+        400:
+          $ref: '#/components/responses/400'
         401:
           $ref: '#/components/responses/UnauthorizedError'
       security:
@@ -201,6 +218,9 @@ paths:
       operationId: getPlatformByProjectId
       parameters: 
         - $ref: '#/components/parameters/projectQuery'
+        - $ref: '#/components/parameters/page'
+        - $ref: '#/components/parameters/limit'
+        - $ref: '#/components/parameters/sortQuery'
       responses:
         200:
           $ref: '#/components/responses/getArrayPlatform'
@@ -304,6 +324,10 @@ paths:
       summary: find all user
       description: return all user
       operationId: getUser
+      parameters: 
+        - $ref: '#/components/parameters/page'
+        - $ref: '#/components/parameters/limit'
+        - $ref: '#/components/parameters/sortQuery'
       responses:
         200:
           $ref: '#/components/responses/getArrayUser'
@@ -527,6 +551,13 @@ components:
         - project
         - user
         - role
+    PaginatedResult:
+      type: object
+      properties:
+        totalPage: { type: number, example: 2 }
+        currentPage: { type: number, example: 0 }
+        totalRecord: { type: number, example: 4 }
+        results: { type: array, items: {} }
   parameters:
     projectPath:
       name: projectId
@@ -562,35 +593,65 @@ components:
       required: true
       schema:
         type: integer
+    limit:
+      name: limit
+      in: query
+      description: Number of records to return (default 3)
+      schema:
+        type: number
+    page:
+      name: page
+      in: query
+      description: Number of page to return (default 0)
+      schema:
+        type: number
+    roleQuery:
+      name: role
+      in: query
+      description: filter role
+      schema:
+        type: string
+    sortQuery:
+      name: sort
+      in: query
+      description: sort direction (default 'id, desc')
+      schema:
+        type: string
   responses:
     getArrayProject:
       description: successful operation
       content:
         application/json:
           schema:
-            type: array
-            items:
-              properties:
-                id: 
-                  type: integer
-                  example: 1
-                name:
-                  type: string
-                  example: TM
-                description:
-                  type: string
-                  example: TelMesengger
-                owner:
-                  properties:
-                    id:
-                      type: integer
-                      example: 1
-                    username:
-                      type: string
-                      example: abi
-                    name:
-                      type: string
-                      example: abidzar
+            type: object
+            allOf:
+              - $ref: '#/components/schemas/PaginatedResult'
+              - type: object
+                properties:
+                  results:
+                    type: array
+                    items:
+                      properties:
+                        id: 
+                          type: integer
+                          example: 1
+                        name:
+                          type: string
+                          example: TM
+                        description:
+                          type: string
+                          example: TelMesengger
+                        owner:
+                          properties:
+                            id:
+                              type: integer
+                              example: 1
+                            username:
+                              type: string
+                              example: abi
+                            name:
+                              type: string
+                              example: abidzar
     getSingleProject:
       description: successful operation
       content:
@@ -664,37 +725,43 @@ components:
       content:
         application/json:
           schema:
-            type: array
-            items: 
-              properties:
-                id:
-                  type: integer
-                  example: 1
-                name:
-                  type: string
-                  example: mobile
-                project_id:
-                  properties:
-                    id:
-                      type: integer
-                      example: 1
-                    name:
-                      type: string
-                      example: TM
-                    description:
-                      type: string
-                      example: TelMessenger
-                    owner:
+            type: object
+            allOf:
+              - $ref: '#/components/schemas/PaginatedResult'
+              - type: object
+                properties:
+                  results:
+                    type: array
+                    items: 
                       properties:
                         id:
                           type: integer
                           example: 1
-                        username:
-                          type: string
-                          example: abi
                         name:
                           type: string
-                          example: abidzar            
+                          example: mobile
+                        project_id:
+                          properties:
+                            id:
+                              type: integer
+                              example: 1
+                            name:
+                              type: string
+                              example: TM
+                            description:
+                              type: string
+                              example: TelMessenger
+                            owner:
+                              properties:
+                                id:
+                                  type: integer
+                                  example: 1
+                                username:
+                                  type: string
+                                  example: abi
+                                name:
+                                  type: string
+                                  example: abidzar            
     successAddPlatform:
       description: record successfully added
       content:
@@ -768,18 +835,24 @@ components:
       content:
         application/json:
           schema:
-            type: array
-            items: 
-              properties:
-                id:
-                  type: integer
-                  example: 1
-                username:
-                  type: string
-                  example: abi
-                name:
-                  type: string
-                  example: abidzar
+            type: object
+            allOf:
+              - $ref: '#/components/schemas/PaginatedResult'
+              - type: object
+                properties:
+                  results:
+                    type: array
+                    items: 
+                      properties:
+                        id:
+                          type: integer
+                          example: 1
+                        username:
+                          type: string
+                          example: abi
+                        name:
+                          type: string
+                          example: abidzar
     successAddUser:
       description: record successfully added
       content:
@@ -829,17 +902,23 @@ components:
       content:
         application/json:
           schema:
-            type: array
-            items:
-              properties:
-                id:
-                  example: 1
-                project:
-                  example: TelMessenger
-                user:
-                  example: abidzar
-                role:
-                  example: QC
+            type: object
+            allOf:
+              - $ref: '#/components/schemas/PaginatedResult'
+              - type: object
+                properties:
+                  results:
+                    type: array
+                    items:
+                      properties:
+                        id:
+                          example: 1
+                        project:
+                          example: TelMessenger
+                        user:
+                          example: abidzar
+                        role:
+                          example: QC
     400:
       description: invalid data
     403: