Перевод pcp-tgu на новый НСИ
This commit is contained in:
@@ -65,7 +65,10 @@ planning:
|
|||||||
external-update-enabled: false
|
external-update-enabled: false
|
||||||
|
|
||||||
classifier:
|
classifier:
|
||||||
platforms-url: http://pcp-satellite-catalog-service:${pcp.ports.satellite-catalog}
|
platforms-url: https://ordinis.nstart.cloud/api/v1/spacecraft/records
|
||||||
|
allowed-platform-statuses:
|
||||||
|
- OPERATIONAL
|
||||||
|
- STANDBY
|
||||||
|
|
||||||
tle:
|
tle:
|
||||||
polling:
|
polling:
|
||||||
|
|||||||
@@ -38,7 +38,10 @@ topics:
|
|||||||
spacecraft-points-update: pcp.tgu.kpp-update.v1
|
spacecraft-points-update: pcp.tgu.kpp-update.v1
|
||||||
|
|
||||||
classifier:
|
classifier:
|
||||||
platforms-url: http://nsi-classifier-api.k8s.265.nstart.local/classifiers/platforms
|
platforms-url: https://ordinis.nstart.cloud/api/v1/spacecraft/records
|
||||||
|
allowed-platform-statuses:
|
||||||
|
- OPERATIONAL
|
||||||
|
- STANDBY
|
||||||
|
|
||||||
---
|
---
|
||||||
spring:
|
spring:
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+4
-1
@@ -9,5 +9,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties
|
|||||||
data class ClassifierProperties(
|
data class ClassifierProperties(
|
||||||
|
|
||||||
/** URL классификатора космических аппаратов. */
|
/** URL классификатора космических аппаратов. */
|
||||||
val platformsUrl: String
|
val platformsUrl: String,
|
||||||
|
|
||||||
|
/** Статусы КА из НСИ, с которыми сервису разрешено работать. */
|
||||||
|
val allowedPlatformStatuses: Set<String>
|
||||||
)
|
)
|
||||||
|
|||||||
+16
-5
@@ -1,7 +1,8 @@
|
|||||||
package space.nstart.pcp_tgu_service.controller
|
package space.nstart.pcp_tgu_service.controller
|
||||||
|
|
||||||
/** Содержит методы API только на чтение для платформ из внешнего классификатора. */
|
/** Содержит методы API только на чтение для платформ из внешнего классификатора. */
|
||||||
import space.nstart.pcp_tgu_service.dto.ContentItemDTO
|
import space.nstart.pcp_tgu_service.domain.PlatformCatalogItem
|
||||||
|
import space.nstart.pcp_tgu_service.dto.PlatformResponse
|
||||||
import space.nstart.pcp_tgu_service.service.PlatformService
|
import space.nstart.pcp_tgu_service.service.PlatformService
|
||||||
import io.swagger.v3.oas.annotations.Operation
|
import io.swagger.v3.oas.annotations.Operation
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag
|
import io.swagger.v3.oas.annotations.tags.Tag
|
||||||
@@ -25,14 +26,24 @@ class PlatformController(
|
|||||||
/**
|
/**
|
||||||
* Возвращает список космических аппаратов из внешнего классификатора.
|
* Возвращает список космических аппаратов из внешнего классификатора.
|
||||||
*/
|
*/
|
||||||
/** Возвращает платформы с необязательной фильтрацией по типу и статусу. */
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@Operation(summary = "Получить список космических аппаратов")
|
@Operation(summary = "Получить список космических аппаратов")
|
||||||
fun getPlatforms(
|
fun getPlatforms(
|
||||||
@RequestParam(required = false) platformTypeCode: String?,
|
|
||||||
@RequestParam(required = false) status: String?
|
@RequestParam(required = false) status: String?
|
||||||
): Mono<List<ContentItemDTO>> =
|
): Mono<List<PlatformResponse>> =
|
||||||
Mono.fromCallable {
|
Mono.fromCallable {
|
||||||
platformService.loadPlatforms(platformTypeCode, status)
|
platformService.loadPlatforms(status).map { platform -> platform.toResponse() }
|
||||||
}.subscribeOn(Schedulers.boundedElastic())
|
}.subscribeOn(Schedulers.boundedElastic())
|
||||||
|
|
||||||
|
private fun PlatformCatalogItem.toResponse(): PlatformResponse =
|
||||||
|
PlatformResponse(
|
||||||
|
id = nsiId,
|
||||||
|
businessKey = businessKey,
|
||||||
|
name = name,
|
||||||
|
status = status,
|
||||||
|
noradId = noradId,
|
||||||
|
mission = mission,
|
||||||
|
validFrom = validFrom,
|
||||||
|
validTo = validTo
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
package space.nstart.pcp_tgu_service.domain
|
||||||
|
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
/** Платформа КА из НСИ в минимальном виде, нужном pcp-tgu-service. */
|
||||||
|
data class PlatformCatalogItem(
|
||||||
|
val nsiId: String,
|
||||||
|
val businessKey: String?,
|
||||||
|
val name: String?,
|
||||||
|
val status: String?,
|
||||||
|
val noradId: Long?,
|
||||||
|
val mission: String?,
|
||||||
|
val validFrom: Instant?,
|
||||||
|
val validTo: Instant?
|
||||||
|
)
|
||||||
+21
-235
@@ -2,249 +2,35 @@ package space.nstart.pcp_tgu_service.dto
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import java.time.Instant
|
||||||
|
|
||||||
/**
|
/** Платформа из root-array ответа НСИ. */
|
||||||
* Корневой DTO JSON-ответа.
|
|
||||||
* Соответствует всему объекту JSON.
|
|
||||||
*/
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
data class PlatformsResponseDTO(
|
data class NsiPlatformDto(
|
||||||
|
val id: String?,
|
||||||
/**
|
val businessKey: String?,
|
||||||
* UUID справочника.
|
val data: NsiPlatformDataDto?,
|
||||||
*/
|
val validFrom: Instant?,
|
||||||
val uuid: String?,
|
val validTo: Instant?
|
||||||
|
|
||||||
/**
|
|
||||||
* API имя сущности.
|
|
||||||
*/
|
|
||||||
val apiName: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GUI имя сущности.
|
|
||||||
*/
|
|
||||||
val guiName: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Статус справочника.
|
|
||||||
*/
|
|
||||||
val status: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Признак иерархии.
|
|
||||||
*/
|
|
||||||
val hierarchy: Boolean?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Правило валидации.
|
|
||||||
*/
|
|
||||||
val validationRule: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Список описаний полей справочника.
|
|
||||||
*/
|
|
||||||
val fields: List<FieldDTO>?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Основной список элементов.
|
|
||||||
*/
|
|
||||||
val content: List<ContentItemDTO>?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Информация о пагинации.
|
|
||||||
*/
|
|
||||||
val page: PageDTO?
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/** Согласованные поля объекта data платформы НСИ. */
|
||||||
* DTO описания одного поля.
|
|
||||||
*/
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
data class FieldDTO(
|
data class NsiPlatformDataDto(
|
||||||
|
val name: String?,
|
||||||
/**
|
|
||||||
* UUID поля.
|
|
||||||
*/
|
|
||||||
val uuid: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* API имя поля.
|
|
||||||
*/
|
|
||||||
val apiName: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GUI имя поля.
|
|
||||||
*/
|
|
||||||
val guiName: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Статус поля.
|
|
||||||
*/
|
|
||||||
val status: String?,
|
val status: String?,
|
||||||
|
|
||||||
/**
|
|
||||||
* Тип поля.
|
|
||||||
*/
|
|
||||||
val type: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* API имя связанного классификатора.
|
|
||||||
*/
|
|
||||||
val fkClassifierApiName: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* API имя связанного поля.
|
|
||||||
*/
|
|
||||||
val fkFieldApiName: String?
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DTO одного элемента массива content.
|
|
||||||
*/
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
data class ContentItemDTO(
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UUID записи.
|
|
||||||
*/
|
|
||||||
val uuid: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UUID родительского элемента.
|
|
||||||
*/
|
|
||||||
val parentItemUuid: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Статус записи.
|
|
||||||
*/
|
|
||||||
val status: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Дата начала действия.
|
|
||||||
*/
|
|
||||||
val initiation: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Дата окончания действия.
|
|
||||||
*/
|
|
||||||
val expiration: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Вложенный объект данных.
|
|
||||||
*/
|
|
||||||
val data: ContentDataDTO?
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DTO вложенного объекта data внутри content.
|
|
||||||
*/
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
data class ContentDataDTO(
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Код типа платформы.
|
|
||||||
*/
|
|
||||||
@param:JsonProperty("platform_type_code")
|
|
||||||
val platformTypeCode: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NORAD ID.
|
|
||||||
*/
|
|
||||||
@param:JsonProperty("norad_id")
|
@param:JsonProperty("norad_id")
|
||||||
val noradId: Long?,
|
val noradId: Long?,
|
||||||
|
val mission: String?
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
data class PlatformResponse(
|
||||||
* Уникальный номер изделия (УМН).
|
val id: String,
|
||||||
*/
|
val businessKey: String?,
|
||||||
val umn: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Название аппарата.
|
|
||||||
*/
|
|
||||||
val name: String?,
|
val name: String?,
|
||||||
|
val status: String?,
|
||||||
/**
|
val noradId: Long?,
|
||||||
* Описание аппарата.
|
val mission: String?,
|
||||||
*/
|
val validFrom: Instant?,
|
||||||
val description: String?,
|
val validTo: Instant?
|
||||||
|
|
||||||
/**
|
|
||||||
* Название типа платформы.
|
|
||||||
*/
|
|
||||||
@param:JsonProperty("platform_type_name")
|
|
||||||
val platformTypeName: String?
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
|
||||||
* DTO блока page (пагинация).
|
|
||||||
*/
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
data class PageDTO(
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Размер страницы.
|
|
||||||
*/
|
|
||||||
val size: Int?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Номер страницы.
|
|
||||||
*/
|
|
||||||
val number: Int?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Общее количество элементов.
|
|
||||||
*/
|
|
||||||
val totalElements: Long?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Общее количество страниц.
|
|
||||||
*/
|
|
||||||
val totalPages: Int?
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Класс парсера JSON.
|
|
||||||
*/
|
|
||||||
class PlatformsJsonParser(
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ObjectMapper Jackson.
|
|
||||||
* Предоставляется Spring Boot автоматически.
|
|
||||||
*/
|
|
||||||
private val objectMapper: ObjectMapper
|
|
||||||
) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Парсит весь JSON в корневой DTO.
|
|
||||||
*
|
|
||||||
* @param json JSON строка
|
|
||||||
* @return PlatformsResponseDTO
|
|
||||||
*/
|
|
||||||
fun parse(json: String): PlatformsResponseDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Десериализация JSON -> Kotlin объект.
|
|
||||||
*/
|
|
||||||
return objectMapper.readValue(json, PlatformsResponseDTO::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Парсит JSON и возвращает только массив content.
|
|
||||||
*
|
|
||||||
* @param json JSON строка
|
|
||||||
* @return список элементов content
|
|
||||||
*/
|
|
||||||
fun parseContent(json: String): List<ContentItemDTO> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Парсим корневой объект.
|
|
||||||
*/
|
|
||||||
val response = objectMapper.readValue(json, PlatformsResponseDTO::class.java)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Если content отсутствует — возвращаем пустой список.
|
|
||||||
*/
|
|
||||||
return response.content.orEmpty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+14
-13
@@ -5,8 +5,8 @@ import space.nstart.pcp_tgu_service.config.ExternalApiProperties
|
|||||||
import space.nstart.pcp_tgu_service.config.PlanningProperties
|
import space.nstart.pcp_tgu_service.config.PlanningProperties
|
||||||
import space.nstart.pcp_tgu_service.domain.InsertionPoint
|
import space.nstart.pcp_tgu_service.domain.InsertionPoint
|
||||||
import space.nstart.pcp_tgu_service.domain.ObservationWindow
|
import space.nstart.pcp_tgu_service.domain.ObservationWindow
|
||||||
|
import space.nstart.pcp_tgu_service.domain.PlatformCatalogItem
|
||||||
import space.nstart.pcp_tgu_service.domain.SpacecraftPoints
|
import space.nstart.pcp_tgu_service.domain.SpacecraftPoints
|
||||||
import space.nstart.pcp_tgu_service.dto.ContentItemDTO
|
|
||||||
import space.nstart.pcp_tgu_service.dto.ExternalRvaItemDto
|
import space.nstart.pcp_tgu_service.dto.ExternalRvaItemDto
|
||||||
import space.nstart.pcp_tgu_service.service.PlatformService
|
import space.nstart.pcp_tgu_service.service.PlatformService
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
@@ -39,16 +39,15 @@ class ExternalPointsClient(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Загружает окна видимости по всем КА из классификатора,
|
* Загружает окна видимости по всем КА из классификатора,
|
||||||
* у которых заполнен NORAD ID.
|
* которые разрешены для работы TGU.
|
||||||
*/
|
*/
|
||||||
/** Загружает окна видимости для всех КА с заполненным NORAD-идентификатором в классификаторе. */
|
|
||||||
fun fetchAllSpacecraftPoints(): List<SpacecraftPoints> {
|
fun fetchAllSpacecraftPoints(): List<SpacecraftPoints> {
|
||||||
val timeStart = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES)
|
val timeStart = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES)
|
||||||
val timeStop = timeStart.plusDays(planningProperties.externalPointsLookaheadDays)
|
val timeStop = timeStart.plusDays(planningProperties.externalPointsLookaheadDays)
|
||||||
|
|
||||||
return platformService.loadPlatforms()
|
return platformService.loadAllowedPlatforms()
|
||||||
.mapNotNull { platform ->
|
.mapNotNull { platform ->
|
||||||
val noradId = platform.data?.noradId ?: return@mapNotNull null
|
val noradId = requireNotNull(platform.noradId)
|
||||||
fetchSpacecraftPointsSafely(
|
fetchSpacecraftPointsSafely(
|
||||||
platform = platform,
|
platform = platform,
|
||||||
satelliteId = noradId,
|
satelliteId = noradId,
|
||||||
@@ -60,7 +59,7 @@ class ExternalPointsClient(
|
|||||||
|
|
||||||
/** Оборачивает загрузку по одному КА и подавляет транспортные ошибки только для него. */
|
/** Оборачивает загрузку по одному КА и подавляет транспортные ошибки только для него. */
|
||||||
private fun fetchSpacecraftPointsSafely(
|
private fun fetchSpacecraftPointsSafely(
|
||||||
platform: ContentItemDTO,
|
platform: PlatformCatalogItem,
|
||||||
satelliteId: Long,
|
satelliteId: Long,
|
||||||
timeStart: LocalDateTime,
|
timeStart: LocalDateTime,
|
||||||
timeStop: LocalDateTime
|
timeStop: LocalDateTime
|
||||||
@@ -138,19 +137,19 @@ class ExternalPointsClient(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun logExternalApiError(
|
private fun logExternalApiError(
|
||||||
platform: ContentItemDTO,
|
platform: PlatformCatalogItem,
|
||||||
satelliteId: Long,
|
satelliteId: Long,
|
||||||
timeStart: LocalDateTime,
|
timeStart: LocalDateTime,
|
||||||
timeStop: LocalDateTime,
|
timeStop: LocalDateTime,
|
||||||
ex: WebClientException
|
ex: WebClientException
|
||||||
) {
|
) {
|
||||||
val platformName = platform.data?.name
|
|
||||||
|
|
||||||
if (ex is WebClientResponseException) {
|
if (ex is WebClientResponseException) {
|
||||||
log.warn(
|
log.warn(
|
||||||
"Skipping spacecraft due to external API response error. name={}, noradId={}, timeStart={}, timeStop={}, status={}, responseBody={}",
|
"Skipping spacecraft due to external API response error. name={}, noradId={}, nsiId={}, businessKey={}, timeStart={}, timeStop={}, status={}, responseBody={}",
|
||||||
platformName,
|
platform.name,
|
||||||
satelliteId,
|
satelliteId,
|
||||||
|
platform.nsiId,
|
||||||
|
platform.businessKey,
|
||||||
timeStart,
|
timeStart,
|
||||||
timeStop,
|
timeStop,
|
||||||
ex.statusCode.value(),
|
ex.statusCode.value(),
|
||||||
@@ -160,9 +159,11 @@ class ExternalPointsClient(
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.warn(
|
log.warn(
|
||||||
"Skipping spacecraft due to external API transport error. name={}, noradId={}, timeStart={}, timeStop={}, message={}",
|
"Skipping spacecraft due to external API transport error. name={}, noradId={}, nsiId={}, businessKey={}, timeStart={}, timeStop={}, message={}",
|
||||||
platformName,
|
platform.name,
|
||||||
satelliteId,
|
satelliteId,
|
||||||
|
platform.nsiId,
|
||||||
|
platform.businessKey,
|
||||||
timeStart,
|
timeStart,
|
||||||
timeStop,
|
timeStop,
|
||||||
ex.message
|
ex.message
|
||||||
|
|||||||
+5
-5
@@ -2,7 +2,7 @@ package space.nstart.pcp_tgu_service.integration.api
|
|||||||
|
|
||||||
/** Вызывает сервис классификатора и возвращает сырые метаданные платформ. */
|
/** Вызывает сервис классификатора и возвращает сырые метаданные платформ. */
|
||||||
import space.nstart.pcp_tgu_service.config.ClassifierProperties
|
import space.nstart.pcp_tgu_service.config.ClassifierProperties
|
||||||
import space.nstart.pcp_tgu_service.dto.PlatformsResponseDTO
|
import space.nstart.pcp_tgu_service.dto.NsiPlatformDto
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import org.springframework.web.reactive.function.client.WebClient
|
import org.springframework.web.reactive.function.client.WebClient
|
||||||
|
|
||||||
@@ -19,13 +19,13 @@ class PlatformsClassifierClient(
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Загружает полный ответ классификатора платформ.
|
* Загружает список платформ из классификатора НСИ.
|
||||||
*/
|
*/
|
||||||
/** Загружает полный ответ классификатора вместе с содержимым платформ. */
|
fun fetchPlatforms(): List<NsiPlatformDto> =
|
||||||
fun fetchPlatforms(): PlatformsResponseDTO =
|
|
||||||
webClient.get()
|
webClient.get()
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.bodyToMono(PlatformsResponseDTO::class.java)
|
.bodyToMono(Array<NsiPlatformDto>::class.java)
|
||||||
|
.map { platforms -> platforms.toList() }
|
||||||
.block()
|
.block()
|
||||||
?: throw IllegalStateException("Classifier response body is empty: ${classifierProperties.platformsUrl}")
|
?: throw IllegalStateException("Classifier response body is empty: ${classifierProperties.platformsUrl}")
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-15
@@ -1,29 +1,56 @@
|
|||||||
package space.nstart.pcp_tgu_service.service
|
package space.nstart.pcp_tgu_service.service
|
||||||
|
|
||||||
/** Загружает метаданные платформ КА и применяет простую in-memory фильтрацию. */
|
/** Загружает метаданные платформ КА и применяет простую in-memory фильтрацию. */
|
||||||
import space.nstart.pcp_tgu_service.dto.ContentItemDTO
|
import space.nstart.pcp_tgu_service.config.ClassifierProperties
|
||||||
|
import space.nstart.pcp_tgu_service.domain.PlatformCatalogItem
|
||||||
|
import space.nstart.pcp_tgu_service.dto.NsiPlatformDto
|
||||||
import space.nstart.pcp_tgu_service.integration.api.PlatformsClassifierClient
|
import space.nstart.pcp_tgu_service.integration.api.PlatformsClassifierClient
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class PlatformService(
|
class PlatformService(
|
||||||
private val platformsClassifierClient: PlatformsClassifierClient
|
private val platformsClassifierClient: PlatformsClassifierClient,
|
||||||
|
private val classifierProperties: ClassifierProperties
|
||||||
) {
|
) {
|
||||||
/** Возвращает платформы из классификатора с необязательной фильтрацией по коду типа и статусу. */
|
private val log = LoggerFactory.getLogger(javaClass)
|
||||||
fun loadPlatforms(
|
|
||||||
platformTypeCode: String? = null,
|
|
||||||
status: String? = null
|
|
||||||
): List<ContentItemDTO> {
|
|
||||||
|
|
||||||
val platforms = platformsClassifierClient.fetchPlatforms().content.orEmpty()
|
/** Возвращает платформы из классификатора с необязательной фильтрацией по статусу НСИ. */
|
||||||
|
fun loadPlatforms(status: String? = null): List<PlatformCatalogItem> {
|
||||||
|
val platforms = platformsClassifierClient.fetchPlatforms()
|
||||||
|
.mapNotNull { platform -> platform.toCatalogItem() }
|
||||||
|
|
||||||
return platforms.filter { platform ->
|
return platforms.filter { platform -> status.isNullOrBlank() || platform.status == status }
|
||||||
val matchesPlatformTypeCode = platformTypeCode.isNullOrBlank() ||
|
|
||||||
platform.data?.platformTypeCode == platformTypeCode
|
|
||||||
val matchesStatus = status.isNullOrBlank() ||
|
|
||||||
platform.status == status
|
|
||||||
|
|
||||||
matchesPlatformTypeCode && matchesStatus
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Возвращает платформы, с которыми TGU разрешено работать сейчас. */
|
||||||
|
fun loadAllowedPlatforms(now: Instant = Instant.now()): List<PlatformCatalogItem> =
|
||||||
|
loadPlatforms().filter { platform ->
|
||||||
|
platform.noradId != null &&
|
||||||
|
platform.status in classifierProperties.allowedPlatformStatuses &&
|
||||||
|
platform.validFrom != null &&
|
||||||
|
platform.validTo != null &&
|
||||||
|
!platform.validFrom.isAfter(now) &&
|
||||||
|
now.isBefore(platform.validTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun NsiPlatformDto.toCatalogItem(): PlatformCatalogItem? {
|
||||||
|
val nsiId = id
|
||||||
|
if (nsiId == null) {
|
||||||
|
log.warn("Skipping NSI platform without id. businessKey={}, noradId={}", businessKey, data?.noradId)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return PlatformCatalogItem(
|
||||||
|
nsiId = nsiId,
|
||||||
|
businessKey = businessKey,
|
||||||
|
name = data?.name,
|
||||||
|
status = data?.status,
|
||||||
|
noradId = data?.noradId,
|
||||||
|
mission = data?.mission,
|
||||||
|
validFrom = validFrom,
|
||||||
|
validTo = validTo
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -151,8 +151,8 @@ class SatelliteMissionProcessService(
|
|||||||
/** Проверяет, что КА присутствует во внешнем классификаторе платформ. */
|
/** Проверяет, что КА присутствует во внешнем классификаторе платформ. */
|
||||||
private fun ensurePlatformExists(noradId: Long) {
|
private fun ensurePlatformExists(noradId: Long) {
|
||||||
/** Признак того, что платформа с указанным NORAD-идентификатором существует во внешнем классификаторе. */
|
/** Признак того, что платформа с указанным NORAD-идентификатором существует во внешнем классификаторе. */
|
||||||
val platformExists: Boolean = platformService.loadPlatforms()
|
val platformExists: Boolean = platformService.loadAllowedPlatforms()
|
||||||
.any { platform -> platform.data?.noradId == noradId }
|
.any { platform -> platform.noradId == noradId }
|
||||||
|
|
||||||
if (!platformExists) {
|
if (!platformExists) {
|
||||||
throw ResponseStatusException(
|
throw ResponseStatusException(
|
||||||
|
|||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
package space.nstart.pcp_tgu_service.integration.api
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.HttpServer
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.mockito.Mockito.mock
|
||||||
|
import space.nstart.pcp_tgu_service.config.ClassifierProperties
|
||||||
|
import space.nstart.pcp_tgu_service.config.ExternalApiProperties
|
||||||
|
import space.nstart.pcp_tgu_service.config.PlanningProperties
|
||||||
|
import space.nstart.pcp_tgu_service.domain.PlatformCatalogItem
|
||||||
|
import space.nstart.pcp_tgu_service.service.PlatformService
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.Collections
|
||||||
|
|
||||||
|
class ExternalPointsClientTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `fetchAllSpacecraftPoints uses allowed platforms and keeps noradId as spacecraftId`() {
|
||||||
|
val requestedPaths = Collections.synchronizedList(mutableListOf<String>())
|
||||||
|
|
||||||
|
withServer(requestedPaths) { baseUrl ->
|
||||||
|
val client = ExternalPointsClient(
|
||||||
|
externalApiProperties = ExternalApiProperties(baseUrl = baseUrl),
|
||||||
|
platformService = FixedPlatformService(
|
||||||
|
listOf(
|
||||||
|
platform(nsiId = "allowed-1", noradId = 25544L),
|
||||||
|
platform(nsiId = "allowed-2", noradId = 41386L)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
planningProperties = planningProperties()
|
||||||
|
)
|
||||||
|
|
||||||
|
val points = client.fetchAllSpacecraftPoints()
|
||||||
|
|
||||||
|
assertEquals(1, points.size)
|
||||||
|
assertEquals("25544", points.single().spacecraftId)
|
||||||
|
assertEquals(listOf("7"), points.single().points.map { it.pointId })
|
||||||
|
assertEquals(1, points.single().points.single().observationWindows.size)
|
||||||
|
assertEquals(
|
||||||
|
listOf(
|
||||||
|
"/api/satellites/25544/rva",
|
||||||
|
"/api/satellites/41386/rva"
|
||||||
|
),
|
||||||
|
requestedPaths.map { path -> path.substringBefore("?") }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FixedPlatformService(
|
||||||
|
private val allowedPlatforms: List<PlatformCatalogItem>
|
||||||
|
) : PlatformService(
|
||||||
|
mock(PlatformsClassifierClient::class.java),
|
||||||
|
ClassifierProperties(
|
||||||
|
platformsUrl = "http://localhost",
|
||||||
|
allowedPlatformStatuses = setOf("OPERATIONAL", "STANDBY")
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun loadPlatforms(status: String?): List<PlatformCatalogItem> =
|
||||||
|
error("ExternalPointsClient must use loadAllowedPlatforms")
|
||||||
|
|
||||||
|
override fun loadAllowedPlatforms(now: Instant): List<PlatformCatalogItem> = allowedPlatforms
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun withServer(requestedPaths: MutableList<String>, block: (String) -> Unit) {
|
||||||
|
val server = HttpServer.create(InetSocketAddress(0), 0)
|
||||||
|
server.createContext("/") { exchange ->
|
||||||
|
requestedPaths.add(exchange.requestURI.toString())
|
||||||
|
val responseBody = when (exchange.requestURI.path) {
|
||||||
|
"/api/satellites/25544/rva" -> """[{"noradId":25544,"stationId":7,"revolution":1,"onStart":{"time":"2026-05-26T10:00:00Z","elevation":1.0},"onMaximum":{"time":"2026-05-26T10:05:00Z","elevation":45.0},"onStop":{"time":"2026-05-26T10:10:00Z","elevation":1.0}}]"""
|
||||||
|
else -> """{"error":"failed"}"""
|
||||||
|
}
|
||||||
|
val status = if (exchange.requestURI.path == "/api/satellites/25544/rva") 200 else 500
|
||||||
|
val responseBytes = responseBody.toByteArray()
|
||||||
|
exchange.responseHeaders.add("Content-Type", "application/json")
|
||||||
|
exchange.sendResponseHeaders(status, responseBytes.size.toLong())
|
||||||
|
exchange.responseBody.use { body -> body.write(responseBytes) }
|
||||||
|
}
|
||||||
|
server.start()
|
||||||
|
try {
|
||||||
|
block("http://localhost:${server.address.port}")
|
||||||
|
} finally {
|
||||||
|
server.stop(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun platform(nsiId: String, noradId: Long): PlatformCatalogItem =
|
||||||
|
PlatformCatalogItem(
|
||||||
|
nsiId = nsiId,
|
||||||
|
businessKey = "business-$nsiId",
|
||||||
|
name = "Satellite $nsiId",
|
||||||
|
status = "OPERATIONAL",
|
||||||
|
noradId = noradId,
|
||||||
|
mission = "MISSION",
|
||||||
|
validFrom = Instant.parse("2026-01-01T00:00:00Z"),
|
||||||
|
validTo = Instant.parse("9999-12-31T23:59:59Z")
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun planningProperties(): PlanningProperties =
|
||||||
|
PlanningProperties(
|
||||||
|
notificationBeforeStartMinutes = 30,
|
||||||
|
targetPlanDurationMinutes = 24 * 60L,
|
||||||
|
targetPlanDurationDeltaMinutes = 60L,
|
||||||
|
externalUpdateEnabled = false,
|
||||||
|
externalPointsLookaheadDays = 3,
|
||||||
|
inMemoryHistoryDays = 14,
|
||||||
|
externalUpdateFixedDelayMs = 0,
|
||||||
|
notificationCheckFixedDelayMs = 30_000
|
||||||
|
)
|
||||||
|
}
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
package space.nstart.pcp_tgu_service.integration.api
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
|
import com.sun.net.httpserver.HttpServer
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||||
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import space.nstart.pcp_tgu_service.config.ClassifierProperties
|
||||||
|
import space.nstart.pcp_tgu_service.dto.NsiPlatformDto
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
class PlatformsClassifierClientTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `sample platforms json is deserialized as root array and ignores unknown fields`() {
|
||||||
|
val objectMapper = jacksonObjectMapper().findAndRegisterModules()
|
||||||
|
val json = Files.readString(Path.of("platforms.json"))
|
||||||
|
|
||||||
|
val platforms = objectMapper.readValue(json, Array<NsiPlatformDto>::class.java).toList()
|
||||||
|
|
||||||
|
val operationalPlatform = platforms.first { platform -> platform.data?.status == "OPERATIONAL" }
|
||||||
|
assertNotNull(operationalPlatform.id)
|
||||||
|
assertNotNull(operationalPlatform.businessKey)
|
||||||
|
assertNotNull(operationalPlatform.data?.name)
|
||||||
|
assertEquals("OPERATIONAL", operationalPlatform.data?.status)
|
||||||
|
assertNotNull(operationalPlatform.data?.noradId)
|
||||||
|
assertNotNull(operationalPlatform.validFrom)
|
||||||
|
assertNotNull(operationalPlatform.validTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `fetchPlatforms reads root array response`() {
|
||||||
|
withServer("""[{"id":"nsi-id","businessKey":"business-key","data":{"name":"Satellite","status":"OPERATIONAL","norad_id":25544,"mission":"MISSION","orbit":{"type":"SSO"}},"validFrom":"2026-01-01T00:00:00Z","validTo":"9999-12-31T23:59:59Z","_meta":{"locale":"en-US"}}]""") { baseUrl ->
|
||||||
|
val client = PlatformsClassifierClient(
|
||||||
|
ClassifierProperties(
|
||||||
|
platformsUrl = baseUrl,
|
||||||
|
allowedPlatformStatuses = setOf("OPERATIONAL", "STANDBY")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val platforms = client.fetchPlatforms()
|
||||||
|
|
||||||
|
assertEquals(1, platforms.size)
|
||||||
|
assertEquals("nsi-id", platforms.single().id)
|
||||||
|
assertEquals("business-key", platforms.single().businessKey)
|
||||||
|
assertEquals("Satellite", platforms.single().data?.name)
|
||||||
|
assertEquals("OPERATIONAL", platforms.single().data?.status)
|
||||||
|
assertEquals(25544L, platforms.single().data?.noradId)
|
||||||
|
assertEquals("MISSION", platforms.single().data?.mission)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `fetchPlatforms throws clear error on empty body`() {
|
||||||
|
withServer("") { baseUrl ->
|
||||||
|
val client = PlatformsClassifierClient(
|
||||||
|
ClassifierProperties(
|
||||||
|
platformsUrl = baseUrl,
|
||||||
|
allowedPlatformStatuses = setOf("OPERATIONAL", "STANDBY")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val exception = assertThrows(IllegalStateException::class.java) {
|
||||||
|
client.fetchPlatforms()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("Classifier response body is empty: $baseUrl", exception.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun withServer(responseBody: String, block: (String) -> Unit) {
|
||||||
|
val server = HttpServer.create(InetSocketAddress(0), 0)
|
||||||
|
server.createContext("/") { exchange ->
|
||||||
|
val responseBytes = responseBody.toByteArray()
|
||||||
|
exchange.responseHeaders.add("Content-Type", "application/json")
|
||||||
|
exchange.sendResponseHeaders(200, responseBytes.size.toLong())
|
||||||
|
exchange.responseBody.use { body -> body.write(responseBytes) }
|
||||||
|
}
|
||||||
|
server.start()
|
||||||
|
try {
|
||||||
|
block("http://localhost:${server.address.port}")
|
||||||
|
} finally {
|
||||||
|
server.stop(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
package space.nstart.pcp_tgu_service.service
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.mockito.Mockito.mock
|
||||||
|
import org.mockito.Mockito.`when`
|
||||||
|
import space.nstart.pcp_tgu_service.config.ClassifierProperties
|
||||||
|
import space.nstart.pcp_tgu_service.dto.NsiPlatformDataDto
|
||||||
|
import space.nstart.pcp_tgu_service.dto.NsiPlatformDto
|
||||||
|
import space.nstart.pcp_tgu_service.integration.api.PlatformsClassifierClient
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
class PlatformServiceTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `loadPlatforms maps NSI DTO and filters by status`() {
|
||||||
|
val client = mock(PlatformsClassifierClient::class.java)
|
||||||
|
`when`(client.fetchPlatforms()).thenReturn(
|
||||||
|
listOf(
|
||||||
|
platformDto(id = "operational-id", status = "OPERATIONAL", noradId = 25544L),
|
||||||
|
platformDto(id = "standby-id", status = "STANDBY", noradId = 41386L),
|
||||||
|
platformDto(id = "lost-id", status = "LOST", noradId = 39186L),
|
||||||
|
platformDto(id = null, status = "OPERATIONAL", noradId = 2L)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val service = service(client)
|
||||||
|
|
||||||
|
val allPlatforms = service.loadPlatforms()
|
||||||
|
val platforms = service.loadPlatforms(status = "STANDBY")
|
||||||
|
|
||||||
|
assertEquals(listOf("operational-id", "standby-id", "lost-id"), allPlatforms.map { it.nsiId })
|
||||||
|
assertEquals(1, platforms.size)
|
||||||
|
assertEquals("standby-id", platforms.single().nsiId)
|
||||||
|
assertEquals("business-standby-id", platforms.single().businessKey)
|
||||||
|
assertEquals("Satellite standby-id", platforms.single().name)
|
||||||
|
assertEquals("STANDBY", platforms.single().status)
|
||||||
|
assertEquals(41386L, platforms.single().noradId)
|
||||||
|
assertEquals("MISSION", platforms.single().mission)
|
||||||
|
assertEquals(Instant.parse("2026-01-01T00:00:00Z"), platforms.single().validFrom)
|
||||||
|
assertEquals(Instant.parse("2026-12-31T00:00:00Z"), platforms.single().validTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `loadAllowedPlatforms uses configured statuses noradId and validity interval`() {
|
||||||
|
val now = Instant.parse("2026-05-26T12:00:00Z")
|
||||||
|
val client = mock(PlatformsClassifierClient::class.java)
|
||||||
|
`when`(client.fetchPlatforms()).thenReturn(
|
||||||
|
listOf(
|
||||||
|
platformDto(id = "operational-id", status = "OPERATIONAL", noradId = 1L),
|
||||||
|
platformDto(id = "standby-id", status = "STANDBY", noradId = 2L),
|
||||||
|
platformDto(id = "lost-id", status = "LOST", noradId = 3L),
|
||||||
|
platformDto(id = "without-norad-id", status = "OPERATIONAL", noradId = null),
|
||||||
|
platformDto(id = "future-id", status = "OPERATIONAL", noradId = 4L, validFrom = "2026-06-01T00:00:00Z"),
|
||||||
|
platformDto(id = "expired-id", status = "OPERATIONAL", noradId = 5L, validTo = "2026-05-26T12:00:00Z"),
|
||||||
|
platformDto(id = "without-valid-from", status = "OPERATIONAL", noradId = 6L, validFrom = null),
|
||||||
|
platformDto(id = "without-valid-to", status = "OPERATIONAL", noradId = 7L, validTo = null)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val service = service(client)
|
||||||
|
|
||||||
|
val allowedPlatforms = service.loadAllowedPlatforms(now)
|
||||||
|
|
||||||
|
assertEquals(listOf("operational-id", "standby-id"), allowedPlatforms.map { it.nsiId })
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun service(client: PlatformsClassifierClient): PlatformService =
|
||||||
|
PlatformService(
|
||||||
|
platformsClassifierClient = client,
|
||||||
|
classifierProperties = ClassifierProperties(
|
||||||
|
platformsUrl = "http://localhost",
|
||||||
|
allowedPlatformStatuses = setOf("OPERATIONAL", "STANDBY")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun platformDto(
|
||||||
|
id: String?,
|
||||||
|
status: String,
|
||||||
|
noradId: Long?,
|
||||||
|
validFrom: String? = "2026-01-01T00:00:00Z",
|
||||||
|
validTo: String? = "2026-12-31T00:00:00Z"
|
||||||
|
): NsiPlatformDto =
|
||||||
|
NsiPlatformDto(
|
||||||
|
id = id,
|
||||||
|
businessKey = id?.let { "business-$it" },
|
||||||
|
data = NsiPlatformDataDto(
|
||||||
|
name = id?.let { "Satellite $it" },
|
||||||
|
status = status,
|
||||||
|
noradId = noradId,
|
||||||
|
mission = "MISSION"
|
||||||
|
),
|
||||||
|
validFrom = validFrom?.let(Instant::parse),
|
||||||
|
validTo = validTo?.let(Instant::parse)
|
||||||
|
)
|
||||||
|
}
|
||||||
+41
-19
@@ -13,11 +13,11 @@ import org.mockito.Mockito.verify
|
|||||||
import org.mockito.Mockito.`when`
|
import org.mockito.Mockito.`when`
|
||||||
import org.springframework.http.HttpStatus
|
import org.springframework.http.HttpStatus
|
||||||
import org.springframework.web.server.ResponseStatusException
|
import org.springframework.web.server.ResponseStatusException
|
||||||
|
import space.nstart.pcp_tgu_service.config.ClassifierProperties
|
||||||
import space.nstart.pcp_tgu_service.config.PlanningProperties
|
import space.nstart.pcp_tgu_service.config.PlanningProperties
|
||||||
import space.nstart.pcp_tgu_service.domain.Plan
|
import space.nstart.pcp_tgu_service.domain.Plan
|
||||||
|
import space.nstart.pcp_tgu_service.domain.PlatformCatalogItem
|
||||||
import space.nstart.pcp_tgu_service.domain.SpacecraftPoints
|
import space.nstart.pcp_tgu_service.domain.SpacecraftPoints
|
||||||
import space.nstart.pcp_tgu_service.dto.ContentDataDTO
|
|
||||||
import space.nstart.pcp_tgu_service.dto.ContentItemDTO
|
|
||||||
import space.nstart.pcp_tgu_service.dto.CreateSatelliteMissionVariables
|
import space.nstart.pcp_tgu_service.dto.CreateSatelliteMissionVariables
|
||||||
import space.nstart.pcp_tgu_service.dto.PointMessage
|
import space.nstart.pcp_tgu_service.dto.PointMessage
|
||||||
import space.nstart.pcp_tgu_service.dto.SpacecraftPointsMessage
|
import space.nstart.pcp_tgu_service.dto.SpacecraftPointsMessage
|
||||||
@@ -28,6 +28,7 @@ import space.nstart.pcp_tgu_service.integration.api.PlatformsClassifierClient
|
|||||||
import space.nstart.pcp_tgu_service.repository.PlanRepository
|
import space.nstart.pcp_tgu_service.repository.PlanRepository
|
||||||
import space.nstart.pcp_tgu_service.repository.SpacecraftPointsRepository
|
import space.nstart.pcp_tgu_service.repository.SpacecraftPointsRepository
|
||||||
import space.nstart.pcp_tgu_service.repository.TrackedPlanJpaRepository
|
import space.nstart.pcp_tgu_service.repository.TrackedPlanJpaRepository
|
||||||
|
import java.time.Instant
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@@ -199,10 +200,36 @@ class SatelliteMissionProcessServiceTest {
|
|||||||
verify(planQueryService).reuseExistingPlan(waitingPlan)
|
verify(planQueryService).reuseExistingPlan(waitingPlan)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `startCreateSatelliteMissionManually returns not found when platform is not allowed`() {
|
||||||
|
val service = SatelliteMissionProcessService(
|
||||||
|
camundaClient = mock(CamundaClient::class.java),
|
||||||
|
platformService = FixedPlatformService(emptyList()),
|
||||||
|
spacecraftPointsService = FixedSpacecraftPointsService(
|
||||||
|
SpacecraftPointsMessage("25544", emptyList())
|
||||||
|
),
|
||||||
|
planCalculationService = mock(PlanCalculationService::class.java),
|
||||||
|
planQueryService = mock(PlanQueryService::class.java),
|
||||||
|
trackedPlanService = FixedTrackedPlanService()
|
||||||
|
)
|
||||||
|
|
||||||
|
val exception = assertThrows(ResponseStatusException::class.java) {
|
||||||
|
service.startCreateSatelliteMissionManually(25544L)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(HttpStatus.NOT_FOUND, exception.statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
private class FixedPlatformService(
|
private class FixedPlatformService(
|
||||||
private val platforms: List<ContentItemDTO>
|
private val platforms: List<PlatformCatalogItem>
|
||||||
) : PlatformService(mock(PlatformsClassifierClient::class.java)) {
|
) : PlatformService(
|
||||||
override fun loadPlatforms(platformTypeCode: String?, status: String?): List<ContentItemDTO> = platforms
|
mock(PlatformsClassifierClient::class.java),
|
||||||
|
ClassifierProperties(
|
||||||
|
platformsUrl = "http://localhost",
|
||||||
|
allowedPlatformStatuses = setOf("OPERATIONAL", "STANDBY")
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun loadAllowedPlatforms(now: Instant): List<PlatformCatalogItem> = platforms
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FixedSpacecraftPointsService(
|
private class FixedSpacecraftPointsService(
|
||||||
@@ -284,21 +311,16 @@ class SatelliteMissionProcessServiceTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun platform(noradId: Long): ContentItemDTO =
|
private fun platform(noradId: Long): PlatformCatalogItem =
|
||||||
ContentItemDTO(
|
PlatformCatalogItem(
|
||||||
uuid = UUID.randomUUID().toString(),
|
nsiId = UUID.randomUUID().toString(),
|
||||||
parentItemUuid = null,
|
businessKey = "business-key-$noradId",
|
||||||
status = "ACTIVE",
|
|
||||||
initiation = null,
|
|
||||||
expiration = null,
|
|
||||||
data = ContentDataDTO(
|
|
||||||
platformTypeCode = null,
|
|
||||||
noradId = noradId,
|
|
||||||
umn = null,
|
|
||||||
name = "satellite",
|
name = "satellite",
|
||||||
description = null,
|
status = "OPERATIONAL",
|
||||||
platformTypeName = null
|
noradId = noradId,
|
||||||
)
|
mission = "MISSION",
|
||||||
|
validFrom = Instant.parse("2026-01-01T00:00:00Z"),
|
||||||
|
validTo = Instant.parse("9999-12-31T23:59:59Z")
|
||||||
)
|
)
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
|||||||
Reference in New Issue
Block a user