Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add possibility to add container when creating physical item (TT-1467) #74

Merged
merged 2 commits into from
Apr 29, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ enum class CollectionsDatabase (val value: String) {
PEOPLE("people"),
LANGUAGES("thesau"),
GEO_LOCATIONS("thesaugeo"),
LOCATIONS("location"),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package no.nb.bikube.catalogue.collections.model

import com.fasterxml.jackson.annotation.JsonProperty

data class CollectionsLocationModel(
@JsonProperty("adlibJSON")
val adlibJson: CollectionsLocationRecordList?
)

data class CollectionsLocationRecordList(
val recordList: List<CollectionsLocationObject>?
)

data class CollectionsLocationObject(
@JsonProperty("@priref")
val priRef: String?,

@JsonProperty("name")
val name: List<String>?,

@JsonProperty("barcode")
val barcode: String?,

// This one is weird for containers/location, use language=0! There we have "container" or "location". Otherwise, it's "package" or "location"
@JsonProperty("package_location")
val packageLocation: List<List<CollectionsLanguageListObject>>?
)

fun CollectionsLocationModel.getFirstObject(): CollectionsLocationObject? {
return this.adlibJson?.recordList?.firstOrNull()
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ class ItemDto (
val alternativeNumberList: List<AlternativeNumberInput>? = null,

@SerialName("PID_data_URN")
val urn: String? = null
val urn: String? = null,

@SerialName("current_location.name")
val currentLocationName: String? = null,
)

@Serializable
Expand All @@ -69,12 +72,13 @@ fun createNewspaperItemDto(
recordType = CollectionsRecordType.ITEM.value,
inputName = item.username,
inputNotes = "Registrert i Bikube",
inputSource = "texts>texts",
inputSource = "texts",
inputDate = LocalDate.now().toString(),
inputTime = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")).toString(),
dataset = "texts",
partOfReference = manifestationCatalogueId,
alternativeNumberList = if (useUrn) listOf(AlternativeNumberInput(item.urn!!, "URN")) else null,
urn = if (useUrn) item.urn else null
urn = if (useUrn) item.urn else null,
currentLocationName = if (item.digital == false) item.containerId else null
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package no.nb.bikube.catalogue.collections.model.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.time.LocalDate
import java.time.LocalTime
import java.time.format.DateTimeFormatter


@Serializable
class CollectionsLocationDto(
@SerialName("name")
val name: String?,

@SerialName("barcode")
val barcode: String?,

@SerialName("part_of")
val partOf: String? = "S2",

// "PACKAGE" or "LOCATION"
@SerialName("package_location")
val packageLocation: String? = null,

@SerialName("input.name")
val inputName: String?,

@SerialName("input.date")
val inputDate: String?,

@SerialName("input.time")
val inputTime: String?,

@SerialName("input.notes")
val inputNotes: String?,

@SerialName("description")
val description: String? = null
)

fun createContainerDto(
barcode: String,
username: String,
location: String?,
): CollectionsLocationDto {
return CollectionsLocationDto(
name = barcode,
barcode = barcode,
partOf = location ?: "S2",
packageLocation = "PACKAGE",
inputName = username,
inputDate = LocalDate.now().toString(),
inputTime = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")).toString(),
inputNotes = "Registrert i Bikube",
description = null
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fun createManifestationDto(
dateStart = date.toString(),
inputName = username,
inputNotes = "Registrert i Bikube",
inputSource = "texts>texts",
inputSource = "texts",
inputDate = LocalDate.now().toString(),
inputTime = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")).toString(),
dataset = "texts"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ fun createNewspaperTitleDto(title: TitleInputDto): TitleDto {
subMedium = "Aviser",
inputName = title.username,
inputNotes = "Registrert i Bikube",
inputSource = "texts>texts",
inputSource = "texts",
inputDate = LocalDate.now().toString(),
inputTime = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")).toString(),
dataset = "texts"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package no.nb.bikube.catalogue.collections.repository
import no.nb.bikube.catalogue.collections.config.CollectionsWebClient
import no.nb.bikube.catalogue.collections.enum.*
import no.nb.bikube.catalogue.collections.exception.CollectionsException
import no.nb.bikube.catalogue.collections.model.CollectionsLocationModel
import no.nb.bikube.catalogue.collections.model.CollectionsModel
import no.nb.bikube.catalogue.collections.model.CollectionsNameModel
import no.nb.bikube.catalogue.collections.model.CollectionsTermModel
Expand Down Expand Up @@ -71,6 +72,10 @@ class CollectionsRepository(
return searchTermDatabases("term=\"${name}\" and term.type=\"place\"", CollectionsDatabase.GEO_LOCATIONS)
}

fun searchLocationAndContainers(barcode: String): Mono<CollectionsLocationModel> {
return searchLocationDatabase("barcode=${barcode}")
}

@Throws(CollectionsException::class)
fun createTextsRecord(serializedBody: String): Mono<CollectionsModel> {
return createRecordWebClientRequest(serializedBody, CollectionsDatabase.TEXTS).bodyToMono<CollectionsModel>()
Expand All @@ -84,6 +89,10 @@ class CollectionsRepository(
return createRecordWebClientRequest(serializedBody, db).bodyToMono<CollectionsTermModel>()
}

fun createLocationRecord(serializedBody: String): Mono<CollectionsLocationModel> {
return createRecordWebClientRequest(serializedBody, CollectionsDatabase.LOCATIONS).bodyToMono<CollectionsLocationModel>()
}

private fun searchTexts(query: String): Mono<CollectionsModel> {
return getRecordsWebClientRequest(query, CollectionsDatabase.TEXTS).bodyToMono<CollectionsModel>()
}
Expand All @@ -96,6 +105,10 @@ class CollectionsRepository(
return getRecordsWebClientRequest(query, db).bodyToMono<CollectionsTermModel>()
}

private fun searchLocationDatabase(query: String): Mono<CollectionsLocationModel> {
return getRecordsWebClientRequest(query, CollectionsDatabase.LOCATIONS).bodyToMono<CollectionsLocationModel>()
}

private fun getRecordsWebClientRequest(
query: String,
db: CollectionsDatabase,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package no.nb.bikube.catalogue.collections.service

import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import no.nb.bikube.catalogue.collections.exception.CollectionsItemNotFound
import no.nb.bikube.catalogue.collections.model.CollectionsLocationObject
import no.nb.bikube.catalogue.collections.model.dto.CollectionsLocationDto
import no.nb.bikube.catalogue.collections.model.dto.createContainerDto
import no.nb.bikube.catalogue.collections.model.getFirstObject
import no.nb.bikube.catalogue.collections.repository.CollectionsRepository
import org.springframework.stereotype.Service
import reactor.core.publisher.Mono
import reactor.core.publisher.SynchronousSink

@Service
class CollectionsLocationService (
private val collectionsRepository: CollectionsRepository
){
fun createContainerIfNotExists(
barcode: String,
username: String
): Mono<CollectionsLocationObject> {
return collectionsRepository.searchLocationAndContainers(barcode)
.flatMap {
if (it.adlibJson?.recordList?.isNotEmpty() == true) {
Mono.just(it.getFirstObject()!!)
} else {
createLocationRecord(barcode, username)
}
}
}

private fun createLocationRecord(
barcode: String,
username: String,
): Mono<CollectionsLocationObject> {
val dto: CollectionsLocationDto = createContainerDto(barcode, username, null)
val encodedBody = Json.encodeToString(dto)
return collectionsRepository.createLocationRecord(encodedBody)
.handle { collectionsModel, sink: SynchronousSink<List<CollectionsLocationObject>> ->
collectionsModel.adlibJson?.recordList
?. let { sink.next(collectionsModel.adlibJson.recordList) }
?: sink.error(CollectionsItemNotFound("New container not found"))
}
.map { it.first() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ data class ItemInputDto(
val username: String,
val digital: Boolean? = false,
val urn: String? = null,
var name: String? = null
var name: String? = null,
val containerId: String? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import no.nb.bikube.catalogue.collections.mapper.*
import no.nb.bikube.catalogue.collections.model.*
import no.nb.bikube.catalogue.collections.model.dto.*
import no.nb.bikube.catalogue.collections.repository.CollectionsRepository
import no.nb.bikube.catalogue.collections.service.CollectionsLocationService
import no.nb.bikube.core.enum.*
import no.nb.bikube.core.exception.*
import no.nb.bikube.core.model.*
Expand All @@ -23,7 +24,8 @@ import java.time.format.DateTimeFormatter

@Service
class NewspaperService (
private val collectionsRepository: CollectionsRepository
private val collectionsRepository: CollectionsRepository,
private val collectionsLocationService: CollectionsLocationService
) {
@Throws(CollectionsException::class)
fun createNewspaperTitle(title: TitleInputDto): Mono<Title> {
Expand Down Expand Up @@ -191,7 +193,12 @@ class NewspaperService (
findOrCreateManifestationRecord(item)
}
}.flatMap { manifestation ->
createLinkedNewspaperItem(item, manifestation.priRef)
if (item.digital == false && !item.containerId.isNullOrBlank()) {
collectionsLocationService.createContainerIfNotExists(item.containerId, item.username)
.then(createLinkedNewspaperItem(item, manifestation.priRef))
} else {
createLinkedNewspaperItem(item, manifestation.priRef)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,5 +357,29 @@ class CollectionsModelMockData {
)
)
)

private val collectionsLocationListMock = listOf(listOf(
CollectionsLanguageListObject(lang = "neutral", text = "PACKAGE")
))


val collectionsLocationObjectMock = CollectionsLocationObject(
priRef = "123",
name = listOf("Oslo"),
barcode = "123456789",
packageLocation = collectionsLocationListMock
)

val collectionsLocationModelMock = CollectionsLocationModel(
adlibJson = CollectionsLocationRecordList(
recordList = listOf(collectionsLocationObjectMock)
)
)

val emptyCollectionsLocationModelMock = CollectionsLocationModel(
adlibJson = CollectionsLocationRecordList(
recordList = emptyList()
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package no.nb.bikube.catalogue.collections.service

import com.ninjasquad.springmockk.MockkBean
import io.mockk.every
import io.mockk.verify
import no.nb.bikube.catalogue.collections.CollectionsModelMockData.Companion.collectionsLocationModelMock
import no.nb.bikube.catalogue.collections.CollectionsModelMockData.Companion.collectionsLocationObjectMock
import no.nb.bikube.catalogue.collections.CollectionsModelMockData.Companion.emptyCollectionsLocationModelMock
import no.nb.bikube.catalogue.collections.repository.CollectionsRepository
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.ActiveProfiles
import reactor.core.publisher.Mono
import reactor.kotlin.test.test

@SpringBootTest
@ActiveProfiles("test")
class CollectionsLocationServiceTest {

@Autowired
private lateinit var collectionsLocationService: CollectionsLocationService

@MockkBean
private lateinit var collectionsRepository: CollectionsRepository

@Test
fun `should return existing container if it exists`() {
// Given a container with given barcode exists
val barcode = "barcode"
val username = "username"
every { collectionsRepository.searchLocationAndContainers(barcode) } returns Mono.just(collectionsLocationModelMock)

// When trying to create a container
collectionsLocationService.createContainerIfNotExists(barcode, username)
.test()
.expectSubscription()
.assertNext {
Assertions.assertEquals(collectionsLocationObjectMock, it)
}
.verifyComplete()

// Then we should return the existing container and not create new
verify (exactly = 1) { collectionsRepository.searchLocationAndContainers(barcode) }
verify (exactly = 0) { collectionsRepository.createLocationRecord(any()) }
}

@Test
fun `should create new container if barcode does not exist`() {
// Given a container with given barcode does not exist
val barcode = "barcode"
val username = "username"
every { collectionsRepository.searchLocationAndContainers(barcode) } returns Mono.just(emptyCollectionsLocationModelMock)
every { collectionsRepository.createLocationRecord(any()) } returns Mono.just(collectionsLocationModelMock)

// When trying to create a container
collectionsLocationService.createContainerIfNotExists(barcode, username)
.test()
.expectSubscription()
.assertNext {
Assertions.assertEquals(collectionsLocationObjectMock, it)
}
.verifyComplete()

// Then should create a new container
verify (exactly = 1) { collectionsRepository.searchLocationAndContainers(barcode) }
verify (exactly = 1) { collectionsRepository.createLocationRecord(any()) }
}
}
6 changes: 4 additions & 2 deletions src/test/kotlin/no/nb/bikube/newspaper/NewspaperMockData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ class NewspaperMockData {
username = TEST_USERNAME,
digital = true,
urn = "avisa_null_null_20200105_1_1_1",
name = "Avis A 2020.01.05"
name = "Avis A 2020.01.05",
containerId = null
)

// Minimum valid for creating digital item
Expand All @@ -97,7 +98,8 @@ class NewspaperMockData {
username = TEST_USERNAME,
digital = true,
urn = "avisa_null_null_20200101_1_1_1",
name = "Avis A 2020.01.01"
name = "Avis A 2020.01.01",
containerId = null
)

val language: Language = Language(
Expand Down
Loading