Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9e11e79
first attempt to add remove collection ednpoint
jchavez137 Jan 19, 2023
333ae91
correcting task type in controller method
jchavez137 Jan 19, 2023
bd103da
adding some unit tests
jchavez137 Jan 20, 2023
0fc98f4
Fix export draft collection (#286)
manuel-delvillar Jan 18, 2023
94ea7df
Add new button action
manuel-delvillar Jan 20, 2023
db96db4
Add new icon
manuel-delvillar Jan 20, 2023
4310c89
Working on delete collection request
manuel-delvillar Jan 20, 2023
5337d39
Working on delete collection
manuel-delvillar Jan 20, 2023
a1a77e9
Fix test
manuel-delvillar Jan 20, 2023
e8f40e5
Add JsonSubType in Task.kt
manuel-delvillar Jan 20, 2023
0f934b9
Update request do delete
manuel-delvillar Jan 20, 2023
4c00a7c
adding security configuration for NoRoles case
jchavez137 Jan 23, 2023
9060000
Update broken tests in manage collection
manuel-delvillar Jan 20, 2023
86e02c2
Unit test delete collection with result
manuel-delvillar Jan 20, 2023
289cacd
Update tests manage collection
manuel-delvillar Jan 23, 2023
933ef4d
Refresh when navigate
manuel-delvillar Jan 23, 2023
9b47fbf
Remove comments
manuel-delvillar Jan 23, 2023
d4f4893
Merge branch 'develop' into feature/OSMT-66-delete-collection
manuel-delvillar Jan 23, 2023
4c8f1fd
Fix problem with reload
manuel-delvillar Jan 23, 2023
1b29f70
Remove refresh
manuel-delvillar Jan 23, 2023
c4876cc
Improvements delete collection
manuel-delvillar Jan 24, 2023
390c09f
changing check logic for delete collection from db and ES, changing T…
jchavez137 Jan 25, 2023
0983e85
changing check logic to be in a transaction
jchavez137 Jan 25, 2023
311a1db
Remove comments and console log
manuel-delvillar Jan 25, 2023
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
1 change: 1 addition & 0 deletions api/src/main/kotlin/edu/wgu/osmt/RoutePaths.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ object RoutePaths {
const val COLLECTION_SKILLS = "$COLLECTION_DETAIL/skills"
const val COLLECTION_AUDIT_LOG = "$COLLECTION_DETAIL/log"
const val COLLECTION_CSV = "$COLLECTION_DETAIL/csv"
const val COLLECTION_REMOVE = "$COLLECTION_DETAIL/remove"


const val TASKS_PATH = "$API/results"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ package edu.wgu.osmt.collection
import edu.wgu.osmt.HasAllPaginated
import edu.wgu.osmt.RoutePaths
import edu.wgu.osmt.api.GeneralApiException
import edu.wgu.osmt.api.model.*
import edu.wgu.osmt.api.model.ApiCollection
import edu.wgu.osmt.api.model.ApiCollectionUpdate
import edu.wgu.osmt.api.model.ApiSearch
import edu.wgu.osmt.api.model.ApiSkillListUpdate
import edu.wgu.osmt.api.model.CollectionSortEnum
import edu.wgu.osmt.auditlog.AuditLog
import edu.wgu.osmt.auditlog.AuditLogRepository
import edu.wgu.osmt.auditlog.AuditLogSortEnum
Expand All @@ -12,7 +16,14 @@ import edu.wgu.osmt.db.PublishStatus
import edu.wgu.osmt.elasticsearch.OffsetPageable
import edu.wgu.osmt.richskill.RichSkillRepository
import edu.wgu.osmt.security.OAuthHelper
import edu.wgu.osmt.task.*
import edu.wgu.osmt.task.AppliesToType
import edu.wgu.osmt.task.CsvTask
import edu.wgu.osmt.task.PublishTask
import edu.wgu.osmt.task.RemoveCollectionSkillsTask
import edu.wgu.osmt.task.Task
import edu.wgu.osmt.task.TaskMessageService
import edu.wgu.osmt.task.TaskResult
import edu.wgu.osmt.task.UpdateCollectionSkillsTask
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpEntity
import org.springframework.http.HttpStatus
Expand All @@ -22,7 +33,14 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.stereotype.Controller
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.bind.annotation.*
import org.springframework.web.bind.annotation.DeleteMapping
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.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.ResponseBody
import org.springframework.web.server.ResponseStatusException
import org.springframework.web.util.UriComponentsBuilder

Expand Down Expand Up @@ -164,6 +182,16 @@ class CollectionController @Autowired constructor(
return Task.processingResponse(task)
}

@DeleteMapping(RoutePaths.COLLECTION_REMOVE, produces = [MediaType.APPLICATION_JSON_VALUE])
fun removeCollection(
@PathVariable uuid: String
): HttpEntity<TaskResult> {

val task = RemoveCollectionSkillsTask(collectionUuid = uuid)
taskMessageService.enqueueJob(TaskMessageService.removeCollectionSkills, task)
return Task.processingResponse(task)
}

@GetMapping(RoutePaths.COLLECTION_AUDIT_LOG, produces = ["application/json"])
fun collectionAuditLog(
@PathVariable uuid: String
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.wgu.osmt.collection

import edu.wgu.osmt.PaginationDefaults
import edu.wgu.osmt.api.FormValidationException
import edu.wgu.osmt.api.model.ApiBatchResult
import edu.wgu.osmt.api.model.ApiCollectionUpdate
Expand All @@ -11,13 +12,22 @@ import edu.wgu.osmt.db.ListFieldUpdate
import edu.wgu.osmt.db.NullableFieldUpdate
import edu.wgu.osmt.keyword.KeywordRepository
import edu.wgu.osmt.keyword.KeywordTypeEnum
import edu.wgu.osmt.richskill.*
import edu.wgu.osmt.richskill.RichSkillDescriptor
import edu.wgu.osmt.richskill.RichSkillDescriptorDao
import edu.wgu.osmt.richskill.RichSkillDescriptorTable
import edu.wgu.osmt.richskill.RichSkillDoc
import edu.wgu.osmt.richskill.RichSkillEsRepo
import edu.wgu.osmt.richskill.RichSkillRepository
import edu.wgu.osmt.richskill.diff
import edu.wgu.osmt.task.PublishTask
import edu.wgu.osmt.task.UpdateCollectionSkillsTask
import org.jetbrains.exposed.sql.SizedIterable
import org.jetbrains.exposed.sql.deleteWhere
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Lazy
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Repository
import org.springframework.transaction.annotation.Transactional
Expand All @@ -36,6 +46,7 @@ interface CollectionRepository {
fun create(name: String, user: String): CollectionDao?
fun create(updateObject: CollectionUpdateObject, user: String): CollectionDao?
fun update(updateObject: CollectionUpdateObject, user: String): CollectionDao?
fun remove(uuid: String): ApiBatchResult

fun createFromApi(
apiUpdates: List<ApiCollectionUpdate>,
Expand Down Expand Up @@ -80,6 +91,7 @@ class CollectionRepositoryImpl @Autowired constructor(

override val table = CollectionTable
override val dao = CollectionDao.Companion
val collectionSkillsTable = CollectionSkills

override fun findAll() = dao.all()

Expand Down Expand Up @@ -197,6 +209,32 @@ class CollectionRepositoryImpl @Autowired constructor(
return daoObject
}

override fun remove(uuid: String): ApiBatchResult {

val collectionFound = findByUUID(uuid)
val esCollectionFound = collectionFound?.let { collectionEsRepo.findByUuid(it.uuid, PageRequest.of(0, PaginationDefaults.size))}

if (esCollectionFound != null && esCollectionFound.content.isNotEmpty()) {
transaction {
table.deleteWhere { table.id eq collectionFound.id }
collectionEsRepo.delete(collectionFound.toDoc())

}
return ApiBatchResult(
success = true,
modifiedCount = 1,
totalCount = 1
)
}

return ApiBatchResult(
success = false,
modifiedCount = 0,
totalCount = 0
)

}

override fun createFromApi(
apiUpdates: List<ApiCollectionUpdate>,
richSkillRepository: RichSkillRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package edu.wgu.osmt.collection

import com.github.sonus21.rqueue.annotation.RqueueListener
import edu.wgu.osmt.richskill.RichSkillRepository
import edu.wgu.osmt.task.RemoveCollectionSkillsTask
import edu.wgu.osmt.task.TaskMessageService
import edu.wgu.osmt.task.TaskStatus
import edu.wgu.osmt.task.UpdateCollectionSkillsTask
Expand Down Expand Up @@ -44,4 +45,22 @@ class UpdateCollectionSkillsTaskProcessor {

logger.info("Task ${task.uuid} completed")
}

@RqueueListener(
value = [TaskMessageService.removeCollectionSkills],
deadLetterQueueListenerEnabled = "true",
deadLetterQueue = TaskMessageService.deadLetters,
concurrency = "1"
)
fun removeCollectionSkills(task: RemoveCollectionSkillsTask) {
logger.info("Started processing to remove collection task id: ${task.uuid}")

val batchResult = collectionRepository.remove(task.collectionUuid)

taskMessageService.publishResult(
task.copy(result=batchResult, status= TaskStatus.Ready)
)

logger.info("Task ${task.uuid} completed")
}
}
6 changes: 4 additions & 2 deletions api/src/main/kotlin/edu/wgu/osmt/security/SecurityConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import edu.wgu.osmt.RoutePaths.COLLECTION_CREATE
import edu.wgu.osmt.RoutePaths.COLLECTION_CSV
import edu.wgu.osmt.RoutePaths.COLLECTION_DETAIL
import edu.wgu.osmt.RoutePaths.COLLECTION_PUBLISH
import edu.wgu.osmt.RoutePaths.COLLECTION_REMOVE
import edu.wgu.osmt.RoutePaths.COLLECTION_SKILLS
import edu.wgu.osmt.RoutePaths.COLLECTION_SKILLS_UPDATE
import edu.wgu.osmt.RoutePaths.COLLECTION_UPDATE
Expand All @@ -29,8 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.http.HttpMethod.GET
import org.springframework.http.HttpMethod.POST
import org.springframework.http.HttpMethod.*
Comment thread
jchavez137 marked this conversation as resolved.
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
Expand Down Expand Up @@ -129,6 +129,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() {
.mvcMatchers(POST, COLLECTION_PUBLISH).hasAnyAuthority(ADMIN)
.mvcMatchers(POST, COLLECTION_UPDATE).hasAnyAuthority(ADMIN, CURATOR)
.mvcMatchers(POST, COLLECTION_SKILLS_UPDATE).hasAnyAuthority(ADMIN)
.mvcMatchers(DELETE, COLLECTION_REMOVE).hasAnyAuthority(ADMIN)

.mvcMatchers("/api/**").hasAnyAuthority(ADMIN, CURATOR, VIEW, READ)
}
Expand All @@ -146,6 +147,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() {
.mvcMatchers(POST, COLLECTION_PUBLISH).authenticated()
.mvcMatchers(POST, COLLECTION_UPDATE).authenticated()
.mvcMatchers(POST, COLLECTION_SKILLS_UPDATE).authenticated()
.mvcMatchers(DELETE, COLLECTION_REMOVE).denyAll()

// fall-through
.mvcMatchers("/api/**").permitAll()
Expand Down
14 changes: 13 additions & 1 deletion api/src/main/kotlin/edu/wgu/osmt/task/Task.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import java.util.*
JsonSubTypes.Type(value = ApiSkillListUpdate::class, name = "ApiSkillListUpdate"),
JsonSubTypes.Type(value = UpdateCollectionSkillsTask::class, name = "UpdateCollectionSkillsTask"),
JsonSubTypes.Type(value = CreateSkillsTask::class, name = "CreateSkillsTask"),
JsonSubTypes.Type(value = ExportSkillsToCsvTask::class, name = "ExportSkillsToCsvTask")
JsonSubTypes.Type(value = ExportSkillsToCsvTask::class, name = "ExportSkillsToCsvTask"),
JsonSubTypes.Type(value = RemoveCollectionSkillsTask::class, name = "RemoveCollectionSkillsTask")
)

interface Task {
Expand Down Expand Up @@ -128,6 +129,17 @@ data class UpdateCollectionSkillsTask(
override val apiResultPath = RoutePaths.TASK_DETAIL_BATCH
}

data class RemoveCollectionSkillsTask(
val collectionUuid: String = "",
override val uuid: String = UUID.randomUUID().toString(),
override val start: Date = Date(),
override val result: ApiBatchResult? = null,
override val status: TaskStatus = TaskStatus.Processing
) : Task {
override val contentType = MediaType.APPLICATION_JSON_VALUE
override val apiResultPath = RoutePaths.TASK_DETAIL_BATCH
}

enum class TaskStatus {
Processing,
Ready
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class TaskMessageService {
const val publishSkills = "batch-publish-skills"
const val updateCollectionSkills = "update-collection-skills"
const val skillsForCollectionCsv = "collection-skills-csv-process"
const val removeCollectionSkills = "remove-collection"
const val skillsForFullLibraryCsv = "full-library-skills-csv-process"
const val skillsForCustomListExportCsv = "custom-rsd-list-export"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import edu.wgu.osmt.richskill.RsdUpdateObject
import edu.wgu.osmt.task.PublishTask
import edu.wgu.osmt.task.UpdateCollectionSkillsTask
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.exposed.sql.selectAll
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.transaction.annotation.Transactional
Expand All @@ -32,6 +33,7 @@ class CollectionRepositoryTest: SpringTest(), BaseDockerizedTest, HasDatabaseRes

val userString = "unittestuser"


@Test
fun `should not create a blank collection`() {
assertThat(collectionRepository.create(CollectionUpdateObject(), userString)).isNull()
Expand Down Expand Up @@ -69,7 +71,7 @@ class CollectionRepositoryTest: SpringTest(), BaseDockerizedTest, HasDatabaseRes
), userString)!!
}

private fun random_collection_update(): ApiCollectionUpdate {
private fun random_collection_update(): ApiCollectionUpdate {
val name = UUID.randomUUID().toString()
val author = UUID.randomUUID().toString()
val status = PublishStatus.Published
Expand Down Expand Up @@ -165,7 +167,6 @@ class CollectionRepositoryTest: SpringTest(), BaseDockerizedTest, HasDatabaseRes

}

@Test
fun testChangeStatusesForTaskWithCollectionId() {
// Arrange
val skillCount = 3
Expand All @@ -189,4 +190,33 @@ class CollectionRepositoryTest: SpringTest(), BaseDockerizedTest, HasDatabaseRes
assertThat(batchResult?.modifiedCount).isEqualTo(skillCount*3)
}

@Test
fun `remove finds and successfully removes an existing collection`() {
// Arrange
val collection = collectionRepository.create(UUID.randomUUID().toString(), userString)!!.toModel()
val updateObject = RsdUpdateObject(name = "test skill", statement = testUser)
val skillDao = richSkillRepository.create(updateObject, testUser)
collection.id?.let { CollectionSkills.create(it, skillDao!!.id.value) }

Comment thread
jchavez137 marked this conversation as resolved.
// Act
val batchResult = collectionRepository.remove(collection.uuid)

// Assert
assertThat(CollectionTable.selectAll()).isEmpty()
assertThat(CollectionSkills.selectAll()).isEmpty()
assertThat(batchResult?.modifiedCount).isEqualTo(1)
assertThat(batchResult?.success).isEqualTo(true)

}

@Test
fun `remove fails to remove a non-existing collection`() {
// Act
val batchResult = collectionRepository.remove(UUID.randomUUID().toString())

// Assert
assertThat(batchResult?.modifiedCount).isEqualTo(0)
assertThat(batchResult?.success).isEqualTo(false)
}

}
6 changes: 4 additions & 2 deletions ui/src/app/auth/auth-roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export enum ButtonAction {
CollectionPublish,
CollectionSkillsUpdate,
LibraryExport,
ExportDraftCollection
ExportDraftCollection,
DeleteCollection,
}

export const ActionByRoles = new Map<number, string[]>([
Expand All @@ -24,7 +25,8 @@ export const ActionByRoles = new Map<number, string[]>([
[ButtonAction.CollectionPublish, [OSMT_ADMIN]],
[ButtonAction.CollectionSkillsUpdate, [OSMT_ADMIN]],
[ButtonAction.LibraryExport, [OSMT_ADMIN]],
[ButtonAction.ExportDraftCollection, [OSMT_ADMIN]]
[ButtonAction.ExportDraftCollection, [OSMT_ADMIN]],
[ButtonAction.DeleteCollection, [OSMT_ADMIN]]
])

//TODO migrate AuthServiceWgu & AuthService.hasRole & isEnabledByRoles into a singleton here. HDN Sept 15, 2022
Loading