Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions api/src/main/kotlin/edu/wgu/osmt/RoutePaths.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ object RoutePaths {
const val SKILL_AUDIT_LOG = "$SKILL_DETAIL/log"

const val METADATA_PATH = "/metadata"

const val KEYWORD_PATH = "${METADATA_PATH}/keywords"
const val KEYWORD_LIST = KEYWORD_PATH
const val KEYWORD_CREATE = KEYWORD_PATH
Expand All @@ -45,6 +46,14 @@ object RoutePaths {
const val KEYWORD_REMOVE = "$KEYWORD_DETAIL/remove"
const val KEYWORD_SKILLS = "${KEYWORD_DETAIL}/skills"

const val JOB_CODE_PATH = "$METADATA_PATH/jobcodes"
const val JOB_CODE_CREATE = JOB_CODE_PATH
const val JOB_CODE_LIST = JOB_CODE_PATH
const val JOB_CODE_DETAIL = "$JOB_CODE_PATH/{id}"
const val JOB_CODE_UPDATE = "$JOB_CODE_DETAIL/update"
const val JOB_CODE_REMOVE = "$JOB_CODE_DETAIL/remove"
const val JOB_CODE_SKILLS = "${JOB_CODE_DETAIL}/skills"

//collections
private const val COLLECTIONS_PATH = "/collections"
const val COLLECTIONS_LIST = COLLECTIONS_PATH
Expand Down Expand Up @@ -74,13 +83,6 @@ object RoutePaths {
const val ES_ADMIN_DELETE_INDICES = "$ES_ADMIN/delete-indices"
const val ES_ADMIN_REINDEX = "$ES_ADMIN/reindex"

const val JOB_CODE_PATH = "$METADATA_PATH/jobcodes"
const val JOB_CODE_CREATE = JOB_CODE_PATH
const val JOB_CODE_LIST = JOB_CODE_PATH
const val JOB_CODE_DETAIL = "$JOB_CODE_PATH/{id}"
const val JOB_CODE_UPDATE = "$JOB_CODE_DETAIL/update"
const val JOB_CODE_REMOVE = "$JOB_CODE_DETAIL/remove"

object QueryParams {
const val FROM = "from"
const val SIZE = "size"
Expand Down
109 changes: 100 additions & 9 deletions api/src/main/kotlin/edu/wgu/osmt/jobcode/JobCodeController.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package edu.wgu.osmt.jobcode;

import edu.wgu.osmt.PaginationDefaults
import edu.wgu.osmt.RoutePaths
import edu.wgu.osmt.api.model.ApiJobCode
import edu.wgu.osmt.api.model.JobCodeSortEnum
import edu.wgu.osmt.api.model.JobCodeUpdate
import edu.wgu.osmt.api.model.*
import edu.wgu.osmt.db.JobCodeLevel
import edu.wgu.osmt.db.PublishStatus
import edu.wgu.osmt.elasticsearch.OffsetPageable
import edu.wgu.osmt.elasticsearch.PaginatedLinks
import edu.wgu.osmt.keyword.KeywordTypeEnum
import edu.wgu.osmt.richskill.RichSkillDoc
import edu.wgu.osmt.richskill.RichSkillEsRepo
import edu.wgu.osmt.security.OAuthHelper
import edu.wgu.osmt.task.RemoveJobCodeTask
import edu.wgu.osmt.task.Task
import edu.wgu.osmt.task.TaskMessageService
Expand All @@ -17,22 +22,22 @@ import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
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.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.RequestParam
import org.springframework.web.bind.annotation.*
import org.springframework.web.server.ResponseStatusException
import org.springframework.web.util.UriComponentsBuilder

@Controller
@Transactional
class JobCodeController @Autowired constructor(
val jobCodeEsRepo: JobCodeEsRepo,
val jobCodeRepository: JobCodeRepository,
val richSkillEsRepo: RichSkillEsRepo,
val taskMessageService: TaskMessageService,
val oAuthHelper: OAuthHelper,
) {

@GetMapping(
Expand Down Expand Up @@ -132,4 +137,90 @@ class JobCodeController @Autowired constructor(
return Task.processingResponse(task)
}

@PostMapping(
path = ["${RoutePaths.API}${RoutePaths.API_V3}${RoutePaths.JOB_CODE_SKILLS}"],
produces = [MediaType.APPLICATION_JSON_VALUE]
)
@ResponseBody
@PreAuthorize("isAuthenticated()")
fun searchJobCodeSkills (
uriComponentsBuilder: UriComponentsBuilder,
@PathVariable id: Long,
@RequestParam(required = false, defaultValue = PaginationDefaults.size.toString()) size: Int,
@RequestParam(required = false, defaultValue = "0") from: Int,
@RequestParam(
required = false,
defaultValue = PublishStatus.DEFAULT_API_PUBLISH_STATUS_SET
) status: Array<String>,
@RequestParam(required = false) sort: String? = null,
@RequestBody(required = false) apiSearch: ApiSearch? = null,
@AuthenticationPrincipal user: Jwt? = null
): HttpEntity<List<RichSkillDoc>> {
val sortEnum = sort?.let{ SkillSortEnum.forApiValue(it)}

val jobCode = jobCodeRepository.findById(id) ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)

return searchRelatedSkills(
uriComponentsBuilder = uriComponentsBuilder,
jobCode = jobCode,
size = size,
from = from,
statusFilters = status,
sort = sortEnum ?: SkillSortEnum.defaultSort,
apiSearch = apiSearch ?: ApiSearch(),
user = user
)
}

private fun searchRelatedSkills (
uriComponentsBuilder: UriComponentsBuilder,
jobCode: JobCodeDao,
size: Int,
from: Int,
statusFilters: Array<String>,
sort: SkillSortEnum,
apiSearch: ApiSearch,
user: Jwt?
): HttpEntity<List<RichSkillDoc>> {

val pageable = OffsetPageable(offset = from, limit = size, sort = sort.sort)
val statuses = statusFilters.mapNotNull { PublishStatus.forApiValue(it) }.toMutableSet()

if (user == null) {
statuses.remove(PublishStatus.Deleted)
statuses.remove(PublishStatus.Draft)
}

val search = ApiSearch (
query = apiSearch.query,
advanced = apiSearch.advanced,
uuids = apiSearch.uuids,
filtered = ApiFilteredSearch(
jobCodes = apiSearch.filtered?.jobCodes,
)
)

val countByApiSearch = richSkillEsRepo.countByApiSearch(search, statuses, pageable)
val searchHits = richSkillEsRepo.byApiSearch(search, statuses, pageable)

val responseHeaders = HttpHeaders()
responseHeaders.add("X-Total-Count", countByApiSearch.toString())

uriComponentsBuilder
.path(RoutePaths.SEARCH_SKILLS)
.queryParam(RoutePaths.QueryParams.FROM, from)
.queryParam(RoutePaths.QueryParams.SIZE, size)
.queryParam(RoutePaths.QueryParams.SORT, sort)
.queryParam(RoutePaths.QueryParams.STATUS, statusFilters.joinToString(",").lowercase())

PaginatedLinks(
pageable,
searchHits.totalHits.toInt(),
uriComponentsBuilder
).addToHeaders(responseHeaders)

return ResponseEntity.status(200).headers(responseHeaders)
.body(searchHits.map { it.content }.toList())
}

}
37 changes: 36 additions & 1 deletion api/src/main/kotlin/edu/wgu/osmt/jobcode/JobCodeRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package edu.wgu.osmt.jobcode
import edu.wgu.osmt.api.model.ApiBatchResult
import edu.wgu.osmt.api.model.JobCodeUpdate
import edu.wgu.osmt.db.JobCodeLevel
import edu.wgu.osmt.richskill.RichSkillJobCodeRepository
import edu.wgu.osmt.richskill.*
import org.jetbrains.exposed.sql.SizedIterable
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.deleteWhere
Expand All @@ -19,6 +20,9 @@ import java.time.ZoneOffset

interface JobCodeRepository {
val table: Table
val richSkillJobCodes: RichSkillJobCodes
val richSkillRepository: RichSkillRepository

fun findAll(): SizedIterable<JobCodeDao>
fun findById(id: Long): JobCodeDao?
fun findByCode(code: String): JobCodeDao?
Expand All @@ -27,6 +31,7 @@ interface JobCodeRepository {
fun findBlsCode(code: String): JobCodeDao?
fun create(code: String, framework: String? = null): JobCodeDao
fun createFromApi(jobCodes: List<JobCodeUpdate>): List<JobCodeDao>
fun updateFromApi(existingJobCodeId: Long, apiJobCodeUpdate: JobCodeUpdate, username: String): JobCodeDao?
fun onetsByDetailCode(detailedCode: String): SizedIterable<JobCodeDao>
fun remove(jobCodeId: Long): ApiBatchResult

Expand All @@ -48,8 +53,13 @@ class JobCodeRepositoryImpl: JobCodeRepository {
@Lazy
lateinit var richSkillJobCodeRepository: RichSkillJobCodeRepository

@Autowired
@Lazy
override lateinit var richSkillRepository: RichSkillRepository

val dao = JobCodeDao.Companion
override val table = JobCodeTable
override val richSkillJobCodes = RichSkillJobCodes

override fun findAll() = dao.all()

Expand Down Expand Up @@ -77,6 +87,31 @@ class JobCodeRepositoryImpl: JobCodeRepository {
}
}

override fun updateFromApi(existingJobCodeId: Long, apiJobCodeUpdate: JobCodeUpdate, username: String): JobCodeDao? {
val found = dao.findById(existingJobCodeId)
if (found!=null) {
transaction {
found.code = apiJobCodeUpdate.code
found.name = apiJobCodeUpdate.targetNodeName
found.framework = apiJobCodeUpdate.framework
jobCodeEsRepo.save(found.toModel())

// update rich skill after values changes in keyword and reindex
richSkillJobCodes.select { richSkillJobCodes.jobCodeId eq found.id }.forEach { it ->
val richSkillId = it[richSkillJobCodes.richSkillId]
// richSkillRepository.findById(richSkillId.value)?.keywords?.forEach { it2 -> println(it2.value) }
richSkillRepository.update(
RsdUpdateObject(
id = richSkillId.value
),
username
)
}
}
}
return found
}

override fun findByCodeOrCreate(code: String, framework: String?): JobCodeDao {
val existing = findByCode(code)
return existing ?: create(code, framework)
Expand Down
13 changes: 7 additions & 6 deletions ui/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { MetadataListComponent } from "./metadata/detail/metadata-list/metadata-
import { MetadataManageComponent } from "./metadata/detail/metadata-manage/metadata-manage.component"
import { MetadataPublicComponent } from "./metadata/detail/metadata-public/metadata-public.component"
import { NamedReferenceFormComponent } from "./metadata/named-reference/named-reference-form/named-reference-form.component"
import { JobCodeFormComponent } from "./metadata/job-code/job-code-form/job-code-form.component";

const routes: Routes = [
{ path: "", redirectTo: "/skills", pathMatch: "full" },
Expand Down Expand Up @@ -103,13 +104,13 @@ const routes: Routes = [
roles: ActionByRoles.get(ButtonAction.MetadataCreate)
},
},
/*{path: "job-codes/create",
component: MetadataFormComponent,
{path: "job-codes/create",
component: JobCodeFormComponent,
canActivate: [AuthGuard],
data: {
roles: ActionByRoles.get(ButtonAction.MetadataCreate)
},
},*/
},
// edit metadata
{
path: "named-references/:id/edit",
Expand All @@ -119,13 +120,13 @@ const routes: Routes = [
roles: ActionByRoles.get(ButtonAction.MetadataUpdate)
},
},
/*{path: "job-codes/:id/edit",
component: MetadataFormComponent,
{path: "job-codes/:id/edit",
component: JobCodeFormComponent,
canActivate: [AuthGuard],
data: {
roles: ActionByRoles.get(ButtonAction.MetadataUpdate)
},
},*/
},
// public metadata detail
{
path: "job-codes/:id",
Expand Down
4 changes: 3 additions & 1 deletion ui/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ import { MetadataCardComponent } from "./metadata/detail/metadata-card/metadata-
import { ManageMetadataActionBarVerticalComponent } from "./metadata/detail/metadata-manage/action-bar-vertical/metadata-manage-action-bar-vertical.component"
import { PublicMetadataActionBarVerticalComponent } from "./metadata/detail/metadata-public/action-bar-vertical/metadata-public-action-bar-vertical.component";
import { NamedReferenceFormComponent } from "./metadata/named-reference/named-reference-form/named-reference-form.component"
import { JobCodeFormComponent } from "./metadata/job-code/job-code-form/job-code-form.component";

export function initializeApp(
appConfig: AppConfig,
Expand Down Expand Up @@ -254,7 +255,8 @@ export function initializeApp(
InlineHeadingComponent,
JobCodeParentsPipe,
InlineHeadingComponent,
NamedReferenceFormComponent
NamedReferenceFormComponent,
JobCodeFormComponent
],
imports: [
NgIdleKeepaliveModule.forRoot(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ <h2 *ngIf="title" class="t-type-heading1 t-margin-small t-margin-bottom" #titleH
<app-metadata-selector [isVisible]="getSelectAllEnabled" [control]="typeControl" [currentSelection]="selectedMetadataType">
</app-metadata-selector>
<div class="create-metadata-container">
<a class="m-button" routerLink="/named-references/create" [state]="{metadataType: typeControl.value}">
<a class="m-button" routerLink="{{createRoute}}" [state]="{metadataType: typeControl.value}">
<svg class="m-button-x-icon t-icon">
<use xlink:href="/assets/images/svg-defs.svg#icon-collection"></use>
</svg>
Expand Down
Loading