diff --git a/.scalafmt.conf b/.scalafmt.conf index ac77ca8d47..934a939d8b 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,5 +1,5 @@ version = 3.9.8 -runner.dialect = scala213source3 +runner.dialect = scala3 align.preset = more maxColumn = 120 newlines.topLevelStatementsMinBreaks = 1 diff --git a/article-api/src/main/scala/no/ndla/articleapi/ArticleApiProperties.scala b/article-api/src/main/scala/no/ndla/articleapi/ArticleApiProperties.scala index 53c865ff24..11e13cfdf4 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/ArticleApiProperties.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/ArticleApiProperties.scala @@ -16,7 +16,7 @@ import no.ndla.validation.ResourceType import scala.util.Properties.* trait Props extends HasBaseProps with HasDatabaseProps { - val props: ArticleApiProperties + lazy val props: ArticleApiProperties } class ArticleApiProperties extends BaseProps with DatabaseProps { diff --git a/article-api/src/main/scala/no/ndla/articleapi/ComponentRegistry.scala b/article-api/src/main/scala/no/ndla/articleapi/ComponentRegistry.scala index 46182e9538..36ca856f91 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/ComponentRegistry.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/ComponentRegistry.scala @@ -70,8 +70,8 @@ class ComponentRegistry(properties: ArticleApiProperties) with FrontpageApiClient with ImageApiClient with V55__SetHideBylineForImagesNotCopyrighted { - override val props: ArticleApiProperties = properties - override val migrator: DBMigrator = DBMigrator( + override lazy val props: ArticleApiProperties = properties + override lazy val migrator: DBMigrator = DBMigrator( new R__SetArticleLanguageFromTaxonomy(props), new R__SetArticleTypeFromTaxonomy, new V8__CopyrightFormatUpdated, @@ -81,32 +81,32 @@ class ComponentRegistry(properties: ArticleApiProperties) new V33__ConvertLanguageUnknown(props), new V55__SetHideBylineForImagesNotCopyrighted ) - override val DBUtil: DBUtility = new DBUtility + override lazy val DBUtil: DBUtility = new DBUtility - lazy val dataSource: HikariDataSource = DataSource.getHikariDataSource - lazy val internController = new InternController - lazy val articleControllerV2 = new ArticleControllerV2 - lazy val healthController: TapirHealthController = new TapirHealthController + override lazy val dataSource: HikariDataSource = DataSource.getHikariDataSource + override lazy val internController = new InternController + override lazy val articleControllerV2 = new ArticleControllerV2 + override lazy val healthController: TapirHealthController = new TapirHealthController - lazy val articleRepository = new ArticleRepository - lazy val articleSearchService = new ArticleSearchService - lazy val articleIndexService = new ArticleIndexService + override lazy val articleRepository = new ArticleRepository + override lazy val articleSearchService = new ArticleSearchService + override lazy val articleIndexService = new ArticleIndexService - lazy val converterService = new ConverterService - lazy val contentValidator = new ContentValidator() + override lazy val converterService = new ConverterService + override lazy val contentValidator = new ContentValidator() - lazy val ndlaClient = new NdlaClient - lazy val searchConverterService = new SearchConverterService - lazy val readService = new ReadService - lazy val writeService = new WriteService + override lazy val ndlaClient = new NdlaClient + override lazy val searchConverterService = new SearchConverterService + override lazy val readService = new ReadService + override lazy val writeService = new WriteService - var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer) - lazy val searchApiClient = new SearchApiClient - lazy val feideApiClient = new FeideApiClient - lazy val myndlaApiClient = new MyNDLAApiClient - lazy val redisClient = new RedisClient(props.RedisHost, props.RedisPort) - lazy val frontpageApiClient = new FrontpageApiClient - lazy val imageApiClient = new ImageApiClient + var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer) + override lazy val searchApiClient = new SearchApiClient + override lazy val feideApiClient = new FeideApiClient + override lazy val myndlaApiClient = new MyNDLAApiClient + override lazy val redisClient = new RedisClient(props.RedisHost, props.RedisPort) + override lazy val frontpageApiClient = new FrontpageApiClient + override lazy val imageApiClient = new ImageApiClient lazy val clock = new SystemClock diff --git a/article-api/src/main/scala/no/ndla/articleapi/caching/Memoize.scala b/article-api/src/main/scala/no/ndla/articleapi/caching/Memoize.scala index 6e42bbd1ba..be5cdeac5a 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/caching/Memoize.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/caching/Memoize.scala @@ -17,7 +17,7 @@ class Memoize[R](maxCacheAgeMs: Long, f: () => R, autoRefreshCache: Boolean) ext def isExpired: Boolean = lastUpdated + maxCacheAgeMs <= System.currentTimeMillis() } - private[this] var cache: Option[CacheValue] = None + private var cache: Option[CacheValue] = None private def renewCache(): Unit = { cache = Some(CacheValue(f(), System.currentTimeMillis())) @@ -28,7 +28,7 @@ class Memoize[R](maxCacheAgeMs: Long, f: () => R, autoRefreshCache: Boolean) ext val task = new Runnable { def run(): Unit = renewCache() } - ex.scheduleAtFixedRate(task, 20, maxCacheAgeMs, TimeUnit.MILLISECONDS) + ex.scheduleAtFixedRate(task, 20, maxCacheAgeMs, TimeUnit.MILLISECONDS): Unit } def apply(): R = { diff --git a/article-api/src/main/scala/no/ndla/articleapi/controller/ArticleControllerV2.scala b/article-api/src/main/scala/no/ndla/articleapi/controller/ArticleControllerV2.scala index 95cb50db07..379460bcc5 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/controller/ArticleControllerV2.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/controller/ArticleControllerV2.scala @@ -26,7 +26,6 @@ import no.ndla.network.tapir.Parameters.feideHeader import no.ndla.network.tapir.{DynamicHeaders, TapirController} import no.ndla.network.tapir.TapirUtil.errorOutputsFor import sttp.model.{Header, MediaType} -import sttp.tapir.EndpointIO.annotations.{header, jsonbody} import sttp.tapir.* import sttp.tapir.generic.auto.* import sttp.tapir.server.ServerEndpoint @@ -36,9 +35,7 @@ import scala.util.{Failure, Success, Try} trait ArticleControllerV2 { this: ReadService & WriteService & ArticleSearchService & SearchConverterService & ConverterService & ContentValidator & Props & ErrorHandling & TapirController => - val articleControllerV2: ArticleControllerV2 - - import props.* + lazy val articleControllerV2: ArticleControllerV2 class ArticleControllerV2 extends TapirController { protected val applicationDescription = "Services for accessing articles from NDLA." @@ -79,11 +76,11 @@ trait ArticleControllerV2 { private val fallback = query[Boolean]("fallback").description("Fallback to existing language if language is specified.").default(false) protected val scrollId: EndpointInput.Query[Option[String]] = query[Option[String]]("search-context").description( - s"""A unique string obtained from a search you want to keep scrolling in. To obtain one from a search, provide one of the following values: ${InitialScrollContextKeywords + s"""A unique string obtained from a search you want to keep scrolling in. To obtain one from a search, provide one of the following values: ${props.InitialScrollContextKeywords .mkString("[", ",", "]")}. |When scrolling, the parameters from the initial search is used, except in the case of '${this.language.name}' and '${this.fallback.name}'. - |This value may change between scrolls. Always use the one in the latest scroll result (The context, if unused, dies after $ElasticSearchScrollKeepAlive). - |If you are not paginating past $ElasticSearchIndexMaxResultWindow hits, you can ignore this and use '${this.pageNo.name}' and '${this.pageSize.name}' instead. + |This value may change between scrolls. Always use the one in the latest scroll result (The context, if unused, dies after ${props.ElasticSearchScrollKeepAlive}). + |If you are not paginating past ${props.ElasticSearchIndexMaxResultWindow} hits, you can ignore this and use '${this.pageNo.name}' and '${this.pageSize.name}' instead. |""".stripMargin ) private val grepCodes = listQuery[String]("grep-codes") @@ -91,13 +88,6 @@ trait ArticleControllerV2 { "A comma separated list of codes from GREP API the resources should be filtered by." ) - private case class SummaryWithHeader( - @jsonbody - body: SearchResultV2DTO, - @header("search-context") - searchContext: Option[String] - ) - /** Does a scroll with [[ArticleSearchService]] If no scrollId is specified execute the function @orFunction in the * second parameter list. * @@ -110,7 +100,7 @@ trait ArticleControllerV2 { orFunction: => Try[(SearchResultV2DTO, DynamicHeaders)] ): Try[(SearchResultV2DTO, DynamicHeaders)] = scrollId match { - case Some(scroll) if !InitialScrollContextKeywords.contains(scroll) => + case Some(scroll) if !props.InitialScrollContextKeywords.contains(scroll) => articleSearchService.scroll(scroll, language) match { case Success(scrollResult) => val body = searchConverterService.asApiSearchResultV2(scrollResult) @@ -134,8 +124,8 @@ trait ArticleControllerV2 { .errorOut(errorOutputsFor()) .serverLogicPure { case (query, pageSize, pageNo, language) => val queryOrEmpty = query.getOrElse("") - val parsedPageSize = pageSize.getOrElse(DefaultPageSize) match { - case tooSmall if tooSmall < 1 => DefaultPageSize + val parsedPageSize = pageSize.getOrElse(props.DefaultPageSize) match { + case tooSmall if tooSmall < 1 => props.DefaultPageSize case x => x } val parsedPageNo = pageNo.getOrElse(1) match { @@ -228,9 +218,9 @@ trait ArticleControllerV2 { ) => scrollSearchOr(scrollId, language.code) { val sort = Sort.valueOf(maybeSort.getOrElse("")) - val pageSize = maybePageSize.getOrElse(DefaultPageSize) + val pageSize = maybePageSize.getOrElse(props.DefaultPageSize) val page = maybePageNo.getOrElse(1) - val shouldScroll = scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = scrollId.exists(props.InitialScrollContextKeywords.contains) search( query, @@ -301,12 +291,12 @@ trait ArticleControllerV2 { val query = searchParams.query val sort = searchParams.sort val license = searchParams.license - val pageSize = searchParams.pageSize.getOrElse(DefaultPageSize) + val pageSize = searchParams.pageSize.getOrElse(props.DefaultPageSize) val page = searchParams.page.getOrElse(1) val idList = searchParams.ids.getOrElse(List.empty) val articleTypesFilter = searchParams.articleTypes.getOrElse(List.empty) val grepCodes = searchParams.grepCodes.getOrElse(Seq.empty) - val shouldScroll = searchParams.scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = searchParams.scrollId.exists(props.InitialScrollContextKeywords.contains) search( query, diff --git a/article-api/src/main/scala/no/ndla/articleapi/controller/InternController.scala b/article-api/src/main/scala/no/ndla/articleapi/controller/InternController.scala index 20154a06bb..1667df0d8c 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/controller/InternController.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/controller/InternController.scala @@ -38,7 +38,7 @@ import scala.util.{Failure, Success} trait InternController { this: ReadService & WriteService & ConverterService & ArticleRepository & IndexService & ArticleIndexService & ContentValidator & Props & DBArticle & TapirController => - val internController: InternController + lazy val internController: InternController class InternController extends TapirController with StrictLogging { override val prefix: EndpointInput[Unit] = "intern" diff --git a/article-api/src/main/scala/no/ndla/articleapi/db/migration/V47__RenameAlignAttributeTable.scala b/article-api/src/main/scala/no/ndla/articleapi/db/migration/V47__RenameAlignAttributeTable.scala index 1e1fe8ddde..2410df9b93 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/db/migration/V47__RenameAlignAttributeTable.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/db/migration/V47__RenameAlignAttributeTable.scala @@ -44,7 +44,7 @@ class V47__RenameAlignAttributeTable extends BaseJavaMigration { override def migrate(context: Context): Unit = DB(context.getConnection) .autoClose(false) - .withinTx { session => migrateRows(session) } + .withinTx { session => migrateRows(using session) } private def migrateRows(implicit session: DBSession): Unit = { val count = countAllRows.get diff --git a/article-api/src/main/scala/no/ndla/articleapi/db/migration/V48__ConvertSizeVariantToHideByline.scala b/article-api/src/main/scala/no/ndla/articleapi/db/migration/V48__ConvertSizeVariantToHideByline.scala index 2f25923a64..689009174c 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/db/migration/V48__ConvertSizeVariantToHideByline.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/db/migration/V48__ConvertSizeVariantToHideByline.scala @@ -45,7 +45,7 @@ class V48__ConvertSizeVariantToHideByline extends BaseJavaMigration { override def migrate(context: Context): Unit = DB(context.getConnection) .autoClose(false) - .withinTx { session => migrateRows(session) } + .withinTx { session => migrateRows(using session) } private def migrateRows(implicit session: DBSession): Unit = { val count = countAllRows.get diff --git a/article-api/src/main/scala/no/ndla/articleapi/db/migration/V49__RemoveConceptListEmbeds.scala b/article-api/src/main/scala/no/ndla/articleapi/db/migration/V49__RemoveConceptListEmbeds.scala index 80f6f9fed5..163ccacbb7 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/db/migration/V49__RemoveConceptListEmbeds.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/db/migration/V49__RemoveConceptListEmbeds.scala @@ -45,7 +45,7 @@ class V49__RemoveConceptListEmbeds extends BaseJavaMigration { override def migrate(context: Context): Unit = DB(context.getConnection) .autoClose(false) - .withinTx { session => migrateRows(session) } + .withinTx { session => migrateRows(using session) } private def migrateRows(implicit session: DBSession): Unit = { val count = countAllRows.get diff --git a/article-api/src/main/scala/no/ndla/articleapi/db/migration/V50__RemoveUnsupportedContactBlockAttributes.scala b/article-api/src/main/scala/no/ndla/articleapi/db/migration/V50__RemoveUnsupportedContactBlockAttributes.scala index a3fb7ff167..e51537742b 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/db/migration/V50__RemoveUnsupportedContactBlockAttributes.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/db/migration/V50__RemoveUnsupportedContactBlockAttributes.scala @@ -45,7 +45,7 @@ class V50__RemoveUnsupportedContactBlockAttributes extends BaseJavaMigration { override def migrate(context: Context): Unit = DB(context.getConnection) .autoClose(false) - .withinTx { session => migrateRows(session) } + .withinTx { session => migrateRows(using session) } private def migrateRows(implicit session: DBSession): Unit = { val count = countAllRows.get diff --git a/article-api/src/main/scala/no/ndla/articleapi/db/migration/V54__RemoveStrongFromTitle.scala b/article-api/src/main/scala/no/ndla/articleapi/db/migration/V54__RemoveStrongFromTitle.scala index 4d23a9bc7a..362b70fb65 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/db/migration/V54__RemoveStrongFromTitle.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/db/migration/V54__RemoveStrongFromTitle.scala @@ -45,7 +45,7 @@ class V54__RemoveStrongFromTitle extends BaseJavaMigration { override def migrate(context: Context): Unit = DB(context.getConnection) .autoClose(false) - .withinTx { session => migrateRows(session) } + .withinTx { session => migrateRows(using session) } private def migrateRows(implicit session: DBSession): Unit = { val count = countAllRows.get diff --git a/article-api/src/main/scala/no/ndla/articleapi/db/migrationwithdependencies/R__SetArticleLanguageFromTaxonomy.scala b/article-api/src/main/scala/no/ndla/articleapi/db/migrationwithdependencies/R__SetArticleLanguageFromTaxonomy.scala index f1c9f1be67..aa88673a3b 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/db/migrationwithdependencies/R__SetArticleLanguageFromTaxonomy.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/db/migrationwithdependencies/R__SetArticleLanguageFromTaxonomy.scala @@ -16,8 +16,8 @@ class R__SetArticleLanguageFromTaxonomy(properties: ArticleApiProperties) extends BaseJavaMigration with Props with DBArticle { - override val props: ArticleApiProperties = properties - override def getChecksum: Integer = 1 // Change this to something else if you want to repeat migration + override lazy val props: ArticleApiProperties = properties + override def getChecksum: Integer = 1 // Change this to something else if you want to repeat migration override def migrate(context: Context): Unit = {} } diff --git a/article-api/src/main/scala/no/ndla/articleapi/db/migrationwithdependencies/V33__ConvertLanguageUnknown.scala b/article-api/src/main/scala/no/ndla/articleapi/db/migrationwithdependencies/V33__ConvertLanguageUnknown.scala index b3716f1817..e93ca56877 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/db/migrationwithdependencies/V33__ConvertLanguageUnknown.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/db/migrationwithdependencies/V33__ConvertLanguageUnknown.scala @@ -12,6 +12,6 @@ import no.ndla.articleapi.{ArticleApiProperties, Props} import org.flywaydb.core.api.migration.{BaseJavaMigration, Context} class V33__ConvertLanguageUnknown(properties: ArticleApiProperties) extends BaseJavaMigration with Props { - override val props: ArticleApiProperties = properties - override def migrate(context: Context): Unit = {} + override lazy val props: ArticleApiProperties = properties + override def migrate(context: Context): Unit = {} } diff --git a/article-api/src/main/scala/no/ndla/articleapi/integration/FrontpageApiClient.scala b/article-api/src/main/scala/no/ndla/articleapi/integration/FrontpageApiClient.scala index 4a667ba99e..8c2ece9d18 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/integration/FrontpageApiClient.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/integration/FrontpageApiClient.scala @@ -19,7 +19,7 @@ import scala.util.Try trait FrontpageApiClient { this: NdlaClient & ConverterService & Props => - val frontpageApiClient: FrontpageApiClient + lazy val frontpageApiClient: FrontpageApiClient class FrontpageApiClient(FrontpageApiBaseUrl: String = props.FrontpageApiUrl) extends StrictLogging { diff --git a/article-api/src/main/scala/no/ndla/articleapi/integration/ImageApiClient.scala b/article-api/src/main/scala/no/ndla/articleapi/integration/ImageApiClient.scala index 490c88b6a5..1d4dbafa9f 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/integration/ImageApiClient.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/integration/ImageApiClient.scala @@ -20,7 +20,7 @@ import scala.util.Try trait ImageApiClient { this: NdlaClient & ConverterService & Props => - val imageApiClient: ImageApiClient + lazy val imageApiClient: ImageApiClient class ImageApiClient { private val Endpoint = s"http://${props.ImageApiHost}/image-api/v3/images" diff --git a/article-api/src/main/scala/no/ndla/articleapi/repository/ArticleRepository.scala b/article-api/src/main/scala/no/ndla/articleapi/repository/ArticleRepository.scala index 47ff0828d6..5a0b6ce99d 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/repository/ArticleRepository.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/repository/ArticleRepository.scala @@ -21,7 +21,7 @@ import scala.util.{Failure, Success, Try} trait ArticleRepository { this: DataSource & DBArticle => - val articleRepository: ArticleRepository + lazy val articleRepository: ArticleRepository class ArticleRepository extends StrictLogging { @@ -130,7 +130,7 @@ trait ArticleRepository { ORDER BY revision DESC LIMIT 1 """ - )(session) + )(using session) def withIdAndRevision(articleId: Long, revision: Int): Option[ArticleRow] = { articleWhere( @@ -188,8 +188,8 @@ trait ArticleRepository { private def externalIdsFromResultSet(wrappedResultSet: WrappedResultSet): List[String] = { Option(wrappedResultSet.array("external_id")) - .map(_.getArray.asInstanceOf[Array[String]]) - .getOrElse(Array.empty) + .map(x => x.getArray.asInstanceOf[Array[String]]) + .getOrElse(Array.empty[String]) .toList } diff --git a/article-api/src/main/scala/no/ndla/articleapi/service/ConverterService.scala b/article-api/src/main/scala/no/ndla/articleapi/service/ConverterService.scala index a21dbbb24a..784a0d6ad9 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/service/ConverterService.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/service/ConverterService.scala @@ -49,9 +49,7 @@ import scala.util.{Failure, Success, Try} trait ConverterService { this: Clock & ArticleRepository & Props => - val converterService: ConverterService - - import props.* + lazy val converterService: ConverterService class ConverterService extends StrictLogging { @@ -374,7 +372,7 @@ trait ConverterService { private def toApiArticleMetaImage(metaImage: ArticleMetaImage): api.ArticleMetaImageDTO = { api.ArticleMetaImageDTO( - s"${externalApiUrls("raw-image")}/${metaImage.imageId}", + s"${props.externalApiUrls("raw-image")}/${metaImage.imageId}", metaImage.altText, metaImage.language ) diff --git a/article-api/src/main/scala/no/ndla/articleapi/service/ReadService.scala b/article-api/src/main/scala/no/ndla/articleapi/service/ReadService.scala index 5ee5aa66a7..391c18d1ae 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/service/ReadService.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/service/ReadService.scala @@ -40,7 +40,7 @@ import scala.util.{Failure, Success, Try} trait ReadService { this: ArticleRepository & FeideApiClient & ConverterService & ArticleSearchService & SearchConverterService & MemoizeHelpers & Props & ErrorHandling & FrontpageApiClient => - val readService: ReadService + lazy val readService: ReadService class ReadService extends StrictLogging { def getInternalIdByExternalId(externalId: String): Option[api.ArticleIdV2DTO] = diff --git a/article-api/src/main/scala/no/ndla/articleapi/service/WriteService.scala b/article-api/src/main/scala/no/ndla/articleapi/service/WriteService.scala index e1441c018b..8c415af167 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/service/WriteService.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/service/WriteService.scala @@ -20,7 +20,6 @@ import no.ndla.database.DBUtility import no.ndla.language.Language import scalikejdbc.{AutoSession, DBSession} import cats.implicits.* -import no.ndla.common.implicits.TryQuestionMark import no.ndla.network.clients.SearchApiClient import java.util.concurrent.{ExecutorService, Executors} @@ -30,7 +29,7 @@ import scala.util.{Failure, Success, Try} trait WriteService { this: ArticleRepository & ConverterService & ContentValidator & ArticleIndexService & ReadService & SearchApiClient & DBUtility => - val writeService: WriteService + lazy val writeService: WriteService class WriteService extends StrictLogging { private val executor: ExecutorService = Executors.newSingleThreadExecutor @@ -91,7 +90,7 @@ trait WriteService { skipValidation: Boolean )(session: DBSession = AutoSession): Try[Article] = for { _ <- performArticleValidation(article, externalIds, useSoftValidation, skipValidation, useImportValidation) - domainArticle <- articleRepository.updateArticleFromDraftApi(article, externalIds)(session) + domainArticle <- articleRepository.updateArticleFromDraftApi(article, externalIds)(using session) _ <- articleIndexService.indexDocument(domainArticle) _ <- Try(searchApiClient.indexDocument("article", domainArticle, None)) } yield domainArticle @@ -107,7 +106,7 @@ trait WriteService { case None => Failure(NotFoundException(s"Could not find article with id '$articleId' to partial publish")) case Some(existingArticle) => val newArticle = converterService.updateArticleFields(existingArticle, partialArticle) - val externalIds = articleRepository.getExternalIdsFromId(articleId)(session) + val externalIds = articleRepository.getExternalIdsFromId(articleId)(using session) for { insertedArticle <- updateArticle( newArticle, @@ -122,22 +121,24 @@ trait WriteService { } def partialUpdateBulk(bulkInput: PartialPublishArticlesBulkDTO): Try[Unit] = { - DBUtil.rollbackOnFailure { session => - bulkInput.idTo.toList.traverse { case (id, ppa) => - val updateResult = partialUpdate( - id, - ppa, - Language.AllLanguages, - fallback = true, - isInBulk = true - )(session).unit - - updateResult.recoverWith { case _: NotFoundException => - logger.warn(s"Article with id '$id' was not found when bulk partial publishing") - Success(()) + DBUtil + .rollbackOnFailure { session => + bulkInput.idTo.toList.traverse { case (id, ppa) => + val updateResult = partialUpdate( + id, + ppa, + Language.AllLanguages, + fallback = true, + isInBulk = true + )(session).map(_ => ()) + + updateResult.recoverWith { case _: NotFoundException => + logger.warn(s"Article with id '$id' was not found when bulk partial publishing") + Success(()) + } } } - }.unit + .map(_ => ()) } def unpublishArticle(id: Long, revision: Option[Int]): Try[api.ArticleIdV2DTO] = { diff --git a/article-api/src/main/scala/no/ndla/articleapi/service/search/ArticleIndexService.scala b/article-api/src/main/scala/no/ndla/articleapi/service/search/ArticleIndexService.scala index 8c8615039d..2c2f87c451 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/service/search/ArticleIndexService.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/service/search/ArticleIndexService.scala @@ -19,9 +19,9 @@ import no.ndla.common.model.domain.article.Article trait ArticleIndexService { this: SearchConverterService & IndexService & ArticleRepository & Props => - val articleIndexService: ArticleIndexService + lazy val articleIndexService: ArticleIndexService - class ArticleIndexService extends StrictLogging with IndexService { + class ArticleIndexService extends IndexService with StrictLogging { override val documentType: String = props.ArticleSearchDocument override val searchIndex: String = props.ArticleSearchIndex diff --git a/article-api/src/main/scala/no/ndla/articleapi/service/search/ArticleSearchService.scala b/article-api/src/main/scala/no/ndla/articleapi/service/search/ArticleSearchService.scala index bba2c4ff76..026b5fc395 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/service/search/ArticleSearchService.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/service/search/ArticleSearchService.scala @@ -30,14 +30,12 @@ import scala.util.{Failure, Success, Try} trait ArticleSearchService { this: Elastic4sClient & SearchConverterService & SearchService & ArticleIndexService & ConverterService & Props & ErrorHandling => - val articleSearchService: ArticleSearchService - - import props.* + lazy val articleSearchService: ArticleSearchService class ArticleSearchService extends StrictLogging with SearchService[api.ArticleSummaryV2DTO] { private val noCopyright = boolQuery().not(termQuery("license", License.Copyrighted.toString)) - override val searchIndex: String = ArticleSearchIndex + override val searchIndex: String = props.ArticleSearchIndex override def hitToApiModel(hit: String, language: String): api.ArticleSummaryV2DTO = { converterService.hitAsArticleSummaryV2(hit, language) @@ -114,9 +112,9 @@ trait ArticleSearchService { val (startAt, numResults) = getStartAtAndNumResults(settings.page, settings.pageSize) val requestedResultWindow = settings.pageSize * settings.page - if (requestedResultWindow > ElasticSearchIndexMaxResultWindow) { + if (requestedResultWindow > props.ElasticSearchIndexMaxResultWindow) { logger.info( - s"Max supported results are $ElasticSearchIndexMaxResultWindow, user requested $requestedResultWindow" + s"Max supported results are ${props.ElasticSearchIndexMaxResultWindow}, user requested $requestedResultWindow" ) Failure(ArticleErrorHelpers.ResultWindowTooLargeException()) } else { @@ -132,7 +130,7 @@ trait ArticleSearchService { // Only add scroll param if it is first page val searchWithScroll = if (startAt == 0 && settings.shouldScroll) { - searchToExecute.scroll(ElasticSearchScrollKeepAlive) + searchToExecute.scroll(props.ElasticSearchScrollKeepAlive) } else { searchToExecute } e4sClient.execute(searchWithScroll) match { diff --git a/article-api/src/main/scala/no/ndla/articleapi/service/search/IndexService.scala b/article-api/src/main/scala/no/ndla/articleapi/service/search/IndexService.scala index 45363dfde0..4b90e2ee33 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/service/search/IndexService.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/service/search/IndexService.scala @@ -24,7 +24,7 @@ import scala.util.{Failure, Success, Try} trait IndexService { this: Elastic4sClient & BaseIndexService & Props & ArticleRepository & SearchLanguage => - trait IndexService extends BaseIndexService with StrictLogging { + abstract class IndexService extends BaseIndexService with StrictLogging { override val MaxResultWindowOption: Int = props.ElasticSearchIndexMaxResultWindow def createIndexRequest(domainModel: Article, indexName: String): IndexRequest diff --git a/article-api/src/main/scala/no/ndla/articleapi/service/search/SearchConverterService.scala b/article-api/src/main/scala/no/ndla/articleapi/service/search/SearchConverterService.scala index 8450cb776c..65f015c3ee 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/service/search/SearchConverterService.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/service/search/SearchConverterService.scala @@ -19,7 +19,7 @@ import org.jsoup.Jsoup trait SearchConverterService { this: ConverterService & SearchLanguage => - val searchConverterService: SearchConverterService + lazy val searchConverterService: SearchConverterService class SearchConverterService extends StrictLogging { diff --git a/article-api/src/main/scala/no/ndla/articleapi/validation/ContentValidator.scala b/article-api/src/main/scala/no/ndla/articleapi/validation/ContentValidator.scala index f57ccde539..b5d84ec6e3 100644 --- a/article-api/src/main/scala/no/ndla/articleapi/validation/ContentValidator.scala +++ b/article-api/src/main/scala/no/ndla/articleapi/validation/ContentValidator.scala @@ -26,7 +26,7 @@ import scala.util.{Failure, Success, Try} trait ContentValidator { this: ArticleRepository & Props => - val contentValidator: ContentValidator + lazy val contentValidator: ContentValidator class ContentValidator { private val inlineHtmlTags = props.InlineHtmlTags diff --git a/article-api/src/test/scala/no/ndla/articleapi/TestData.scala b/article-api/src/test/scala/no/ndla/articleapi/TestData.scala index 3fb81aaa76..104cb99a5c 100644 --- a/article-api/src/test/scala/no/ndla/articleapi/TestData.scala +++ b/article-api/src/test/scala/no/ndla/articleapi/TestData.scala @@ -18,10 +18,10 @@ import no.ndla.common.model.domain.{article, *} import no.ndla.common.model.domain.language.OptLanguageFields import no.ndla.mapping.License -trait TestData { +trait TestDataT { this: Props => - class TestData { + class TestDataClass { private val publicDomainCopyright = Copyright(License.PublicDomain.toString, None, List(), List(), List(), None, None, false) private val byNcSaCopyright = diff --git a/article-api/src/test/scala/no/ndla/articleapi/TestEnvironment.scala b/article-api/src/test/scala/no/ndla/articleapi/TestEnvironment.scala index 45f3240737..8db6b96369 100644 --- a/article-api/src/test/scala/no/ndla/articleapi/TestEnvironment.scala +++ b/article-api/src/test/scala/no/ndla/articleapi/TestEnvironment.scala @@ -54,46 +54,47 @@ trait TestEnvironment with MemoizeHelpers with DBArticle with Props - with TestData + with TestDataT with DBMigrator with SearchLanguage with FrontpageApiClient with ImageApiClient { - lazy val props: ArticleApiProperties = new ArticleApiProperties { + override lazy val props: ArticleApiProperties = new ArticleApiProperties { override def InlineHtmlTags: Set[String] = Set("code", "em", "span", "strong", "sub", "sup") override def IntroductionHtmlTags: Set[String] = InlineHtmlTags ++ Set("br", "p") } - val TestData: TestData = new TestData - val migrator: DBMigrator = mock[DBMigrator] - val DBUtil: DBUtility = mock[DBUtility] - val articleSearchService: ArticleSearchService = mock[ArticleSearchService] - val articleIndexService: ArticleIndexService = mock[ArticleIndexService] + lazy val TestData: TestDataClass = new TestDataClass + override lazy val migrator: DBMigrator = mock[DBMigrator] + override lazy val DBUtil: DBUtility = mock[DBUtility] - val internController: InternController = mock[InternController] - val articleControllerV2: ArticleControllerV2 = mock[ArticleControllerV2] + override lazy val articleSearchService: ArticleSearchService = mock[ArticleSearchService] + override lazy val articleIndexService: ArticleIndexService = mock[ArticleIndexService] - val healthController: TapirHealthController = mock[TapirHealthController] + override lazy val internController: InternController = mock[InternController] + override lazy val articleControllerV2: ArticleControllerV2 = mock[ArticleControllerV2] - val dataSource: HikariDataSource = mock[HikariDataSource] - val articleRepository: ArticleRepository = mock[ArticleRepository] + override lazy val healthController: TapirHealthController = mock[TapirHealthController] - val converterService: ConverterService = mock[ConverterService] - val readService: ReadService = mock[ReadService] - val writeService: WriteService = mock[WriteService] - val contentValidator: ContentValidator = mock[ContentValidator] + override lazy val dataSource: HikariDataSource = mock[HikariDataSource] + override lazy val articleRepository: ArticleRepository = mock[ArticleRepository] - val ndlaClient: NdlaClient = mock[NdlaClient] - val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] - val searchConverterService: SearchConverterService = mock[SearchConverterService] - var e4sClient: NdlaE4sClient = mock[NdlaE4sClient] - val searchApiClient: SearchApiClient = mock[SearchApiClient] - val feideApiClient: FeideApiClient = mock[FeideApiClient] - val redisClient: RedisClient = mock[RedisClient] - val frontpageApiClient: FrontpageApiClient = mock[FrontpageApiClient] - val imageApiClient: ImageApiClient = mock[ImageApiClient] + override lazy val converterService: ConverterService = mock[ConverterService] + override lazy val readService: ReadService = mock[ReadService] + override lazy val writeService: WriteService = mock[WriteService] + override lazy val contentValidator: ContentValidator = mock[ContentValidator] - val clock: SystemClock = mock[SystemClock] + override lazy val ndlaClient: NdlaClient = mock[NdlaClient] + override lazy val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] + override lazy val searchConverterService: SearchConverterService = mock[SearchConverterService] + var e4sClient: NdlaE4sClient = mock[NdlaE4sClient] + override lazy val searchApiClient: SearchApiClient = mock[SearchApiClient] + override lazy val feideApiClient: FeideApiClient = mock[FeideApiClient] + override lazy val redisClient: RedisClient = mock[RedisClient] + override lazy val frontpageApiClient: FrontpageApiClient = mock[FrontpageApiClient] + override lazy val imageApiClient: ImageApiClient = mock[ImageApiClient] + + override lazy val clock: SystemClock = mock[SystemClock] def services: List[TapirController] = List.empty val swagger: SwaggerController = mock[SwaggerController] diff --git a/article-api/src/test/scala/no/ndla/articleapi/controller/ArticleControllerV2Test.scala b/article-api/src/test/scala/no/ndla/articleapi/controller/ArticleControllerV2Test.scala index 9c5c66a90d..5f0cf5cafd 100644 --- a/article-api/src/test/scala/no/ndla/articleapi/controller/ArticleControllerV2Test.scala +++ b/article-api/src/test/scala/no/ndla/articleapi/controller/ArticleControllerV2Test.scala @@ -38,7 +38,7 @@ class ArticleControllerV2Test extends UnitSuite with TestEnvironment with TapirC val authHeaderWithWrongRole = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik9FSTFNVVU0T0RrNU56TTVNekkyTXpaRE9EazFOMFl3UXpkRE1EUXlPRFZDUXpRM1FUSTBNQSJ9.eyJodHRwczovL25kbGEubm8vY2xpZW50X2lkIjoieHh4eXl5IiwiaXNzIjoiaHR0cHM6Ly9uZGxhLmV1LmF1dGgwLmNvbS8iLCJzdWIiOiJ4eHh5eXlAY2xpZW50cyIsImF1ZCI6Im5kbGFfc3lzdGVtIiwiaWF0IjoxNTEwMzA1NzczLCJleHAiOjE1MTAzOTIxNzMsInNjb3BlIjoic29tZTpvdGhlciIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.Hbmh9KX19nx7yT3rEcP9pyzRO0uQJBRucfqH9QEZtLyXjYj_fAyOhsoicOVEbHSES7rtdiJK43-gijSpWWmGWOkE6Ym7nHGhB_nLdvp_25PDgdKHo-KawZdAyIcJFr5_t3CJ2Z2IPVbrXwUd99vuXEBaV0dMwkT0kDtkwHuS-8E" - lazy val controller: ArticleControllerV2 = new ArticleControllerV2 + override val controller: ArticleControllerV2 = new ArticleControllerV2 override def beforeEach(): Unit = { reset(clock) diff --git a/article-api/src/test/scala/no/ndla/articleapi/repository/ArticleRepositoryTest.scala b/article-api/src/test/scala/no/ndla/articleapi/repository/ArticleRepositoryTest.scala index d156cd8de0..2ccc7f04c6 100644 --- a/article-api/src/test/scala/no/ndla/articleapi/repository/ArticleRepositoryTest.scala +++ b/article-api/src/test/scala/no/ndla/articleapi/repository/ArticleRepositoryTest.scala @@ -16,14 +16,15 @@ import no.ndla.common.model.domain.Tag import no.ndla.common.model.domain.article.Article import no.ndla.scalatestsuite.DatabaseIntegrationSuite import scalikejdbc.AutoSession +import org.scalatest.EitherValues.convertEitherToValuable import java.net.Socket import scala.util.{Success, Try} class ArticleRepositoryTest extends DatabaseIntegrationSuite with UnitSuite with TestEnvironment { - override val dataSource: HikariDataSource = testDataSource.get - override val migrator = new DBMigrator - var repository: ArticleRepository = _ + override lazy val dataSource: HikariDataSource = testDataSource.get + override lazy val migrator = new DBMigrator + var repository: ArticleRepository = _ lazy val sampleArticle: Article = TestData.sampleArticleWithByNcSa @@ -86,7 +87,7 @@ class ArticleRepositoryTest extends DatabaseIntegrationSuite with UnitSuite with val externalIds = List("123", "456") val sampleArticle: Article = TestData.sampleDomainArticle.copy(id = Some(5), revision = Some(42)) - val Success(res: Article) = repository.updateArticleFromDraftApi(sampleArticle, externalIds) + val res = repository.updateArticleFromDraftApi(sampleArticle, externalIds).get res.id.isDefined should be(true) repository.withId(res.id.get)(AutoSession).get.article.get should be(sampleArticle) @@ -226,7 +227,7 @@ class ArticleRepositoryTest extends DatabaseIntegrationSuite with UnitSuite with List("6000", "10") ) - val Right(relatedId) = repository.withId(1)(AutoSession).toArticle.get.relatedContent.head + val relatedId = repository.withId(1)(AutoSession).toArticle.get.relatedContent.head.value relatedId should be(2L) } diff --git a/article-api/src/test/scala/no/ndla/articleapi/service/ReadServiceTest.scala b/article-api/src/test/scala/no/ndla/articleapi/service/ReadServiceTest.scala index 0521ee1025..6a43845eb4 100644 --- a/article-api/src/test/scala/no/ndla/articleapi/service/ReadServiceTest.scala +++ b/article-api/src/test/scala/no/ndla/articleapi/service/ReadServiceTest.scala @@ -56,8 +56,8 @@ class ReadServiceTest extends UnitSuite with TestEnvironment { val articleContent2: ArticleContent = ArticleContent(content2, "und") - override val readService = new ReadService - override val converterService = new ConverterService + override lazy val readService = new ReadService + override lazy val converterService = new ConverterService override def beforeEach(): Unit = { reset(feideApiClient) @@ -182,15 +182,17 @@ class ReadServiceTest extends UnitSuite with TestEnvironment { .thenReturn(Seq(toArticleRow(article1), toArticleRow(article2), toArticleRow(article3))) when(articleRepository.getExternalIdsFromId(any)(any)).thenReturn(List(""), List(""), List("")) - val Success(result) = - readService.getArticlesByIds( - articleIds = ids, - language = "nb", - fallback = true, - page = 1, - pageSize = 10, - feideAccessToken = None - ) + val result = + readService + .getArticlesByIds( + articleIds = ids, + language = "nb", + fallback = true, + page = 1, + pageSize = 10, + feideAccessToken = None + ) + .get result.length should be(3) verify(feideApiClient, times(0)).getFeideExtendedUser(Some(feideId)) @@ -209,15 +211,17 @@ class ReadServiceTest extends UnitSuite with TestEnvironment { .thenReturn(Seq(toArticleRow(article1), toArticleRow(article2), toArticleRow(article3))) when(articleRepository.getExternalIdsFromId(any)(any)).thenReturn(List(""), List(""), List("")) - val Success(result) = - readService.getArticlesByIds( - articleIds = ids, - language = "nb", - fallback = true, - page = 1, - pageSize = 10, - feideAccessToken = Some(feideId) - ) + val result = + readService + .getArticlesByIds( + articleIds = ids, + language = "nb", + fallback = true, + page = 1, + pageSize = 10, + feideAccessToken = Some(feideId) + ) + .get result.length should be(3) verify(feideApiClient, times(1)).getFeideExtendedUser(Some(feideId)) @@ -236,15 +240,17 @@ class ReadServiceTest extends UnitSuite with TestEnvironment { .thenReturn(Seq(toArticleRow(article1), toArticleRow(article2), toArticleRow(article3))) when(articleRepository.getExternalIdsFromId(any)(any)).thenReturn(List(""), List(""), List("")) - val Success(result) = - readService.getArticlesByIds( - articleIds = ids, - language = "nb", - fallback = true, - page = 1, - pageSize = 10, - feideAccessToken = Some(feideId) - ) + val result = + readService + .getArticlesByIds( + articleIds = ids, + language = "nb", + fallback = true, + page = 1, + pageSize = 10, + feideAccessToken = Some(feideId) + ) + .get result.length should be(2) result.map(res => res.availability).contains("teacher") should be(false) @@ -263,15 +269,17 @@ class ReadServiceTest extends UnitSuite with TestEnvironment { when(articleRepository.getExternalIdsFromId(any)(any)).thenReturn(List(""), List(""), List("")) when(feideApiClient.getFeideExtendedUser(any)).thenReturn(Failure(new RuntimeException)) - val Success(result) = - readService.getArticlesByIds( - articleIds = ids, - language = "nb", - fallback = true, - page = 1, - pageSize = 10, - feideAccessToken = None - ) + val result = + readService + .getArticlesByIds( + articleIds = ids, + language = "nb", + fallback = true, + page = 1, + pageSize = 10, + feideAccessToken = None + ) + .get result.length should be(2) result.map(res => res.availability).contains("teacher") should be(false) @@ -280,16 +288,21 @@ class ReadServiceTest extends UnitSuite with TestEnvironment { test("that getArticlesByIds fails if no ids were given") { reset(articleRepository) - val Failure(result: ValidationException) = - readService.getArticlesByIds( - articleIds = List.empty, - language = "nb", - fallback = true, - page = 1, - pageSize = 10, - feideAccessToken = None - ) - result.errors.head.message should be("Query parameter 'ids' is missing") + val result = + readService + .getArticlesByIds( + articleIds = List.empty, + language = "nb", + fallback = true, + page = 1, + pageSize = 10, + feideAccessToken = None + ) + result.failed.get + .asInstanceOf[ValidationException] + .errors + .head + .message should be("Query parameter 'ids' is missing") verify(articleRepository, times(0)).withIds(any, any, any)(any) } diff --git a/article-api/src/test/scala/no/ndla/articleapi/service/WriteServiceTest.scala b/article-api/src/test/scala/no/ndla/articleapi/service/WriteServiceTest.scala index b85afd45f1..0670526748 100644 --- a/article-api/src/test/scala/no/ndla/articleapi/service/WriteServiceTest.scala +++ b/article-api/src/test/scala/no/ndla/articleapi/service/WriteServiceTest.scala @@ -21,7 +21,7 @@ import scalikejdbc.DBSession import scala.util.{Success, Try} class WriteServiceTest extends UnitSuite with TestEnvironment { - override val converterService = new ConverterService + override lazy val converterService = new ConverterService val today: NDLADate = NDLADate.now() val yesterday: NDLADate = NDLADate.now().minusDays(1) diff --git a/article-api/src/test/scala/no/ndla/articleapi/service/search/ArticleSearchConverterServiceTest.scala b/article-api/src/test/scala/no/ndla/articleapi/service/search/ArticleSearchConverterServiceTest.scala index f68bc6eea7..0f7113c9ff 100644 --- a/article-api/src/test/scala/no/ndla/articleapi/service/search/ArticleSearchConverterServiceTest.scala +++ b/article-api/src/test/scala/no/ndla/articleapi/service/search/ArticleSearchConverterServiceTest.scala @@ -16,8 +16,8 @@ import no.ndla.search.model.{SearchableLanguageList, SearchableLanguageValues} class ArticleSearchConverterServiceTest extends UnitSuite with TestEnvironment { - override val searchConverterService = new SearchConverterService - val sampleArticle: Article = TestData.sampleArticleWithPublicDomain.copy() + override lazy val searchConverterService = new SearchConverterService + val sampleArticle: Article = TestData.sampleArticleWithPublicDomain.copy() val titles: List[Title] = List( Title("Bokmål tittel", "nb"), diff --git a/article-api/src/test/scala/no/ndla/articleapi/service/search/ArticleSearchServiceTest.scala b/article-api/src/test/scala/no/ndla/articleapi/service/search/ArticleSearchServiceTest.scala index 5de1167b92..e3ec597031 100644 --- a/article-api/src/test/scala/no/ndla/articleapi/service/search/ArticleSearchServiceTest.scala +++ b/article-api/src/test/scala/no/ndla/articleapi/service/search/ArticleSearchServiceTest.scala @@ -18,20 +18,15 @@ import no.ndla.language.Language import no.ndla.mapping.License.{CC_BY_NC_SA, Copyrighted, PublicDomain} import no.ndla.scalatestsuite.ElasticsearchIntegrationSuite -import scala.util.Success - class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite with TestEnvironment { - import TestData.testSettings - import props.* - e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("http://localhost:9200")) - override val articleSearchService = new ArticleSearchService - override val articleIndexService: ArticleIndexService = new ArticleIndexService { + override lazy val articleSearchService = new ArticleSearchService + override lazy val articleIndexService: ArticleIndexService = new ArticleIndexService { override val indexShards = 1 } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService val byNcSa: Copyright = Copyright( @@ -250,97 +245,101 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSu } test("That getStartAtAndNumResults returns SEARCH_MAX_PAGE_SIZE for value greater than SEARCH_MAX_PAGE_SIZE") { - articleSearchService.getStartAtAndNumResults(0, 10001) should equal((0, MaxPageSize)) + articleSearchService.getStartAtAndNumResults(0, 10001) should equal((0, props.MaxPageSize)) } test( "That getStartAtAndNumResults returns the correct calculated start at for page and page-size with default page-size" ) { val page = 74 - val expectedStartAt = (page - 1) * DefaultPageSize - articleSearchService.getStartAtAndNumResults(page, DefaultPageSize) should equal((expectedStartAt, DefaultPageSize)) + val expectedStartAt = (page - 1) * props.DefaultPageSize + articleSearchService.getStartAtAndNumResults(page, props.DefaultPageSize) should equal( + (expectedStartAt, props.DefaultPageSize) + ) } test("That getStartAtAndNumResults returns the correct calculated start at for page and page-size") { val page = 123 - val expectedStartAt = (page - 1) * DefaultPageSize - articleSearchService.getStartAtAndNumResults(page, DefaultPageSize) should equal((expectedStartAt, DefaultPageSize)) + val expectedStartAt = (page - 1) * props.DefaultPageSize + articleSearchService.getStartAtAndNumResults(page, props.DefaultPageSize) should equal( + (expectedStartAt, props.DefaultPageSize) + ) } test("searching should return only articles of a given type if a type filter is specified") { - val Success(results) = - articleSearchService.matchingQuery(testSettings.copy(articleTypes = Seq(ArticleType.TopicArticle.entryName))) + val results = articleSearchService + .matchingQuery(TestData.testSettings.copy(articleTypes = Seq(ArticleType.TopicArticle.entryName))) + .get results.totalCount should be(3) - val Success(results2) = articleSearchService.matchingQuery(testSettings.copy(articleTypes = Seq.empty)) + val results2 = articleSearchService.matchingQuery(TestData.testSettings.copy(articleTypes = Seq.empty)).get results2.totalCount should be(10) } test("That searching without query returns all documents ordered by id ascending") { - val Success(results) = - articleSearchService.matchingQuery(testSettings.copy(query = None)) - val hits = results.results + val results = articleSearchService.matchingQuery(TestData.testSettings.copy(query = None)).get + val hits = results.results results.totalCount should be(10) hits.map(_.id) should be(Seq(1, 2, 3, 5, 6, 7, 8, 9, 11, 13)) } test("That searching returns all documents ordered by id descending") { - val Success(results) = articleSearchService.matchingQuery(testSettings.copy(sort = Sort.ByIdDesc)) - val hits = results.results + val results = articleSearchService.matchingQuery(TestData.testSettings.copy(sort = Sort.ByIdDesc)).get + val hits = results.results results.totalCount should be(10) hits.map(_.id) should be(Seq(13, 11, 9, 8, 7, 6, 5, 3, 2, 1)) } test("That searching returns all documents ordered by title ascending") { - val Success(results) = articleSearchService.matchingQuery(testSettings.copy(sort = Sort.ByTitleAsc)) - val hits = results.results + val results = articleSearchService.matchingQuery(TestData.testSettings.copy(sort = Sort.ByTitleAsc)).get + val hits = results.results results.totalCount should be(10) hits.map(_.id) should be(Seq(8, 1, 3, 9, 5, 11, 6, 2, 7, 13)) } test("That searching returns all documents ordered by title descending") { - val Success(results) = articleSearchService.matchingQuery(testSettings.copy(sort = Sort.ByTitleDesc)) - val hits = results.results + val results = articleSearchService.matchingQuery(TestData.testSettings.copy(sort = Sort.ByTitleDesc)).get + val hits = results.results results.totalCount should be(10) hits.map(_.id) should be(Seq(13, 7, 2, 6, 11, 5, 9, 3, 1, 8)) } test("That searching returns all documents ordered by lastUpdated descending") { - val results = articleSearchService.matchingQuery(testSettings.copy(sort = Sort.ByLastUpdatedDesc)) + val results = articleSearchService.matchingQuery(TestData.testSettings.copy(sort = Sort.ByLastUpdatedDesc)) val hits = results.get.results results.get.totalCount should be(10) hits.map(_.id) should be(Seq(3, 2, 1, 8, 9, 11, 13, 7, 6, 5)) } test("That all returns all documents ordered by lastUpdated ascending") { - val Success(results) = articleSearchService.matchingQuery(testSettings.copy(sort = Sort.ByLastUpdatedAsc)) - val hits = results.results + val results = articleSearchService.matchingQuery(TestData.testSettings.copy(sort = Sort.ByLastUpdatedAsc)).get + val hits = results.results results.totalCount should be(10) hits.map(_.id) should be(Seq(5, 6, 7, 8, 9, 11, 13, 1, 2, 3)) } test("That all filtering on license only returns documents with given license") { - val Success(results) = articleSearchService.matchingQuery( - testSettings.copy(sort = Sort.ByTitleAsc, license = Some(PublicDomain.toString)) - ) + val results = articleSearchService + .matchingQuery(TestData.testSettings.copy(sort = Sort.ByTitleAsc, license = Some(PublicDomain.toString))) + .get val hits = results.results results.totalCount should be(9) hits.map(_.id) should be(Seq(8, 3, 9, 5, 11, 6, 2, 7, 13)) } test("That all filtered by id only returns documents with the given ids") { - val Success(results) = articleSearchService.matchingQuery(testSettings.copy(withIdIn = List(1, 3))) - val hits = results.results + val results = articleSearchService.matchingQuery(TestData.testSettings.copy(withIdIn = List(1, 3))).get + val hits = results.results results.totalCount should be(2) hits.head.id should be(1) hits.last.id should be(3) } test("That paging returns only hits on current page and not more than page-size") { - val Success(page1) = - articleSearchService.matchingQuery(testSettings.copy(sort = Sort.ByTitleAsc, page = 1, pageSize = 2)) - val Success(page2) = - articleSearchService.matchingQuery(testSettings.copy(sort = Sort.ByTitleAsc, page = 2, pageSize = 2)) + val page1 = + articleSearchService.matchingQuery(TestData.testSettings.copy(sort = Sort.ByTitleAsc, page = 1, pageSize = 2)).get + val page2 = + articleSearchService.matchingQuery(TestData.testSettings.copy(sort = Sort.ByTitleAsc, page = 2, pageSize = 2)).get val hits1 = page1.results val hits2 = page2.results @@ -358,30 +357,32 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSu test("matchingQuery should filter results based on an article type filter") { val results = articleSearchService.matchingQuery( - testSettings + TestData.testSettings .copy(query = Some("bil"), sort = Sort.ByRelevanceDesc, articleTypes = Seq(ArticleType.TopicArticle.entryName)) ) results.get.totalCount should be(0) val results2 = articleSearchService.matchingQuery( - testSettings + TestData.testSettings .copy(query = Some("bil"), sort = Sort.ByRelevanceDesc, articleTypes = Seq(ArticleType.Standard.entryName)) ) results2.get.totalCount should be(3) } test("That search matches title and html-content ordered by relevance descending") { - val Success(results) = - articleSearchService.matchingQuery(testSettings.copy(query = Some("bil"), sort = Sort.ByRelevanceDesc)) + val results = + articleSearchService + .matchingQuery(TestData.testSettings.copy(query = Some("bil"), sort = Sort.ByRelevanceDesc)) + .get val hits = results.results results.totalCount should be(3) hits.map(_.id) should be(Seq(1, 5, 3)) } test("That search combined with filter by id only returns documents matching the query with one of the given ids") { - val Success(results) = articleSearchService.matchingQuery( - testSettings.copy(query = Some("bil"), withIdIn = List(3), sort = Sort.ByRelevanceDesc) - ) + val results = articleSearchService + .matchingQuery(TestData.testSettings.copy(query = Some("bil"), withIdIn = List(3), sort = Sort.ByRelevanceDesc)) + .get val hits = results.results results.totalCount should be(1) hits.head.id should be(3) @@ -389,77 +390,104 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSu } test("That search matches title") { - val Success(results) = - articleSearchService.matchingQuery(testSettings.copy(query = Some("Pingvinen"), sort = Sort.ByTitleAsc)) + val results = + articleSearchService + .matchingQuery(TestData.testSettings.copy(query = Some("Pingvinen"), sort = Sort.ByTitleAsc)) + .get val hits = results.results results.totalCount should be(1) hits.head.id should be(2) } test("That search matches tags") { - val Success(results) = - articleSearchService.matchingQuery(testSettings.copy(query = Some("and"), sort = Sort.ByTitleAsc)) + val results = + articleSearchService.matchingQuery(TestData.testSettings.copy(query = Some("and"), sort = Sort.ByTitleAsc)).get val hits = results.results results.totalCount should be(1) hits.head.id should be(3) } test("That search does not return superman since it has license copyrighted and license is not specified") { - val Success(results) = - articleSearchService.matchingQuery(testSettings.copy(query = Some("supermann"), sort = Sort.ByTitleAsc)) + val results = + articleSearchService + .matchingQuery(TestData.testSettings.copy(query = Some("supermann"), sort = Sort.ByTitleAsc)) + .get results.totalCount should be(0) } test("That search returns superman since license is specified as copyrighted") { - val Success(results) = articleSearchService.matchingQuery( - testSettings.copy(query = Some("supermann"), sort = Sort.ByTitleAsc, license = Some(Copyrighted.toString)) - ) + val results = articleSearchService + .matchingQuery( + TestData.testSettings + .copy(query = Some("supermann"), sort = Sort.ByTitleAsc, license = Some(Copyrighted.toString)) + ) + .get val hits = results.results results.totalCount should be(1) hits.head.id should be(4) } test("Searching with logical AND only returns results with all terms") { - val Success(search1) = - articleSearchService.matchingQuery(testSettings.copy(query = Some("bilde + bil"), sort = Sort.ByTitleAsc)) + val search1 = + articleSearchService + .matchingQuery( + TestData.testSettings.copy(query = Some("bilde + bil"), sort = Sort.ByTitleAsc) + ) + .get val hits1 = search1.results hits1.map(_.id) should equal(Seq(1, 3, 5)) - val Success(search2) = - articleSearchService.matchingQuery(testSettings.copy(query = Some("batmen + bil"), sort = Sort.ByTitleAsc)) + val search2 = + articleSearchService + .matchingQuery( + TestData.testSettings.copy(query = Some("batmen + bil"), sort = Sort.ByTitleAsc) + ) + .get val hits2 = search2.results hits2.map(_.id) should equal(Seq(1)) - val Success(search3) = articleSearchService.matchingQuery( - testSettings.copy(query = Some("bil + bilde + -flaggermusmann"), sort = Sort.ByTitleAsc) - ) + val search3 = articleSearchService + .matchingQuery( + TestData.testSettings.copy(query = Some("bil + bilde + -flaggermusmann"), sort = Sort.ByTitleAsc) + ) + .get val hits3 = search3.results hits3.map(_.id) should equal(Seq(3, 5)) - val Success(search4) = - articleSearchService.matchingQuery(testSettings.copy(query = Some("bil + -hulken"), sort = Sort.ByTitleAsc)) + val search4 = + articleSearchService + .matchingQuery( + TestData.testSettings.copy(query = Some("bil + -hulken"), sort = Sort.ByTitleAsc) + ) + .get val hits4 = search4.results hits4.map(_.id) should equal(Seq(1, 3)) } test("search in content should be ranked lower than introduction and title") { - val Success(search) = articleSearchService.matchingQuery( - testSettings.copy(query = Some("mareritt+ragnarok"), sort = Sort.ByRelevanceDesc) - ) + val search = articleSearchService + .matchingQuery( + TestData.testSettings.copy(query = Some("mareritt+ragnarok"), sort = Sort.ByRelevanceDesc) + ) + .get val hits = search.results hits.map(_.id) should equal(Seq(9, 8)) } test("Search for all languages should return all articles in different languages") { - val Success(search) = articleSearchService.matchingQuery( - testSettings.copy(language = Language.AllLanguages, pageSize = 100, sort = Sort.ByTitleAsc) - ) + val search = articleSearchService + .matchingQuery( + TestData.testSettings.copy(language = Language.AllLanguages, pageSize = 100, sort = Sort.ByTitleAsc) + ) + .get search.totalCount should equal(11) } test("Search for all languages should return all articles in correct language") { - val Success(search) = - articleSearchService.matchingQuery(testSettings.copy(language = Language.AllLanguages, pageSize = 100)) + val search = + articleSearchService + .matchingQuery(TestData.testSettings.copy(language = Language.AllLanguages, pageSize = 100)) + .get val hits = search.results search.totalCount should equal(11) @@ -478,14 +506,16 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSu } test("Search for all languages should return all languages if copyrighted") { - val Success(search) = articleSearchService.matchingQuery( - testSettings.copy( - language = Language.AllLanguages, - license = Some(Copyrighted.toString), - sort = Sort.ByTitleAsc, - pageSize = 100 + val search = articleSearchService + .matchingQuery( + TestData.testSettings.copy( + language = Language.AllLanguages, + license = Some(Copyrighted.toString), + sort = Sort.ByTitleAsc, + pageSize = 100 + ) ) - ) + .get val hits = search.results search.totalCount should equal(1) @@ -493,12 +523,17 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSu } test("Searching with query for all languages should return language that matched") { - val Success(searchEn) = articleSearchService.matchingQuery( - testSettings.copy(query = Some("Cats"), language = Language.AllLanguages, sort = Sort.ByRelevanceDesc) - ) - val Success(searchNb) = articleSearchService.matchingQuery( - testSettings.copy(query = Some("Katter"), language = Language.AllLanguages, sort = Sort.ByRelevanceDesc) - ) + val searchEn = articleSearchService + .matchingQuery( + TestData.testSettings.copy(query = Some("Cats"), language = Language.AllLanguages, sort = Sort.ByRelevanceDesc) + ) + .get + val searchNb = articleSearchService + .matchingQuery( + TestData.testSettings + .copy(query = Some("Katter"), language = Language.AllLanguages, sort = Sort.ByRelevanceDesc) + ) + .get searchEn.totalCount should equal(1) searchEn.results.head.id should equal(11) @@ -512,9 +547,12 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSu } test("metadescription is searchable") { - val Success(search) = articleSearchService.matchingQuery( - testSettings.copy(query = Some("hurr dirr"), language = Language.AllLanguages, sort = Sort.ByRelevanceDesc) - ) + val search = articleSearchService + .matchingQuery( + TestData.testSettings + .copy(query = Some("hurr dirr"), language = Language.AllLanguages, sort = Sort.ByRelevanceDesc) + ) + .get search.totalCount should equal(1) search.results.head.id should equal(11) @@ -523,10 +561,12 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSu } test("That searching with fallback parameter returns article in language priority even if doesnt match on language") { - val Success(search) = - articleSearchService.matchingQuery( - testSettings.copy(withIdIn = List(9, 10, 11), language = "en", fallback = true) - ) + val search = + articleSearchService + .matchingQuery( + TestData.testSettings.copy(withIdIn = List(9, 10, 11), language = "en", fallback = true) + ) + .get search.totalCount should equal(3) search.results.head.id should equal(9) @@ -538,8 +578,8 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSu } test("That searching for language not in analyzer works as expected") { - val Success(search) = - articleSearchService.matchingQuery(testSettings.copy(language = "biz")) + val search = + articleSearchService.matchingQuery(TestData.testSettings.copy(language = "biz")).get search.totalCount should equal(1) search.results.head.id should equal(11) @@ -547,21 +587,22 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSu } test("That searching for language not in index works as expected") { - val Success(search) = - articleSearchService.matchingQuery(testSettings.copy(language = "mix")) + val search = + articleSearchService.matchingQuery(TestData.testSettings.copy(language = "mix")).get search.totalCount should equal(0) } test("That searching for not supported language does not break") { - val Success(search) = - articleSearchService.matchingQuery(testSettings.copy(language = "asdf")) + val search = + articleSearchService.matchingQuery(TestData.testSettings.copy(language = "asdf")).get search.totalCount should equal(0) } test("That metaImage altText is included in the search") { - val Success(search) = articleSearchService.matchingQuery(testSettings.copy(withIdIn = List(1), fallback = true)) + val search = + articleSearchService.matchingQuery(TestData.testSettings.copy(withIdIn = List(1), fallback = true)).get search.totalCount should be(1) search.results.head.metaImage should be( Some( @@ -574,21 +615,23 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSu val pageSize = 2 val expectedIds = List(1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 13).sliding(pageSize, pageSize).toList - val Success(initialSearch) = - articleSearchService.matchingQuery( - testSettings.copy( - language = Language.AllLanguages, - pageSize = pageSize, - fallback = true, - shouldScroll = true + val initialSearch = + articleSearchService + .matchingQuery( + TestData.testSettings.copy( + language = Language.AllLanguages, + pageSize = pageSize, + fallback = true, + shouldScroll = true + ) ) - ) + .get - val Success(scroll1) = articleSearchService.scroll(initialSearch.scrollId.get, "*") - val Success(scroll2) = articleSearchService.scroll(scroll1.scrollId.get, "*") - val Success(scroll3) = articleSearchService.scroll(scroll2.scrollId.get, "*") - val Success(scroll4) = articleSearchService.scroll(scroll3.scrollId.get, "*") - val Success(scroll5) = articleSearchService.scroll(scroll4.scrollId.get, "*") + val scroll1 = articleSearchService.scroll(initialSearch.scrollId.get, "*").get + val scroll2 = articleSearchService.scroll(scroll1.scrollId.get, "*").get + val scroll3 = articleSearchService.scroll(scroll2.scrollId.get, "*").get + val scroll4 = articleSearchService.scroll(scroll3.scrollId.get, "*").get + val scroll5 = articleSearchService.scroll(scroll4.scrollId.get, "*").get initialSearch.results.map(_.id) should be(expectedIds.head) scroll1.results.map(_.id) should be(expectedIds(1)) @@ -599,16 +642,19 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSu } test("That highlighting works when scrolling") { - val Success(initialSearch) = - articleSearchService.matchingQuery( - testSettings.copy( - query = Some("about"), - pageSize = 1, - fallback = true, - shouldScroll = true + val initialSearch = + articleSearchService + .matchingQuery( + TestData.testSettings.copy( + query = Some("about"), + pageSize = 1, + fallback = true, + shouldScroll = true + ) ) - ) - val Success(scroll) = articleSearchService.scroll(initialSearch.scrollId.get, "*") + .get + + val scroll = articleSearchService.scroll(initialSearch.scrollId.get, "*").get initialSearch.results.size should be(1) initialSearch.results.head.id should be(10) @@ -621,43 +667,55 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSu test("That filtering for grepCodes works as expected") { - val Success(search1) = articleSearchService.matchingQuery(testSettings.copy(grepCodes = Seq("KV123"))) + val search1 = articleSearchService.matchingQuery(TestData.testSettings.copy(grepCodes = Seq("KV123"))).get search1.totalCount should be(2) search1.results.map(_.id) should be(Seq(1, 2)) - val Success(search2) = articleSearchService.matchingQuery(testSettings.copy(grepCodes = Seq("KV123", "KV456"))) + val search2 = + articleSearchService.matchingQuery(TestData.testSettings.copy(grepCodes = Seq("KV123", "KV456"))).get search2.totalCount should be(3) search2.results.map(_.id) should be(Seq(1, 2, 3)) - val Success(search3) = articleSearchService.matchingQuery(testSettings.copy(grepCodes = Seq("KV456"))) + val search3 = articleSearchService.matchingQuery(TestData.testSettings.copy(grepCodes = Seq("KV456"))).get search3.totalCount should be(3) search3.results.map(_.id) should be(Seq(1, 2, 3)) } test("That 'everyone' doesn't see teacher and student articles in search") { - val Success(search1) = articleSearchService.matchingQuery( - testSettings.copy(query = Some("availability"), availability = Seq(Availability.everyone)) - ) + val search1 = articleSearchService + .matchingQuery( + TestData.testSettings.copy(query = Some("availability"), availability = Seq(Availability.everyone)) + ) + .get - val Success(search2) = - articleSearchService.matchingQuery(testSettings.copy(query = Some("availability"), availability = Seq.empty)) + val search2 = + articleSearchService + .matchingQuery( + TestData.testSettings.copy(query = Some("availability"), availability = Seq.empty) + ) + .get search1.results.map(_.id) should be(Seq(13)) search2.results.map(_.id) should be(Seq(13)) } test("That 'everyone' doesn't see teacher articles in search") { - val Success(search1) = articleSearchService.matchingQuery( - testSettings.copy(query = Some("availability"), availability = Seq(Availability.everyone)) - ) + val search1 = articleSearchService + .matchingQuery( + TestData.testSettings.copy(query = Some("availability"), availability = Seq(Availability.everyone)) + ) + .get search1.results.map(_.id) should be(Seq(13)) } test("That 'teachers' sees teacher articles in search") { - val Success(search1) = articleSearchService.matchingQuery( - testSettings.copy(query = Some("availability"), availability = Seq(Availability.teacher, Availability.everyone)) - ) + val search1 = articleSearchService + .matchingQuery( + TestData.testSettings + .copy(query = Some("availability"), availability = Seq(Availability.teacher, Availability.everyone)) + ) + .get search1.results.map(_.id) should be(Seq(12, 13)) } diff --git a/article-api/src/test/scala/no/ndla/articleapi/validation/ContentValidatorTest.scala b/article-api/src/test/scala/no/ndla/articleapi/validation/ContentValidatorTest.scala index d39af83f6a..6537b18d5c 100644 --- a/article-api/src/test/scala/no/ndla/articleapi/validation/ContentValidatorTest.scala +++ b/article-api/src/test/scala/no/ndla/articleapi/validation/ContentValidatorTest.scala @@ -25,16 +25,24 @@ import no.ndla.common.model.domain.article.Copyright import no.ndla.common.model.domain.language.OptLanguageFields import no.ndla.mapping.License.{CC_BY_SA, NA} -import scala.util.Failure +import scala.util.{Failure, Try} class ContentValidatorTest extends UnitSuite with TestEnvironment { - override val contentValidator = new ContentValidator - val validDocument = """

heisann

heia

""" - val validIntroduction = """

heisann heia

hopp

""" - val invalidDocument = """
""" - val validDisclaimer = + override lazy val contentValidator = new ContentValidator + val validDocument = """

heisann

heia

""" + val validIntroduction = """

heisann heia

hopp

""" + val invalidDocument = """
""" + val validDisclaimer = """

hallo!test

""" + extension (t: Try[?]) + def asValidationError: ValidationException = { + t match { + case Failure(exception: ValidationException) => exception + case other => fail(s"Expected a ValidationException, but got: $other") + } + } + test("validateArticle does not throw an exception on a valid document") { val article = TestData.sampleArticleWithByNcSa.copy(content = Seq(ArticleContent(validDocument, "nb"))) contentValidator.validateArticle(article, false).isSuccess should be(true) @@ -87,7 +95,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { disclaimer = OptLanguageFields.withValue("

hei

", "nb") ) - val Failure(error: ValidationException) = contentValidator.validateArticle(article, false) + val error = contentValidator.validateArticle(article, false).asValidationError error should be( ValidationException( "disclaimer.nb", @@ -145,9 +153,8 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { } test("validateArticle should fail if the title exceeds 256 bytes") { - val article = TestData.sampleArticleWithByNcSa.copy(title = Seq(Title("A" * 257, "nb"))) - val Failure(ex: ValidationException) = contentValidator.validateArticle(article, false) - + val article = TestData.sampleArticleWithByNcSa.copy(title = Seq(Title("A" * 257, "nb"))) + val ex = contentValidator.validateArticle(article, false).asValidationError ex.errors.length should be(1) ex.errors.head.message should be("This field exceeds the maximum permitted length of 256 characters") } @@ -325,7 +332,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { test("validation should fail if metaImage altText contains html") { val article = TestData.sampleArticleWithByNcSa.copy(metaImage = Seq(ArticleMetaImage("1234", "Ikke krutte god", "nb"))) - val Failure(res1: ValidationException) = contentValidator.validateArticle(article, false) + val res1 = contentValidator.validateArticle(article, false).failed.get.asInstanceOf[ValidationException] res1.errors should be( Seq(ValidationMessage("metaImage.alt", "The content contains illegal html-characters. No HTML is allowed")) ) @@ -335,34 +342,40 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { } test("validation should fail if not imported and tags are < 3") { - val Failure(res0: ValidationException) = contentValidator.validateArticle( - TestData.sampleArticleWithByNcSa.copy(tags = Seq(Tag(Seq("a", "b"), "nb"))), - false - ) + val res0 = contentValidator + .validateArticle( + TestData.sampleArticleWithByNcSa.copy(tags = Seq(Tag(Seq("a", "b"), "nb"))), + false + ) + .asValidationError res0.errors should be( Seq(ValidationMessage("tags.nb", s"Invalid amount of tags. Articles needs 3 or more tags to be valid.")) ) - val Failure(res1: ValidationException) = - contentValidator.validateArticle( - TestData.sampleArticleWithByNcSa.copy( - tags = Seq(Tag(Seq("a", "b", "c"), "nb"), Tag(Seq("a", "b"), "en")) - ), - false - ) + val res1 = + contentValidator + .validateArticle( + TestData.sampleArticleWithByNcSa.copy( + tags = Seq(Tag(Seq("a", "b", "c"), "nb"), Tag(Seq("a", "b"), "en")) + ), + false + ) + .asValidationError res1.errors should be( Seq(ValidationMessage("tags.en", s"Invalid amount of tags. Articles needs 3 or more tags to be valid.")) ) - val Failure(res2: ValidationException) = - contentValidator.validateArticle( - TestData.sampleArticleWithByNcSa.copy( - tags = Seq(Tag(Seq("a"), "en"), Tag(Seq("a"), "nb"), Tag(Seq("a", "b", "c"), "nn")) - ), - false - ) + val res2 = + contentValidator + .validateArticle( + TestData.sampleArticleWithByNcSa.copy( + tags = Seq(Tag(Seq("a"), "en"), Tag(Seq("a"), "nb"), Tag(Seq("a", "b", "c"), "nn")) + ), + false + ) + .asValidationError res2.errors.sortBy(_.field) should be( Seq( ValidationMessage("tags.en", s"Invalid amount of tags. Articles needs 3 or more tags to be valid."), @@ -396,11 +409,13 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { ) res1.isSuccess should be(true) - val Failure(res2: ValidationException) = - contentValidator.validateArticle( - TestData.sampleArticleWithByNcSa.copy(tags = Seq(Tag(Seq("a", "b", "c"), "nn"))), - isImported = true - ) + val res2 = + contentValidator + .validateArticle( + TestData.sampleArticleWithByNcSa.copy(tags = Seq(Tag(Seq("a", "b", "c"), "nn"))), + isImported = true + ) + .asValidationError res2.errors should be( Seq( ValidationMessage("tags.nn", s"The content contains illegal html-characters. No HTML is allowed") @@ -417,30 +432,34 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { } test("validation should fail if there are no tags for any languages") { - val Failure(res: ValidationException) = - contentValidator.validateArticle(TestData.sampleArticleWithByNcSa.copy(tags = Seq()), false) + val res = + contentValidator.validateArticle(TestData.sampleArticleWithByNcSa.copy(tags = Seq()), false).asValidationError res.errors.length should be(1) res.errors.head.field should equal("tags") res.errors.head.message should equal("The article must have at least one set of tags") } test("validation should fail if metaImageId is an empty string") { - val Failure(res: ValidationException) = - contentValidator.validateArticle( - TestData.sampleArticleWithByNcSa.copy( - metaImage = Seq(ArticleMetaImage("", "alt-text", "nb")) - ), - false - ) + val res = + contentValidator + .validateArticle( + TestData.sampleArticleWithByNcSa.copy( + metaImage = Seq(ArticleMetaImage("", "alt-text", "nb")) + ), + false + ) + .asValidationError res.errors.length should be(1) res.errors.head.field should be("metaImageId") res.errors.head.message should be("Meta image ID must be a number") } test("validation should fail if license is chosen and no copyright holders are provided") { - val copyright = Copyright(CC_BY_SA.toString, None, Seq(), Seq(), Seq(), None, None, false) - val Failure(res: ValidationException) = - contentValidator.validateArticle(TestData.sampleArticleWithByNcSa.copy(copyright = copyright), false) + val copyright = Copyright(CC_BY_SA.toString, None, Seq(), Seq(), Seq(), None, None, false) + val res = + contentValidator + .validateArticle(TestData.sampleArticleWithByNcSa.copy(copyright = copyright), false) + .asValidationError res.errors.length should be(1) res.errors.head.field should be("license.license") res.errors.head.message should be("At least one copyright holder is required when license is CC-BY-SA-4.0") @@ -460,10 +479,12 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { } test("softvalidation is more lenient than strictvalidation") { - val Failure(strictRes: ValidationException) = contentValidator.validateArticle( - TestData.sampleArticleWithByNcSa.copy(metaImage = Seq(ArticleMetaImage("", "alt-text", "nb"))), - false - ) + val strictRes = contentValidator + .validateArticle( + TestData.sampleArticleWithByNcSa.copy(metaImage = Seq(ArticleMetaImage("", "alt-text", "nb"))), + false + ) + .asValidationError val softRes = contentValidator.softValidateArticle( TestData.sampleArticleWithByNcSa.copy(metaImage = Seq(ArticleMetaImage("", "alt-text", "nb"))), diff --git a/audio-api/src/main/scala/no/ndla/audioapi/AudioApiProperties.scala b/audio-api/src/main/scala/no/ndla/audioapi/AudioApiProperties.scala index 4308c097e1..bb75a540aa 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/AudioApiProperties.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/AudioApiProperties.scala @@ -16,7 +16,7 @@ import no.ndla.network.{AuthUser, Domains} import scala.util.Properties.* trait Props extends HasBaseProps with HasDatabaseProps { - val props: AudioApiProperties + lazy val props: AudioApiProperties } class AudioApiProperties extends BaseProps with DatabaseProps with StrictLogging { diff --git a/audio-api/src/main/scala/no/ndla/audioapi/ComponentRegistry.scala b/audio-api/src/main/scala/no/ndla/audioapi/ComponentRegistry.scala index 47ee5a8d10..c557d83756 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/ComponentRegistry.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/ComponentRegistry.scala @@ -61,46 +61,46 @@ class ComponentRegistry(properties: AudioApiProperties) with TranscriptionService with NdlaAWSTranscribeClient with NdlaBrightcoveClient { - override val props: AudioApiProperties = properties - override val migrator: DBMigrator = DBMigrator( + override lazy val props: AudioApiProperties = properties + override lazy val migrator: DBMigrator = DBMigrator( new V5__AddAgreementToAudio, new V6__TranslateUntranslatedAuthors ) override lazy val dataSource: HikariDataSource = DataSource.getHikariDataSource - lazy val s3Client = new NdlaS3Client(props.StorageName, props.StorageRegion) - lazy val s3TranscribeClient = new NdlaS3Client(props.TranscribeStorageName, props.TranscribeStorageRegion) - lazy val brightcoveClient = new NdlaBrightcoveClient() - lazy val transcribeClient = new NdlaAWSTranscribeClient(props.TranscribeStorageRegion) + override lazy val s3Client = new NdlaS3Client(props.StorageName, props.StorageRegion) + override lazy val s3TranscribeClient = new NdlaS3Client(props.TranscribeStorageName, props.TranscribeStorageRegion) + override lazy val brightcoveClient = new NdlaBrightcoveClient() + override lazy val transcribeClient = new NdlaAWSTranscribeClient(props.TranscribeStorageRegion) - lazy val audioRepository = new AudioRepository - lazy val seriesRepository = new SeriesRepository + override lazy val audioRepository = new AudioRepository + override lazy val seriesRepository = new SeriesRepository - lazy val ndlaClient = new NdlaClient - lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient + override lazy val ndlaClient = new NdlaClient + override lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient - lazy val readService = new ReadService - lazy val writeService = new WriteService - lazy val validationService = new ValidationService - lazy val converterService = new ConverterService - lazy val transcriptionService = new TranscriptionService + override lazy val readService = new ReadService + override lazy val writeService = new WriteService + override lazy val validationService = new ValidationService + override lazy val converterService = new ConverterService + override lazy val transcriptionService = new TranscriptionService - lazy val internController = new InternController - lazy val audioApiController = new AudioController - lazy val seriesController = new SeriesController - lazy val healthController = new HealthController - lazy val transcriptionController = new TranscriptionController + override lazy val internController = new InternController + override lazy val audioApiController = new AudioController + override lazy val seriesController = new SeriesController + override lazy val healthController = new HealthController + override lazy val transcriptionController = new TranscriptionController - var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer) - lazy val searchConverterService = new SearchConverterService - lazy val audioIndexService = new AudioIndexService - lazy val audioSearchService = new AudioSearchService - lazy val seriesIndexService = new SeriesIndexService - lazy val seriesSearchService = new SeriesSearchService - lazy val tagIndexService = new TagIndexService - lazy val tagSearchService = new TagSearchService + var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer) + override lazy val searchConverterService = new SearchConverterService + override lazy val audioIndexService = new AudioIndexService + override lazy val audioSearchService = new AudioSearchService + override lazy val seriesIndexService = new SeriesIndexService + override lazy val seriesSearchService = new SeriesSearchService + override lazy val tagIndexService = new TagIndexService + override lazy val tagSearchService = new TagSearchService - lazy val clock = new SystemClock + override lazy val clock = new SystemClock val swagger = new SwaggerController( List( diff --git a/audio-api/src/main/scala/no/ndla/audioapi/controller/AudioController.scala b/audio-api/src/main/scala/no/ndla/audioapi/controller/AudioController.scala index 1c8cf7b52d..9283c1ff81 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/controller/AudioController.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/controller/AudioController.scala @@ -9,7 +9,6 @@ package no.ndla.audioapi.controller import cats.implicits.* -import io.circe.generic.auto.* import no.ndla.audioapi.controller.multipart.{MetaDataAndFileForm, MetaDataAndOptFileForm} import no.ndla.audioapi.Props import no.ndla.audioapi.model.Sort @@ -24,7 +23,6 @@ import no.ndla.common.implicits.* import no.ndla.common.model.api.CommaSeparatedList.* import no.ndla.common.model.api.LanguageCode import no.ndla.common.model.domain.UploadedFile -import no.ndla.network.tapir.NoNullJsonPrinter.* import no.ndla.network.tapir.{NonEmptyString, TapirController} import no.ndla.network.tapir.TapirUtil.errorOutputsFor import no.ndla.network.tapir.auth.Permission.AUDIO_API_WRITE @@ -40,10 +38,9 @@ import scala.util.{Failure, Success, Try} trait AudioController { this: AudioRepository & ReadService & WriteService & AudioSearchService & SearchConverterService & ConverterService & Props & ErrorHandling & TapirController => - val audioApiController: AudioController + lazy val audioApiController: AudioController class AudioController extends TapirController { - import props.* val maxAudioFileSizeBytes: Int = props.MaxAudioFileSizeBytes override val serviceName: String = "audio" override val prefix: EndpointInput[Unit] = "audio-api" / "v1" / serviceName @@ -58,7 +55,7 @@ trait AudioController { private val license = query[Option[String]]("license").description("Return only audio with provided license.") private val pageNo = query[Option[Int]]("page").description("The page number of the search hits to display.") private val pageSize = query[Option[Int]]("page-size").description( - s"The number of search hits to display for each page. Defaults to $DefaultPageSize and max is $MaxPageSize." + s"The number of search hits to display for each page. Defaults to ${props.DefaultPageSize} and max is ${props.MaxPageSize}." ) private val audioIds = listQuery[Long]("ids").description( "Return only audios that have one of the provided ids. To provide multiple ids, separate by comma (,)." @@ -69,11 +66,11 @@ trait AudioController { Default is by -relevance (desc) when query is set, and title (asc) when query is empty.""".stripMargin ) private val scrollId = query[Option[String]]("search-context").description( - s"""A unique string obtained from a search you want to keep scrolling in. To obtain one from a search, provide one of the following values: ${InitialScrollContextKeywords + s"""A unique string obtained from a search you want to keep scrolling in. To obtain one from a search, provide one of the following values: ${props.InitialScrollContextKeywords .mkString("[", ",", "]")}. |When scrolling, the parameters from the initial search is used, except in the case of '${this.language.name}'. - |This value may change between scrolls. Always use the one in the latest scroll result (The context, if unused, dies after $ElasticSearchScrollKeepAlive). - |If you are not paginating past $ElasticSearchIndexMaxResultWindow hits, you can ignore this and use '${this.pageNo.name}' and '${this.pageSize.name}' instead. + |This value may change between scrolls. Always use the one in the latest scroll result (The context, if unused, dies after ${props.ElasticSearchScrollKeepAlive}). + |If you are not paginating past ${props.ElasticSearchIndexMaxResultWindow} hits, you can ignore this and use '${this.pageNo.name}' and '${this.pageSize.name}' instead. |""".stripMargin ) private val audioType = query[Option[String]]("audio-type").description( @@ -92,6 +89,7 @@ trait AudioController { private val pathLanguage = path[String]("language").description("The ISO 639-1 language code describing language.") import ErrorHelpers.* + import sttp.tapir.json.circe._ def getSearch: ServerEndpoint[Any, Eff] = endpoint.get .summary("Find audio files") @@ -112,7 +110,7 @@ trait AudioController { case (query, language, license, sort, pageNo, pageSize, scrollId, audioType, seriesFilter, fallback) => val lang = language.getOrElse(LanguageCode(Language.AllLanguages)) scrollSearchOr(scrollId, lang.code) { - val shouldScroll = scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = scrollId.exists(props.InitialScrollContextKeywords.contains) search( query.underlying, language.map(_.code), @@ -138,7 +136,7 @@ trait AudioController { .serverLogicPure { searchParams => val lang = searchParams.language.getOrElse(LanguageCode(Language.AllLanguages)) scrollSearchOr(searchParams.scrollId, lang.code) { - val shouldScroll = searchParams.scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = searchParams.scrollId.exists(props.InitialScrollContextKeywords.contains) search( searchParams.query, searchParams.language.map(_.code), @@ -222,7 +220,7 @@ trait AudioController { .serverLogicPure { user => formData => doWithStream(formData.file) { uploadedFile => writeService.storeNewAudio( - formData.metadata.body, + formData.metadata, uploadedFile, user ) @@ -243,10 +241,10 @@ trait AudioController { formData.file match { case Some(f) => doWithStream(f) { stream => - writeService.updateAudio(id, formData.metadata.body, Some(stream), user) + writeService.updateAudio(id, formData.metadata, Some(stream), user) } case None => - writeService.updateAudio(id, formData.metadata.body, None, user) + writeService.updateAudio(id, formData.metadata, None, user) } } } @@ -262,8 +260,8 @@ trait AudioController { .out(jsonBody[TagsSearchResultDTO]) .errorOut(errorOutputsFor(400, 404)) .serverLogicPure { case (query, ps, pn, lang) => - val pageSize = ps.getOrElse(DefaultPageSize) match { - case tooSmall if tooSmall < 1 => DefaultPageSize + val pageSize = ps.getOrElse(props.DefaultPageSize) match { + case tooSmall if tooSmall < 1 => props.DefaultPageSize case x => x } val pageNo = pn.getOrElse(1) match { @@ -313,7 +311,7 @@ trait AudioController { orFunction: => Try[SummaryWithHeader] ): Try[SummaryWithHeader] = scrollId match { - case Some(scroll) if !InitialScrollContextKeywords.contains(scroll) => + case Some(scroll) if !props.InitialScrollContextKeywords.contains(scroll) => audioSearchService.scroll(scroll, language) match { case Success(scrollResult) => val body = searchConverterService.asApiAudioSummarySearchResult(scrollResult) diff --git a/audio-api/src/main/scala/no/ndla/audioapi/controller/HealthController.scala b/audio-api/src/main/scala/no/ndla/audioapi/controller/HealthController.scala index 33b257dd0b..9960f75a64 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/controller/HealthController.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/controller/HealthController.scala @@ -15,7 +15,7 @@ import no.ndla.network.tapir.TapirHealthController trait HealthController { this: NdlaS3Client & AudioRepository & Props & TapirHealthController => - val healthController: HealthController + lazy val healthController: HealthController class HealthController extends TapirHealthController { diff --git a/audio-api/src/main/scala/no/ndla/audioapi/controller/InternController.scala b/audio-api/src/main/scala/no/ndla/audioapi/controller/InternController.scala index fa762a9342..2177cdd024 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/controller/InternController.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/controller/InternController.scala @@ -31,7 +31,7 @@ import scala.util.{Failure, Success} trait InternController { this: AudioIndexService & ConverterService & AudioRepository & AudioIndexService & SeriesIndexService & TagIndexService & ReadService & Props & ErrorHandling & TapirController => - val internController: InternController + lazy val internController: InternController class InternController extends TapirController { override val prefix: EndpointInput[Unit] = "intern" diff --git a/audio-api/src/main/scala/no/ndla/audioapi/controller/SeriesController.scala b/audio-api/src/main/scala/no/ndla/audioapi/controller/SeriesController.scala index c9151ecbaf..c47a6b96f6 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/controller/SeriesController.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/controller/SeriesController.scala @@ -34,17 +34,15 @@ import scala.util.{Failure, Success, Try} trait SeriesController { this: ReadService & WriteService & SeriesSearchService & SearchConverterService & ConverterService & Props & ErrorHandling & TapirController => - val seriesController: SeriesController + lazy val seriesController: SeriesController class SeriesController extends TapirController { - import props.* - private val queryString = query[Option[String]]("query") .description("Return only results with titles or tags matching the specified query.") private val language = query[Option[LanguageCode]]("language").description("The ISO 639-1 language code describing language.") private val pageNo = query[Option[Int]]("page").description("The page number of the search hits to display.") private val pageSize = query[Option[Int]]("page-size").description( - s"The number of search hits to display for each page. Defaults to $DefaultPageSize and max is $MaxPageSize." + s"The number of search hits to display for each page. Defaults to ${props.DefaultPageSize} and max is ${props.MaxPageSize}." ) private val sort = query[Option[String]]("sort").description( s"""The sorting used on results. @@ -52,11 +50,11 @@ trait SeriesController { Default is by -relevance (desc) when query is set, and title (asc) when query is empty.""".stripMargin ) private val scrollId = query[Option[String]]("search-context").description( - s"""A unique string obtained from a search you want to keep scrolling in. To obtain one from a search, provide one of the following values: ${InitialScrollContextKeywords + s"""A unique string obtained from a search you want to keep scrolling in. To obtain one from a search, provide one of the following values: ${props.InitialScrollContextKeywords .mkString("[", ",", "]")}. |When scrolling, the parameters from the initial search is used, except in the case of '${this.language.name}'. - |This value may change between scrolls. Always use the one in the latest scroll result (The context, if unused, dies after $ElasticSearchScrollKeepAlive). - |If you are not paginating past $ElasticSearchIndexMaxResultWindow hits, you can ignore this and use '${this.pageNo.name}' and '${this.pageSize.name}' instead. + |This value may change between scrolls. Always use the one in the latest scroll result (The context, if unused, dies after ${props.ElasticSearchScrollKeepAlive}). + |If you are not paginating past ${props.ElasticSearchIndexMaxResultWindow} hits, you can ignore this and use '${this.pageNo.name}' and '${this.pageSize.name}' instead. |""".stripMargin ) private val fallback = @@ -82,9 +80,9 @@ trait SeriesController { .serverLogicPure { case (query, language, sort, page, pageSize, scrollId, fallback) => val lang = language.getOrElse(LanguageCode(Language.AllLanguages)).code scrollSearchOr(scrollId, lang) { - val shouldScroll = scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = scrollId.exists(props.InitialScrollContextKeywords.contains) search(query, lang, Sort.valueOf(sort), pageSize, page, shouldScroll, fallback.getOrElse(false)) - } + }.handleErrorsOrOk } def postSeriesSearch: ServerEndpoint[Any, Eff] = endpoint.post @@ -101,11 +99,11 @@ trait SeriesController { val sort = searchParams.sort val pageSize = searchParams.pageSize val page = searchParams.page - val shouldScroll = searchParams.scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = searchParams.scrollId.exists(props.InitialScrollContextKeywords.contains) val fallback = searchParams.fallback.getOrElse(false) search(query, language.code, sort, pageSize, page, shouldScroll, fallback) - } + }.handleErrorsOrOk } def getSingleSeries: ServerEndpoint[Any, Eff] = endpoint.get @@ -237,7 +235,7 @@ trait SeriesController { orFunction: => Try[SummaryWithHeader] ): Try[SummaryWithHeader] = scrollId match { - case Some(scroll) if !InitialScrollContextKeywords.contains(scroll) => + case Some(scroll) if !props.InitialScrollContextKeywords.contains(scroll) => seriesSearchService.scroll(scroll, language).map { scrollResult => SummaryWithHeader( body = searchConverterService.asApiSeriesSummarySearchResult(scrollResult), diff --git a/audio-api/src/main/scala/no/ndla/audioapi/controller/TranscriptionController.scala b/audio-api/src/main/scala/no/ndla/audioapi/controller/TranscriptionController.scala index 3f5c6648c6..9bbfce41b2 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/controller/TranscriptionController.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/controller/TranscriptionController.scala @@ -23,7 +23,7 @@ import sttp.tapir.generic.auto.schemaForCaseClass import scala.util.{Failure, Success} trait TranscriptionController { this: Props & TapirController & ReadService & TranscriptionService => - val transcriptionController: TranscriptionController + lazy val transcriptionController: TranscriptionController class TranscriptionController() extends TapirController { override val serviceName: String = "transcription" diff --git a/audio-api/src/main/scala/no/ndla/audioapi/controller/multipart/MultipartInput.scala b/audio-api/src/main/scala/no/ndla/audioapi/controller/multipart/MultipartInput.scala index 73057e2203..08499e4770 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/controller/multipart/MultipartInput.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/controller/multipart/MultipartInput.scala @@ -10,14 +10,24 @@ package no.ndla.audioapi.controller.multipart import no.ndla.audioapi.model.api.{NewAudioMetaInformationDTO, UpdatedAudioMetaInformationDTO} import sttp.model.Part +import sttp.tapir.generic.auto.* +import sttp.tapir.Codec.JsonCodec +import sttp.tapir.json.circe.* import java.io.File case class MetaDataAndFileForm( - metadata: Part[NewAudioMetaInformationDTO], + metadata: NewAudioMetaInformationDTO, file: Part[File] ) +object MetaDataAndFileForm { + implicit val codec: JsonCodec[NewAudioMetaInformationDTO] = circeCodec[NewAudioMetaInformationDTO] +} + case class MetaDataAndOptFileForm( - metadata: Part[UpdatedAudioMetaInformationDTO], + metadata: UpdatedAudioMetaInformationDTO, file: Option[Part[File]] ) +object MetaDataAndOptFileForm { + implicit val codec: JsonCodec[UpdatedAudioMetaInformationDTO] = circeCodec[UpdatedAudioMetaInformationDTO] +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/AudioDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/AudioDTO.scala index 5db8127160..e56e0bd5e0 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/AudioDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/AudioDTO.scala @@ -9,6 +9,8 @@ package no.ndla.audioapi.model.api import sttp.tapir.Schema.annotations.description +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} +import io.circe.{Decoder, Encoder} @description("Url and size information about the audio") case class AudioDTO( @@ -17,3 +19,8 @@ case class AudioDTO( @description("The size of the audio file") fileSize: Long, @description("The current language for this audio") language: String ) + +object AudioDTO { + implicit val encoder: Encoder[AudioDTO] = deriveEncoder + implicit val decoder: Decoder[AudioDTO] = deriveDecoder +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/AudioMetaInformationDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/AudioMetaInformationDTO.scala index 5d1b72d007..3795c66274 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/AudioMetaInformationDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/AudioMetaInformationDTO.scala @@ -10,7 +10,10 @@ package no.ndla.audioapi.model.api import no.ndla.common.model.NDLADate import no.ndla.common.model.api.CopyrightDTO +import sttp.tapir.Schema import sttp.tapir.Schema.annotations.description +import io.circe.{Decoder, Encoder} +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} @description("Meta information about the audio object") case class AudioMetaInformationDTO( @@ -41,3 +44,11 @@ case class AudioMetaInformationDTO( @description("The time of last update for the audio-file") updated: NDLADate ) + +object AudioMetaInformationDTO { + implicit val encoder: Encoder[AudioMetaInformationDTO] = deriveEncoder + implicit val decoder: Decoder[AudioMetaInformationDTO] = deriveDecoder + + import sttp.tapir.generic.auto.* + implicit def schema: Schema[AudioMetaInformationDTO] = Schema.derived +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/AudioSummaryDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/AudioSummaryDTO.scala index 67e66b1e56..ccfb35c7e6 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/AudioSummaryDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/AudioSummaryDTO.scala @@ -8,8 +8,11 @@ package no.ndla.audioapi.model.api +import sttp.tapir.Schema import no.ndla.common.model.NDLADate import sttp.tapir.Schema.annotations.description +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} +import io.circe.{Decoder, Encoder} @description("Short summary of information about the audio") case class AudioSummaryDTO( @@ -34,3 +37,11 @@ case class AudioSummaryDTO( @description("The time and date of last update") lastUpdated: NDLADate ) + +object AudioSummaryDTO { + implicit val encoder: Encoder[AudioSummaryDTO] = deriveEncoder + implicit val decoder: Decoder[AudioSummaryDTO] = deriveDecoder + + import sttp.tapir.generic.auto.* + implicit def schema: Schema[AudioSummaryDTO] = Schema.derived +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/CoverPhotoDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/CoverPhotoDTO.scala index 397e1ef8e5..d8de30f6d8 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/CoverPhotoDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/CoverPhotoDTO.scala @@ -16,3 +16,8 @@ case class CoverPhotoDTO( @description("Url to the coverPhoto") url: String, @description("Alttext for the coverPhoto") altText: String ) + +object CoverPhotoDTO { + implicit val encoder: io.circe.Encoder[CoverPhotoDTO] = io.circe.generic.semiauto.deriveEncoder + implicit val decoder: io.circe.Decoder[CoverPhotoDTO] = io.circe.generic.semiauto.deriveDecoder +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/DescriptionDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/DescriptionDTO.scala index 34ddb44f35..3c859f3985 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/DescriptionDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/DescriptionDTO.scala @@ -14,3 +14,8 @@ case class DescriptionDTO( @description("The description of the element") description: String, @description("ISO 639-1 code that represents the language used in the description") language: String ) + +object DescriptionDTO { + implicit val encoder: io.circe.Encoder[DescriptionDTO] = io.circe.generic.semiauto.deriveEncoder + implicit val decoder: io.circe.Decoder[DescriptionDTO] = io.circe.generic.semiauto.deriveDecoder +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/ManuscriptDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/ManuscriptDTO.scala index 36f7bcaae9..74847518d8 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/ManuscriptDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/ManuscriptDTO.scala @@ -9,8 +9,16 @@ package no.ndla.audioapi.model.api import sttp.tapir.Schema.annotations.description +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} +import io.circe.{Decoder, Encoder} case class ManuscriptDTO( @description("The manuscript of the audio file") manuscript: String, @description("ISO 639-1 code that represents the language used in the manuscript") language: String ) + +object ManuscriptDTO { + implicit val encoder: Encoder[ManuscriptDTO] = deriveEncoder + implicit val decoder: Decoder[ManuscriptDTO] = deriveDecoder + +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/NewAudioMetaInformationDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/NewAudioMetaInformationDTO.scala index aa36cf456d..8b345941e4 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/NewAudioMetaInformationDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/NewAudioMetaInformationDTO.scala @@ -8,19 +8,32 @@ package no.ndla.audioapi.model.api +import io.circe.{Decoder, Encoder} +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import no.ndla.common.model.api.CopyrightDTO import sttp.tapir.Schema.annotations.description -// format: off @description("Meta information about the audio object") case class NewAudioMetaInformationDTO( - @description("The title of the audio file") title: String, - @description("ISO 639-1 code that represents the language used in this resource") language: String, - @description("Copyright information for the audio files") copyright: CopyrightDTO, - @description("Tags for this audio file") tags: Seq[String], - @description("Type of audio. 'standard', or 'podcast', defaults to 'standard'") audioType: Option[String], - @description("Meta information about podcast, only applicable if audioType is 'podcast'.") podcastMeta: Option[NewPodcastMetaDTO], - @description("Id of series if the audio is a podcast and a part of a series.") seriesId: Option[Long], - @description("Manuscript for the audio") manuscript: Option[String] + @description("The title of the audio file") + title: String, + @description("ISO 639-1 code that represents the language used in this resource") + language: String, + @description("Copyright information for the audio files") + copyright: CopyrightDTO, + @description("Tags for this audio file") + tags: Seq[String], + @description("Type of audio. 'standard', or 'podcast', defaults to 'standard'") + audioType: Option[String], + @description("Meta information about podcast, only applicable if audioType is 'podcast'.") + podcastMeta: Option[NewPodcastMetaDTO], + @description("Id of series if the audio is a podcast and a part of a series.") + seriesId: Option[Long], + @description("Manuscript for the audio") + manuscript: Option[String] ) -// format: on + +object NewAudioMetaInformationDTO { + implicit val encoder: Encoder[NewAudioMetaInformationDTO] = deriveEncoder + implicit val decoder: Decoder[NewAudioMetaInformationDTO] = deriveDecoder +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/NewPodcastMetaDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/NewPodcastMetaDTO.scala index 79f7da6db5..61e540d8c2 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/NewPodcastMetaDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/NewPodcastMetaDTO.scala @@ -9,12 +9,16 @@ package no.ndla.audioapi.model.api import sttp.tapir.Schema.annotations.description +import io.circe.{Decoder, Encoder} +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} -// format: off @description("Meta information about podcast audio") case class NewPodcastMetaDTO( - @description("Introduction for the podcast") introduction: String, - @description("Cover photo for the podcast") coverPhotoId: String, - @description("Cover photo alttext for the podcast") coverPhotoAltText: String, + @description("Introduction for the podcast") introduction: String, + @description("Cover photo for the podcast") coverPhotoId: String, + @description("Cover photo alttext for the podcast") coverPhotoAltText: String ) -// format: on +object NewPodcastMetaDTO { + implicit val encoder: Encoder[NewPodcastMetaDTO] = deriveEncoder + implicit val decoder: Decoder[NewPodcastMetaDTO] = deriveDecoder +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/PodcastMetaDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/PodcastMetaDTO.scala index 353784dcfa..a4957f8d4d 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/PodcastMetaDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/PodcastMetaDTO.scala @@ -9,12 +9,17 @@ package no.ndla.audioapi.model.api import sttp.tapir.Schema.annotations.description +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} +import io.circe.{Decoder, Encoder} -// format: off @description("Meta information about podcast audio") case class PodcastMetaDTO( - @description("Introduction for the podcast") introduction: String, - @description("Cover photo for the podcast") coverPhoto: CoverPhotoDTO, - @description("ISO 639-1 code that represents the language used in the title") language: String + @description("Introduction for the podcast") introduction: String, + @description("Cover photo for the podcast") coverPhoto: CoverPhotoDTO, + @description("ISO 639-1 code that represents the language used in the title") language: String ) -// format: on + +object PodcastMetaDTO { + implicit def encoder: Encoder[PodcastMetaDTO] = deriveEncoder + implicit def decoder: Decoder[PodcastMetaDTO] = deriveDecoder +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/SearchParamsDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/SearchParamsDTO.scala index 6bf6c80274..8541f6e0c5 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/SearchParamsDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/SearchParamsDTO.scala @@ -11,6 +11,8 @@ package no.ndla.audioapi.model.api import no.ndla.audioapi.model.Sort import no.ndla.common.model.api.LanguageCode import sttp.tapir.Schema.annotations.description +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} +import io.circe.{Decoder, Encoder} @description("The search parameters") case class SearchParamsDTO( @@ -37,3 +39,8 @@ case class SearchParamsDTO( @description("Return all matched audios whether they exist on selected language or not.") fallback: Option[Boolean] ) + +object SearchParamsDTO { + implicit val encoder: Encoder[SearchParamsDTO] = deriveEncoder + implicit val decoder: Decoder[SearchParamsDTO] = deriveDecoder +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/SearchResult.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/SearchResult.scala index 96172c923a..50c75439b6 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/SearchResult.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/SearchResult.scala @@ -9,6 +9,8 @@ package no.ndla.audioapi.model.api import sttp.tapir.Schema.annotations.description +import io.circe.{Decoder, Encoder} +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} @description("Information about audio summary search-results") case class AudioSummarySearchResultDTO( @@ -19,6 +21,11 @@ case class AudioSummarySearchResultDTO( @description("The search results") results: Seq[AudioSummaryDTO] ) +object AudioSummarySearchResultDTO { + implicit val encoder: Encoder[AudioSummarySearchResultDTO] = deriveEncoder + implicit val decoder: Decoder[AudioSummarySearchResultDTO] = deriveDecoder +} + @description("Information about series summary search-results") case class SeriesSummarySearchResultDTO( @description("The total number of articles matching this query") totalCount: Long, @@ -27,6 +34,10 @@ case class SeriesSummarySearchResultDTO( @description("The chosen search language") language: String, @description("The search results") results: Seq[SeriesSummaryDTO] ) +object SeriesSummarySearchResultDTO { + implicit val encoder: Encoder[SeriesSummarySearchResultDTO] = deriveEncoder + implicit val decoder: Decoder[SeriesSummarySearchResultDTO] = deriveDecoder +} @description("Information about tags-search-results") case class TagsSearchResultDTO( @@ -36,3 +47,7 @@ case class TagsSearchResultDTO( @description("The chosen search language") language: String, @description("The search results") results: Seq[String] ) +object TagsSearchResultDTO { + implicit val encoder: Encoder[TagsSearchResultDTO] = deriveEncoder + implicit val decoder: Decoder[TagsSearchResultDTO] = deriveDecoder +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/SeriesDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/SeriesDTO.scala index bdba0330bf..7f546586a0 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/SeriesDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/SeriesDTO.scala @@ -8,6 +8,8 @@ package no.ndla.audioapi.model.api +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} +import io.circe.{Decoder, Encoder} import sttp.tapir.Schema.annotations.description @description("Meta information about the series") @@ -21,3 +23,8 @@ case class SeriesDTO( @description("A list of available languages for this series") supportedLanguages: Seq[String], @description("Specifies if this series generates rss-feed") hasRSS: Boolean ) + +object SeriesDTO { + implicit def encoder: Encoder[SeriesDTO] = deriveEncoder + implicit def decoder: Decoder[SeriesDTO] = deriveDecoder +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/SeriesSummaryDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/SeriesSummaryDTO.scala index c2daa3a752..2df87ce6af 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/SeriesSummaryDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/SeriesSummaryDTO.scala @@ -9,6 +9,9 @@ package no.ndla.audioapi.model.api import sttp.tapir.Schema.annotations.description +import sttp.tapir.Schema +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} +import io.circe.{Decoder, Encoder} @description("Short summary of information about the series") case class SeriesSummaryDTO( @@ -19,3 +22,10 @@ case class SeriesSummaryDTO( @description("A list of episode summaries") episodes: Option[Seq[AudioSummaryDTO]], @description("Cover photo for the series") coverPhoto: CoverPhotoDTO ) +object SeriesSummaryDTO { + implicit val encoder: Encoder[SeriesSummaryDTO] = deriveEncoder + implicit val decoder: Decoder[SeriesSummaryDTO] = deriveDecoder + + import sttp.tapir.generic.auto.* + implicit def schema: Schema[SeriesSummaryDTO] = Schema.derived +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/TagDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/TagDTO.scala index ef26500a37..c2160d0035 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/TagDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/TagDTO.scala @@ -9,9 +9,16 @@ package no.ndla.audioapi.model.api import sttp.tapir.Schema.annotations.description +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} +import io.circe.{Decoder, Encoder} @description("Description of the tags of the audio") case class TagDTO( @description("The searchable tag.") tags: Seq[String], @description("ISO 639-1 code that represents the language used in tag") language: String ) + +object TagDTO { + implicit val encoder: Encoder[TagDTO] = deriveEncoder + implicit val decoder: Decoder[TagDTO] = deriveDecoder +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/TitleDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/TitleDTO.scala index 0c6b5510ce..86622013c1 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/TitleDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/TitleDTO.scala @@ -8,9 +8,17 @@ package no.ndla.audioapi.model.api +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} +import io.circe.{Decoder, Encoder} +import sttp.tapir.Schema import sttp.tapir.Schema.annotations.description case class TitleDTO( @description("The title of the audio file") title: String, @description("ISO 639-1 code that represents the language used in the title") language: String ) + +object TitleDTO { + implicit val encoder: Encoder[TitleDTO] = deriveEncoder + implicit val decoder: Decoder[TitleDTO] = deriveDecoder +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/api/UpdatedAudioMetaInformationDTO.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/api/UpdatedAudioMetaInformationDTO.scala index 2ac9d14ea9..3713e3b8d5 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/api/UpdatedAudioMetaInformationDTO.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/api/UpdatedAudioMetaInformationDTO.scala @@ -10,18 +10,32 @@ package no.ndla.audioapi.model.api import no.ndla.common.model.api.CopyrightDTO import sttp.tapir.Schema.annotations.description +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} +import io.circe.{Decoder, Encoder} -// format: off @description("Meta information about the audio object") case class UpdatedAudioMetaInformationDTO( - @description("The revision number of this audio") revision: Int, - @description("The title of the audio file") title: String, - @description("ISO 639-1 code that represents the language used in this resource") language: String, - @description("Copyright information for the audio files") copyright: CopyrightDTO, - @description("Tags for this audio file") tags: Seq[String], - @description("Type of audio. 'standard', or 'podcast', defaults to 'standard'") audioType: Option[String], - @description("Meta information about podcast, only applicable if audioType is 'podcast'.") podcastMeta: Option[NewPodcastMetaDTO], - @description("Id of series if the audio is a podcast and a part of a series.") seriesId: Option[Long], - @description("Manuscript for the audio") manuscript: Option[String], + @description("The revision number of this audio") + revision: Int, + @description("The title of the audio file") + title: String, + @description("ISO 639-1 code that represents the language used in this resource") + language: String, + @description("Copyright information for the audio files") + copyright: CopyrightDTO, + @description("Tags for this audio file") + tags: Seq[String], + @description("Type of audio. 'standard', or 'podcast', defaults to 'standard'") + audioType: Option[String], + @description("Meta information about podcast, only applicable if audioType is 'podcast'.") + podcastMeta: Option[NewPodcastMetaDTO], + @description("Id of series if the audio is a podcast and a part of a series.") + seriesId: Option[Long], + @description("Manuscript for the audio") + manuscript: Option[String] ) -// format: on + +object UpdatedAudioMetaInformationDTO { + implicit val encoder: Encoder[UpdatedAudioMetaInformationDTO] = deriveEncoder + implicit val decoder: Decoder[UpdatedAudioMetaInformationDTO] = deriveDecoder +} diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/domain/AudioMetaInformation.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/domain/AudioMetaInformation.scala index 32a4744a84..c36025457d 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/domain/AudioMetaInformation.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/domain/AudioMetaInformation.scala @@ -8,6 +8,7 @@ package no.ndla.audioapi.model.domain +import sttp.tapir.Schema import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} import no.ndla.common.CirceUtil @@ -73,6 +74,8 @@ object AudioMetaInformation extends SQLSyntaxSupport[AudioMetaInformation] { implicit val encoder: Encoder[AudioMetaInformation] = deriveEncoder implicit val decoder: Decoder[AudioMetaInformation] = deriveDecoder + import sttp.tapir.generic.auto.* + implicit def schema: Schema[AudioMetaInformation] = Schema.derived def fromResultSet(au: SyntaxProvider[AudioMetaInformation])(rs: WrappedResultSet): AudioMetaInformation = fromResultSet(au.resultName)(rs) diff --git a/audio-api/src/main/scala/no/ndla/audioapi/model/domain/Series.scala b/audio-api/src/main/scala/no/ndla/audioapi/model/domain/Series.scala index 854da8b148..2f7c15260d 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/model/domain/Series.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/model/domain/Series.scala @@ -19,14 +19,14 @@ import scalikejdbc.* import scala.util.Try /** Base series without database generated fields */ -class SeriesWithoutId( - val title: Seq[Title], - val coverPhoto: CoverPhoto, - val episodes: Option[Seq[AudioMetaInformation]], - val updated: NDLADate, - val created: NDLADate, - val description: Seq[Description], - val hasRSS: Boolean +case class SeriesWithoutId( + title: Seq[Title], + coverPhoto: CoverPhoto, + episodes: Option[Seq[AudioMetaInformation]], + updated: NDLADate, + created: NDLADate, + description: Seq[Description], + hasRSS: Boolean ) object SeriesWithoutId { implicit val encoder: Encoder[SeriesWithoutId] = deriveEncoder @@ -39,15 +39,24 @@ object SeriesWithoutId { case class Series( id: Long, revision: Int, - override val episodes: Option[Seq[AudioMetaInformation]], - override val title: Seq[Title], - override val coverPhoto: CoverPhoto, - override val updated: NDLADate, - override val created: NDLADate, - override val description: Seq[Description], - override val hasRSS: Boolean -) extends SeriesWithoutId(title, coverPhoto, episodes, updated, created, description, hasRSS) { + episodes: Option[Seq[AudioMetaInformation]], + title: Seq[Title], + coverPhoto: CoverPhoto, + updated: NDLADate, + created: NDLADate, + description: Seq[Description], + hasRSS: Boolean +) { lazy val supportedLanguages: Seq[String] = getSupportedLanguages(title, description) + def withoutId: SeriesWithoutId = SeriesWithoutId( + title = title, + coverPhoto = coverPhoto, + episodes = episodes, + updated = updated, + created = created, + description = description, + hasRSS = hasRSS + ) } object Series extends SQLSyntaxSupport[Series] { diff --git a/audio-api/src/main/scala/no/ndla/audioapi/repository/AudioRepository.scala b/audio-api/src/main/scala/no/ndla/audioapi/repository/AudioRepository.scala index 5a68ee7b50..c0f633fc8c 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/repository/AudioRepository.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/repository/AudioRepository.scala @@ -21,7 +21,7 @@ import scala.util.{Failure, Success, Try} trait AudioRepository { this: DataSource & SeriesRepository & Props & ErrorHandling => - val audioRepository: AudioRepository + lazy val audioRepository: AudioRepository class AudioRepository extends StrictLogging with Repository[AudioMetaInformation] { def audioCount(implicit session: DBSession = ReadOnlyAutoSession): Long = diff --git a/audio-api/src/main/scala/no/ndla/audioapi/repository/SeriesRepository.scala b/audio-api/src/main/scala/no/ndla/audioapi/repository/SeriesRepository.scala index 1b0864b5f5..1992064157 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/repository/SeriesRepository.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/repository/SeriesRepository.scala @@ -24,7 +24,7 @@ import scala.util.{Failure, Success, Try} trait SeriesRepository { this: DataSource & Props & ErrorHandling => - val seriesRepository: SeriesRepository + lazy val seriesRepository: SeriesRepository class SeriesRepository extends StrictLogging with Repository[Series] { diff --git a/audio-api/src/main/scala/no/ndla/audioapi/service/ConverterService.scala b/audio-api/src/main/scala/no/ndla/audioapi/service/ConverterService.scala index df80eccd7f..61e25a8ba5 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/service/ConverterService.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/service/ConverterService.scala @@ -22,15 +22,14 @@ import no.ndla.language.model.WithLanguage import no.ndla.mapping.License.getLicense import no.ndla.network.tapir.auth.TokenUser +import scala.reflect.ClassTag import scala.util.{Failure, Success, Try} trait ConverterService { this: Clock & Props => - val converterService: ConverterService + lazy val converterService: ConverterService class ConverterService extends StrictLogging { - import props.* - def updateSeries(existingSeries: domain.Series, updatedSeries: api.NewSeriesDTO): domain.Series = { val newTitle = common.Title(updatedSeries.title, updatedSeries.language) val newDescription = domain.Description(updatedSeries.description, updatedSeries.language) @@ -130,21 +129,27 @@ trait ConverterService { private def maybeToApiTitle(maybeTitle: Option[common.Title]): api.TitleDTO = { maybeTitle match { case Some(title) => toApiTitle(title) - case None => api.TitleDTO("", DefaultLanguage) + case None => api.TitleDTO("", props.DefaultLanguage) } } private def toApiTags(maybeTag: Option[common.Tag]): TagDTO = { maybeTag match { case Some(tag) => api.TagDTO(tag.tags, tag.language) - case None => api.TagDTO(Seq(), DefaultLanguage) + case None => api.TagDTO(Seq(), props.DefaultLanguage) } } def toApiAudio(audio: Option[domain.Audio]): api.AudioDTO = { audio match { - case Some(x) => api.AudioDTO(s"$Domain/$AudioFilesUrlSuffix/${x.filePath}", x.mimeType, x.fileSize, x.language) - case None => api.AudioDTO("", "", 0, DefaultLanguage) + case Some(x) => + api.AudioDTO( + s"${props.Domain}/${props.AudioFilesUrlSuffix}/${x.filePath}", + x.mimeType, + x.fileSize, + x.language + ) + case None => api.AudioDTO("", "", 0, props.DefaultLanguage) } } @@ -190,7 +195,7 @@ trait ConverterService { ) } - def getPhotoUrl(meta: domain.CoverPhoto): String = s"$RawImageApiUrl/${meta.imageId}" + def getPhotoUrl(meta: domain.CoverPhoto): String = s"${props.RawImageApiUrl}/${meta.imageId}" def toApiCoverPhoto(meta: domain.CoverPhoto): api.CoverPhotoDTO = { api.CoverPhotoDTO( @@ -252,13 +257,13 @@ trait ConverterService { def findAndConvertDomainToApiField[DomainType <: WithLanguage]( fields: Seq[DomainType], language: Option[String] - )(implicit mf: Manifest[DomainType]): Try[DomainType] = { - findByLanguageOrBestEffort(fields, language.getOrElse(DefaultLanguage)) match { + )(implicit ct: ClassTag[DomainType]): Try[DomainType] = { + findByLanguageOrBestEffort(fields, language.getOrElse(props.DefaultLanguage)) match { case Some(field) => Success(field) case None => Failure( CouldNotFindLanguageException( - s"Could not find value for '${mf.runtimeClass.getName}' field. This is a data inconsistency or a bug." + s"Could not find value for '${ct.runtimeClass.getName}' field. This is a data inconsistency or a bug." ) ) } diff --git a/audio-api/src/main/scala/no/ndla/audioapi/service/ReadService.scala b/audio-api/src/main/scala/no/ndla/audioapi/service/ReadService.scala index 57052e24eb..41668f7b38 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/service/ReadService.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/service/ReadService.scala @@ -18,7 +18,7 @@ import scala.util.{Failure, Success, Try} trait ReadService { this: AudioRepository & SeriesRepository & ConverterService & TagSearchService & SearchConverterService => - val readService: ReadService + lazy val readService: ReadService class ReadService { diff --git a/audio-api/src/main/scala/no/ndla/audioapi/service/TranscriptionService.scala b/audio-api/src/main/scala/no/ndla/audioapi/service/TranscriptionService.scala index 4b67bc77bd..ba2f646e5d 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/service/TranscriptionService.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/service/TranscriptionService.scala @@ -24,8 +24,8 @@ import scala.util.{Failure, Success, Try} trait TranscriptionService { this: NdlaS3Client & Props & NdlaBrightcoveClient & NdlaAWSTranscribeClient => - val transcriptionService: TranscriptionService - val s3TranscribeClient: NdlaS3Client + lazy val transcriptionService: TranscriptionService + lazy val s3TranscribeClient: NdlaS3Client sealed trait TranscriptionResult case class TranscriptionComplete(transcription: String) extends TranscriptionResult @@ -92,7 +92,7 @@ trait TranscriptionService { val transcriptionJob = transcriptionJobResponse.transcriptionJob() val transcriptionJobStatus = transcriptionJob.transcriptionJobStatus() - if (transcriptionJobStatus == "COMPLETED") { + if (transcriptionJobStatus == TranscriptionJobStatus.COMPLETED) { val transcribeUri = s"transcription/$language/$videoId.vtt" getObjectFromS3(transcribeUri).map(TranscriptionComplete(_)) diff --git a/audio-api/src/main/scala/no/ndla/audioapi/service/ValidationService.scala b/audio-api/src/main/scala/no/ndla/audioapi/service/ValidationService.scala index 9e14ddac51..10e86d008d 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/service/ValidationService.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/service/ValidationService.scala @@ -27,7 +27,7 @@ import scala.util.{Failure, Success, Try} trait ValidationService { this: ConverterService & Props => - val validationService: ValidationService + lazy val validationService: ValidationService class ValidationService { @@ -174,7 +174,7 @@ trait ValidationService { } else Seq.empty } - def validate[T <: domain.SeriesWithoutId](series: T): Try[T] = { + def validate(series: domain.SeriesWithoutId): Try[domain.SeriesWithoutId] = { val validationMessages = validateNonEmpty("title", series.title).toSeq ++ series.title.flatMap(title => validateNonEmpty("title", title.language)) ++ series.title.flatMap(title => validateTitle("title", title, Seq.empty)) ++ diff --git a/audio-api/src/main/scala/no/ndla/audioapi/service/WriteService.scala b/audio-api/src/main/scala/no/ndla/audioapi/service/WriteService.scala index 46881214ec..973386806a 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/service/WriteService.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/service/WriteService.scala @@ -29,7 +29,7 @@ import scala.util.{Failure, Random, Success, Try} trait WriteService { this: ConverterService & ValidationService & AudioRepository & SeriesRepository & AudioIndexService & SeriesIndexService & TagIndexService & NdlaS3Client & ReadService & Clock => - val writeService: WriteService + lazy val writeService: WriteService class WriteService extends StrictLogging { @@ -49,10 +49,10 @@ trait WriteService { episodesToValidate.toSeq, Some(existingSeries.id) ) - validatedSeries <- validationService.validate(merged) - updatedSeries <- seriesRepository.update(validatedSeries) + validatedSeries <- validationService.validate(merged.withoutId) + updatedSeries <- seriesRepository.update(merged) _ <- updateSeriesForEpisodes(None, episodesToDelete.toSeq) - _ <- updateSeriesForEpisodes(Some(validatedSeries.id), episodesToAdd.toSeq) + _ <- updateSeriesForEpisodes(Some(merged.id), episodesToAdd.toSeq) updatedWithEpisodes = updatedSeries.copy(episodes = Some(validatedEpisodes)) _ <- seriesIndexService.indexDocument(updatedWithEpisodes) converted <- converterService.toApiSeries(updatedWithEpisodes, Some(toUpdateSeries.language)) @@ -166,8 +166,8 @@ trait WriteService { deleteSeries(seriesId).map(_ => None) else { for { - validated <- validationService.validate(newSeries) - updated <- seriesRepository.update(validated) + validated <- validationService.validate(newSeries.withoutId) + updated <- seriesRepository.update(newSeries) indexed <- seriesIndexService.indexDocument(updated) converted <- converterService.toApiSeries(indexed, None) result = Some(converted) diff --git a/audio-api/src/main/scala/no/ndla/audioapi/service/search/AudioIndexService.scala b/audio-api/src/main/scala/no/ndla/audioapi/service/search/AudioIndexService.scala index dfa2dc95e1..86a0bf17f6 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/service/search/AudioIndexService.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/service/search/AudioIndexService.scala @@ -26,12 +26,11 @@ import scala.util.{Failure, Try} trait AudioIndexService { this: Elastic4sClient & SearchConverterService & IndexService & SeriesIndexService & AudioRepository & Props => - val audioIndexService: AudioIndexService + lazy val audioIndexService: AudioIndexService - class AudioIndexService extends StrictLogging with IndexService[AudioMetaInformation, SearchableAudioInformation] { - import props.* - override val documentType: String = SearchDocument - override val searchIndex: String = SearchIndex + class AudioIndexService extends IndexService[AudioMetaInformation, SearchableAudioInformation] with StrictLogging { + override val documentType: String = props.SearchDocument + override val searchIndex: String = props.SearchIndex override val repository: AudioRepository = audioRepository override def createIndexRequests(domainModel: AudioMetaInformation, indexName: String): Try[Seq[IndexRequest]] = { diff --git a/audio-api/src/main/scala/no/ndla/audioapi/service/search/AudioSearchService.scala b/audio-api/src/main/scala/no/ndla/audioapi/service/search/AudioSearchService.scala index 83dbb4932b..ac13bb8aad 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/service/search/AudioSearchService.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/service/search/AudioSearchService.scala @@ -28,12 +28,10 @@ import scala.util.{Failure, Success, Try} trait AudioSearchService { this: Elastic4sClient & AudioIndexService & SearchConverterService & SearchService & Props & ErrorHandling => - val audioSearchService: AudioSearchService + lazy val audioSearchService: AudioSearchService class AudioSearchService extends StrictLogging with SearchService[api.AudioSummaryDTO] { - import props.* - - override val searchIndex: String = SearchIndex + override val searchIndex: String = props.SearchIndex override def hitToApiModel(hitString: String, language: String): Try[api.AudioSummaryDTO] = { for { @@ -106,9 +104,9 @@ trait AudioSearchService { val (startAt, numResults) = getStartAtAndNumResults(settings.page, settings.pageSize) val requestedResultWindow = settings.page.getOrElse(1) * numResults - if (requestedResultWindow > ElasticSearchIndexMaxResultWindow) { + if (requestedResultWindow > props.ElasticSearchIndexMaxResultWindow) { logger.info( - s"Max supported results are $ElasticSearchIndexMaxResultWindow, user requested $requestedResultWindow" + s"Max supported results are ${props.ElasticSearchIndexMaxResultWindow}, user requested $requestedResultWindow" ) Failure(new Helpers.ResultWindowTooLargeException()) } else { @@ -125,7 +123,7 @@ trait AudioSearchService { // Only add scroll param if it is first page val searchWithScroll = if (startAt == 0 && settings.shouldScroll) { - searchToExecute.scroll(ElasticSearchScrollKeepAlive) + searchToExecute.scroll(props.ElasticSearchScrollKeepAlive) } else { searchToExecute } e4sClient.execute(searchWithScroll) match { diff --git a/audio-api/src/main/scala/no/ndla/audioapi/service/search/IndexService.scala b/audio-api/src/main/scala/no/ndla/audioapi/service/search/IndexService.scala index 804b387232..ab1cf0dc5f 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/service/search/IndexService.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/service/search/IndexService.scala @@ -24,7 +24,7 @@ import scala.util.{Failure, Success, Try} trait IndexService { this: Elastic4sClient & BaseIndexService & SearchConverterService & AudioRepository & Props & SearchLanguage => - trait IndexService[D, T] extends BaseIndexService with StrictLogging { + abstract class IndexService[D, T] extends BaseIndexService with StrictLogging { override val MaxResultWindowOption: Int = props.ElasticSearchIndexMaxResultWindow val documentType: String diff --git a/audio-api/src/main/scala/no/ndla/audioapi/service/search/SearchConverterService.scala b/audio-api/src/main/scala/no/ndla/audioapi/service/search/SearchConverterService.scala index 43b78e475d..5f83bad0ac 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/service/search/SearchConverterService.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/service/search/SearchConverterService.scala @@ -26,7 +26,7 @@ import scala.util.Try trait SearchConverterService { this: ConverterService & Props & SearchLanguage => - val searchConverterService: SearchConverterService + lazy val searchConverterService: SearchConverterService class SearchConverterService extends StrictLogging { diff --git a/audio-api/src/main/scala/no/ndla/audioapi/service/search/SearchService.scala b/audio-api/src/main/scala/no/ndla/audioapi/service/search/SearchService.scala index 872a21c6a6..dce96d80ea 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/service/search/SearchService.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/service/search/SearchService.scala @@ -29,13 +29,12 @@ trait SearchService { this: Elastic4sClient & SearchConverterService & Props & SearchLanguage => trait SearchService[T] extends StrictLogging { - import props.* val searchIndex: String def scroll(scrollId: String, language: String): Try[SearchResult[T]] = e4sClient .execute { - searchScroll(scrollId, ElasticSearchScrollKeepAlive) + searchScroll(scrollId, props.ElasticSearchScrollKeepAlive) } .flatMap(response => { getHits(response.result, language).map(hits => { @@ -145,8 +144,8 @@ trait SearchService { def getStartAtAndNumResults(page: Option[Int], pageSize: Option[Int]): (Int, Int) = { val numResults = pageSize match { - case Some(num) => if (num > 0) num.min(MaxPageSize) else DefaultPageSize - case None => DefaultPageSize + case Some(num) => if (num > 0) num.min(props.MaxPageSize) else props.DefaultPageSize + case None => props.DefaultPageSize } val startAt = page match { diff --git a/audio-api/src/main/scala/no/ndla/audioapi/service/search/SeriesIndexService.scala b/audio-api/src/main/scala/no/ndla/audioapi/service/search/SeriesIndexService.scala index f9ebf180af..36b5f11f7c 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/service/search/SeriesIndexService.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/service/search/SeriesIndexService.scala @@ -25,12 +25,11 @@ import scala.util.{Failure, Success, Try} trait SeriesIndexService { this: Elastic4sClient & SearchConverterService & IndexService & SeriesRepository & Props => - val seriesIndexService: SeriesIndexService + lazy val seriesIndexService: SeriesIndexService - class SeriesIndexService extends StrictLogging with IndexService[Series, SearchableSeries] { - import props.* - override val documentType: String = SeriesSearchDocument - override val searchIndex: String = SeriesSearchIndex + class SeriesIndexService extends IndexService[Series, SearchableSeries] with StrictLogging { + override val documentType: String = props.SeriesSearchDocument + override val searchIndex: String = props.SeriesSearchIndex override val repository: SeriesRepository = seriesRepository override def createIndexRequests(domainModel: Series, indexName: String): Try[Seq[IndexRequest]] = { diff --git a/audio-api/src/main/scala/no/ndla/audioapi/service/search/SeriesSearchService.scala b/audio-api/src/main/scala/no/ndla/audioapi/service/search/SeriesSearchService.scala index b85a6a24c9..edc5a73f3d 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/service/search/SeriesSearchService.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/service/search/SeriesSearchService.scala @@ -29,12 +29,10 @@ import scala.util.{Failure, Success, Try} trait SeriesSearchService { this: Elastic4sClient & SeriesIndexService & SearchConverterService & SearchService & ConverterService & Props & ErrorHandling => - val seriesSearchService: SeriesSearchService + lazy val seriesSearchService: SeriesSearchService class SeriesSearchService extends StrictLogging with SearchService[api.SeriesSummaryDTO] { - import props.* - - override val searchIndex: String = SeriesSearchIndex + override val searchIndex: String = props.SeriesSearchIndex override def hitToApiModel(hitString: String, language: String): Try[api.SeriesSummaryDTO] = { for { @@ -88,9 +86,9 @@ trait SeriesSearchService { val (startAt, numResults) = getStartAtAndNumResults(settings.page, settings.pageSize) val requestedResultWindow = settings.page.getOrElse(1) * numResults - if (requestedResultWindow > ElasticSearchIndexMaxResultWindow) { + if (requestedResultWindow > props.ElasticSearchIndexMaxResultWindow) { logger.info( - s"Max supported results are $ElasticSearchIndexMaxResultWindow, user requested $requestedResultWindow" + s"Max supported results are ${props.ElasticSearchIndexMaxResultWindow}, user requested $requestedResultWindow" ) Failure(new Helpers.ResultWindowTooLargeException()) } else { @@ -107,7 +105,7 @@ trait SeriesSearchService { // Only add scroll param if it is first page val searchWithScroll = if (startAt == 0 && settings.shouldScroll) { - searchToExecute.scroll(ElasticSearchScrollKeepAlive) + searchToExecute.scroll(props.ElasticSearchScrollKeepAlive) } else { searchToExecute } e4sClient.execute(searchWithScroll) match { diff --git a/audio-api/src/main/scala/no/ndla/audioapi/service/search/TagIndexService.scala b/audio-api/src/main/scala/no/ndla/audioapi/service/search/TagIndexService.scala index e54e1fb9ce..b33376915c 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/service/search/TagIndexService.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/service/search/TagIndexService.scala @@ -21,13 +21,11 @@ import scala.util.{Success, Try} trait TagIndexService { this: SearchConverterService & IndexService & AudioRepository & Props => - val tagIndexService: TagIndexService + lazy val tagIndexService: TagIndexService - class TagIndexService extends StrictLogging with IndexService[AudioMetaInformation, SearchableTag] { - import props.* - - override val documentType: String = AudioTagSearchDocument - override val searchIndex: String = AudioTagSearchIndex + class TagIndexService extends IndexService[AudioMetaInformation, SearchableTag] with StrictLogging { + override val documentType: String = props.AudioTagSearchDocument + override val searchIndex: String = props.AudioTagSearchIndex override val repository: Repository[AudioMetaInformation] = audioRepository override def createIndexRequests(domainModel: AudioMetaInformation, indexName: String): Try[Seq[IndexRequest]] = { diff --git a/audio-api/src/main/scala/no/ndla/audioapi/service/search/TagSearchService.scala b/audio-api/src/main/scala/no/ndla/audioapi/service/search/TagSearchService.scala index 388d688dba..964e193164 100644 --- a/audio-api/src/main/scala/no/ndla/audioapi/service/search/TagSearchService.scala +++ b/audio-api/src/main/scala/no/ndla/audioapi/service/search/TagSearchService.scala @@ -26,12 +26,10 @@ import scala.util.{Failure, Success, Try} trait TagSearchService { this: Elastic4sClient & SearchConverterService & SearchService & TagIndexService & SearchConverterService & Props & ErrorHandling => - val tagSearchService: TagSearchService + lazy val tagSearchService: TagSearchService class TagSearchService extends StrictLogging with SearchService[String] { - import props.* - - override val searchIndex: String = AudioTagSearchIndex + override val searchIndex: String = props.AudioTagSearchIndex override def hitToApiModel(hit: String, language: String): Try[String] = { CirceUtil.tryParseAs[SearchableTag](hit).map(_.tag) @@ -73,9 +71,9 @@ trait TagSearchService { val (startAt, numResults) = getStartAtAndNumResults(Some(page), Some(pageSize)) val requestedResultWindow = pageSize * page - if (requestedResultWindow > ElasticSearchIndexMaxResultWindow) { + if (requestedResultWindow > props.ElasticSearchIndexMaxResultWindow) { logger.info( - s"Max supported results are $ElasticSearchIndexMaxResultWindow, user requested $requestedResultWindow" + s"Max supported results are ${props.ElasticSearchIndexMaxResultWindow}, user requested $requestedResultWindow" ) Failure(new Helpers.ResultWindowTooLargeException()) } else { @@ -88,7 +86,7 @@ trait TagSearchService { val searchWithScroll = if (startAt != 0) { searchToExecute } - else { searchToExecute.scroll(ElasticSearchScrollKeepAlive) } + else { searchToExecute.scroll(props.ElasticSearchScrollKeepAlive) } e4sClient.execute(searchWithScroll) match { case Success(response) => diff --git a/audio-api/src/test/scala/no/ndla/audioapi/TestEnvironment.scala b/audio-api/src/test/scala/no/ndla/audioapi/TestEnvironment.scala index cfb2374a1c..b809f057e9 100644 --- a/audio-api/src/test/scala/no/ndla/audioapi/TestEnvironment.scala +++ b/audio-api/src/test/scala/no/ndla/audioapi/TestEnvironment.scala @@ -59,40 +59,40 @@ trait TestEnvironment with ErrorHandling { override lazy val props: AudioApiProperties = new AudioApiProperties - val dataSource: HikariDataSource = mock[HikariDataSource] - val audioRepository: AudioRepository = mock[AudioRepository] - val seriesRepository: SeriesRepository = mock[SeriesRepository] + override lazy val dataSource: HikariDataSource = mock[HikariDataSource] + override lazy val audioRepository: AudioRepository = mock[AudioRepository] + override lazy val seriesRepository: SeriesRepository = mock[SeriesRepository] - val s3Client: NdlaS3Client = mock[NdlaS3Client] - val brightcoveClient: NdlaBrightcoveClient = mock[NdlaBrightcoveClient] - val transcribeClient: NdlaAWSTranscribeClient = mock[NdlaAWSTranscribeClient] + override lazy val s3Client: NdlaS3Client = mock[NdlaS3Client] + override lazy val brightcoveClient: NdlaBrightcoveClient = mock[NdlaBrightcoveClient] + override lazy val transcribeClient: NdlaAWSTranscribeClient = mock[NdlaAWSTranscribeClient] - val ndlaClient: NdlaClient = mock[NdlaClient] - val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] + override lazy val ndlaClient: NdlaClient = mock[NdlaClient] + override lazy val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] - val readService: ReadService = mock[ReadService] - val writeService: WriteService = mock[WriteService] - val validationService: ValidationService = mock[ValidationService] - val converterService: ConverterService = mock[ConverterService] - val transcriptionService: TranscriptionService = mock[TranscriptionService] - val s3TranscribeClient: NdlaS3Client = mock[NdlaS3Client] + override lazy val readService: ReadService = mock[ReadService] + override lazy val writeService: WriteService = mock[WriteService] + override lazy val validationService: ValidationService = mock[ValidationService] + override lazy val converterService: ConverterService = mock[ConverterService] + override lazy val transcriptionService: TranscriptionService = mock[TranscriptionService] + override lazy val s3TranscribeClient: NdlaS3Client = mock[NdlaS3Client] - val internController: InternController = mock[InternController] - val audioApiController: AudioController = mock[AudioController] - val healthController: HealthController = mock[HealthController] - val seriesController: SeriesController = mock[SeriesController] + override lazy val internController: InternController = mock[InternController] + override lazy val audioApiController: AudioController = mock[AudioController] + override lazy val healthController: HealthController = mock[HealthController] + override lazy val seriesController: SeriesController = mock[SeriesController] - var e4sClient: NdlaE4sClient = mock[NdlaE4sClient] - val audioSearchService: AudioSearchService = mock[AudioSearchService] - val audioIndexService: AudioIndexService = mock[AudioIndexService] - val seriesSearchService: SeriesSearchService = mock[SeriesSearchService] - val seriesIndexService: SeriesIndexService = mock[SeriesIndexService] - val tagSearchService: TagSearchService = mock[TagSearchService] - val tagIndexService: TagIndexService = mock[TagIndexService] - val searchConverterService: SearchConverterService = mock[SearchConverterService] + var e4sClient: NdlaE4sClient = mock[NdlaE4sClient] + override lazy val audioSearchService: AudioSearchService = mock[AudioSearchService] + override lazy val audioIndexService: AudioIndexService = mock[AudioIndexService] + override lazy val seriesSearchService: SeriesSearchService = mock[SeriesSearchService] + override lazy val seriesIndexService: SeriesIndexService = mock[SeriesIndexService] + override lazy val tagSearchService: TagSearchService = mock[TagSearchService] + override lazy val tagIndexService: TagIndexService = mock[TagIndexService] + override lazy val searchConverterService: SearchConverterService = mock[SearchConverterService] - val clock: SystemClock = mock[SystemClock] - def services: List[TapirController] = List.empty - val swagger: SwaggerController = mock[SwaggerController] + override lazy val clock: SystemClock = mock[SystemClock] + def services: List[TapirController] = List.empty + val swagger: SwaggerController = mock[SwaggerController] } diff --git a/audio-api/src/test/scala/no/ndla/audioapi/controller/AudioControllerTest.scala b/audio-api/src/test/scala/no/ndla/audioapi/controller/AudioControllerTest.scala index 2522e090ac..3ae58870dc 100644 --- a/audio-api/src/test/scala/no/ndla/audioapi/controller/AudioControllerTest.scala +++ b/audio-api/src/test/scala/no/ndla/audioapi/controller/AudioControllerTest.scala @@ -293,8 +293,6 @@ class AudioControllerTest extends UnitSuite with TestEnvironment with Retries wi } test("That deleting language returns audio if exists") { - import io.circe.generic.auto.* - when(writeService.deleteAudioLanguageVersion(1, "nb")) .thenReturn(Success(Some(TestData.DefaultApiImageMetaInformation))) @@ -352,11 +350,10 @@ class AudioControllerTest extends UnitSuite with TestEnvironment with Retries wi .get(uri"http://localhost:$serverPort/audio-api/v1/audio/ids/?ids=1,2,3") ) response.code.code should be(200) - import io.circe.generic.auto.* val parsedBody = unsafeParseAs[List[api.AudioMetaInformationDTO]](response.body) parsedBody should be(expectedResult) - verify(readService, times(1)).getAudiosByIds(eqTo(List(1, 2, 3)), any) + verify(readService, times(1)).getAudiosByIds(eqTo(List(1L, 2L, 3L)), any) } test("That GET /?query= doesnt pass empty-string search parameter") { diff --git a/audio-api/src/test/scala/no/ndla/audioapi/controller/InternControllerTest.scala b/audio-api/src/test/scala/no/ndla/audioapi/controller/InternControllerTest.scala index 60cc47eba6..0a66697886 100644 --- a/audio-api/src/test/scala/no/ndla/audioapi/controller/InternControllerTest.scala +++ b/audio-api/src/test/scala/no/ndla/audioapi/controller/InternControllerTest.scala @@ -23,7 +23,7 @@ import sttp.client3.quick.* import scala.util.{Failure, Success} class InternControllerTest extends UnitSuite with TestEnvironment with TapirControllerTest { - override val converterService = new ConverterService + override lazy val converterService = new ConverterService override val controller: InternController = new InternController val DefaultDomainImageMetaInformation: AudioMetaInformation = domain.AudioMetaInformation( diff --git a/audio-api/src/test/scala/no/ndla/audioapi/service/TranscriptionServiceTest.scala b/audio-api/src/test/scala/no/ndla/audioapi/service/TranscriptionServiceTest.scala index a389c55ace..0a42960ba6 100644 --- a/audio-api/src/test/scala/no/ndla/audioapi/service/TranscriptionServiceTest.scala +++ b/audio-api/src/test/scala/no/ndla/audioapi/service/TranscriptionServiceTest.scala @@ -23,9 +23,9 @@ import software.amazon.awssdk.services.transcribe.model.{ import scala.util.Success class TranscriptionServiceTest extends UnitSuite with TestEnvironment { - override val transcriptionService: TranscriptionService = new TranscriptionService - override val brightcoveClient: NdlaBrightcoveClient = new NdlaBrightcoveClient - override lazy val props: AudioApiProperties = new AudioApiProperties { + override lazy val transcriptionService: TranscriptionService = new TranscriptionService + override lazy val brightcoveClient: NdlaBrightcoveClient = new NdlaBrightcoveClient + override lazy val props: AudioApiProperties = new AudioApiProperties { override val BrightcoveAccountId: Prop[String] = propFromTestValue("BRIGHTCOVE_ACCOUNT_ID", "123") override val BrightcoveClientId: Prop[String] = propFromTestValue("BRIGHTCOIVE_CLIENT_ID", "123") override val BrightcoveClientSecret: Prop[String] = propFromTestValue("BRIGHTCOVE_CLIENT_SECRET", "123") diff --git a/audio-api/src/test/scala/no/ndla/audioapi/service/ValidationServiceTest.scala b/audio-api/src/test/scala/no/ndla/audioapi/service/ValidationServiceTest.scala index 680443707c..8e4eece230 100644 --- a/audio-api/src/test/scala/no/ndla/audioapi/service/ValidationServiceTest.scala +++ b/audio-api/src/test/scala/no/ndla/audioapi/service/ValidationServiceTest.scala @@ -19,7 +19,7 @@ import org.mockito.Mockito.{doReturn, reset, spy, when} import java.awt.image.BufferedImage class ValidationServiceTest extends UnitSuite with TestEnvironment { - override val validationService: ValidationService = spy(new ValidationService) + override lazy val validationService: ValidationService = spy(new ValidationService) override def beforeEach(): Unit = { reset(validationService) diff --git a/audio-api/src/test/scala/no/ndla/audioapi/service/WriteServiceTest.scala b/audio-api/src/test/scala/no/ndla/audioapi/service/WriteServiceTest.scala index 38e94c630d..d59f753a2e 100644 --- a/audio-api/src/test/scala/no/ndla/audioapi/service/WriteServiceTest.scala +++ b/audio-api/src/test/scala/no/ndla/audioapi/service/WriteServiceTest.scala @@ -30,8 +30,8 @@ import java.io.FileInputStream import scala.util.{Failure, Success} class WriteServiceTest extends UnitSuite with TestEnvironment { - override val writeService = new WriteService - override val converterService = new ConverterService + override lazy val writeService = new WriteService + override lazy val converterService = new ConverterService val (newFileName1, newFileName2) = ("AbCdeF.mp3", "GhijKl.mp3") val filePartMock: UploadedFile = mock[UploadedFile] val s3ObjectMock: HeadObjectResponse = mock[HeadObjectResponse] @@ -114,7 +114,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { val fis = mock[FileInputStream] when(filePartMock.stream).thenReturn(fis) - when(s3ObjectMock.contentLength()).thenReturn(1024) + when(s3ObjectMock.contentLength()).thenReturn(1024L) when(s3ObjectMock.contentType()).thenReturn("audio/mp3") reset(audioRepository) @@ -281,7 +281,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { when(s3Client.headObject(any)).thenReturn(Success(s3ObjectMock)) when(audioIndexService.indexDocument(any[domain.AudioMetaInformation])).thenReturn(Success(afterInsert)) when(tagIndexService.indexDocument(any[domain.AudioMetaInformation])).thenReturn(Success(afterInsert)) - when(audioRepository.setSeriesId(any, any)(any)).thenReturn(Success(1)) + when(audioRepository.setSeriesId(any, any)(any)).thenReturn(Success(1L)) val result = writeService.storeNewAudio(newAudioMeta, filePartMock, testUser) result.isSuccess should be(true) @@ -488,7 +488,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { when(audioRepository.update(any[domain.AudioMetaInformation], any[Long])).thenReturn(Success(afterInsert)) when(audioIndexService.indexDocument(any[domain.AudioMetaInformation])).thenReturn(Success(afterInsert)) when(tagIndexService.indexDocument(any[domain.AudioMetaInformation])).thenReturn(Success(afterInsert)) - when(audioRepository.setSeriesId(any, any)(any)).thenReturn(Success(1)) + when(audioRepository.setSeriesId(any, any)(any)).thenReturn(Success(1L)) val result = writeService.updateAudio(1, updatedAudioMeta, Some(filePartMock), testUser) result.isSuccess should be(true) @@ -645,8 +645,8 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { Success(passedEpisodes) }) - when(validationService.validate(any[domain.Series])).thenAnswer((i: InvocationOnMock) => { - Success(i.getArgument[domain.Series](0)) + when(validationService.validate(any[domain.SeriesWithoutId])).thenAnswer((i: InvocationOnMock) => { + Success(i.getArgument[domain.SeriesWithoutId](0)) }) } @@ -663,7 +663,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { val id = i.getArgument[Long](0) series.episodes.get.find(_.id.contains(id)) }) - when(seriesRepository.insert(any[domain.Series])(any[DBSession])) + when(seriesRepository.insert(any[domain.SeriesWithoutId])(any[DBSession])) .thenReturn(Failure(new RuntimeException("weird failure there buddy"))) val updateSeries = api.NewSeriesDTO( @@ -743,7 +743,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { val id = i.getArgument[Long](0) series.episodes.get.find(_.id.contains(id)) }) - when(audioRepository.setSeriesId(any[Long], any[Option[Long]])(any[DBSession])).thenReturn(Success(1)) + when(audioRepository.setSeriesId(any[Long], any[Option[Long]])(any[DBSession])).thenReturn(Success(1L)) when(seriesRepository.insert(any[domain.SeriesWithoutId])(any[DBSession])) .thenAnswer((i: InvocationOnMock) => { @@ -797,7 +797,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { val id = i.getArgument[Long](0) episodesMap.get(id) }) - when(audioRepository.setSeriesId(any[Long], any[Option[Long]])(any[DBSession])).thenReturn(Success(1)) + when(audioRepository.setSeriesId(any[Long], any[Option[Long]])(any[DBSession])).thenReturn(Success(1L)) when(seriesRepository.update(any[domain.Series])(any[DBSession])).thenAnswer((i: InvocationOnMock) => { Success(i.getArgument[domain.Series](0)) }) @@ -816,7 +816,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { hasRSS = Some(true) ) - val result = writeService.updateSeries(1, updateSeries) + val result = writeService.updateSeries(1L, updateSeries) result.isSuccess should be(true) verify(audioRepository, times(1)).setSeriesId(eqTo(1L), eqTo(None))(any[DBSession]) @@ -1000,10 +1000,10 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { ) ) - val afterInsert = audio.copy(id = Some(5555), revision = Some(1)) + val afterInsert = audio.copy(id = Some(5555L), revision = Some(1)) when(s3Client.deleteObject(any)).thenReturn(Success(mock[DeleteObjectResponse])) - when(audioRepository.withId(5555)).thenReturn(Some(audio)) + when(audioRepository.withId(5555L)).thenReturn(Some(audio)) when(validationService.validateAudioFile(any)).thenReturn(Seq.empty) when(s3Client.putObject(any, any)).thenReturn(Success(mock[PutObjectResponse])) when(s3Client.headObject(any)).thenReturn(Success(s3ObjectMock)) @@ -1019,7 +1019,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { when(audioRepository.update(any[domain.AudioMetaInformation], any[Long])).thenReturn(Success(afterInsert)) when(audioIndexService.indexDocument(any[domain.AudioMetaInformation])).thenReturn(Success(afterInsert)) when(tagIndexService.indexDocument(any[domain.AudioMetaInformation])).thenReturn(Success(afterInsert)) - when(audioRepository.setSeriesId(any, any)(any)).thenReturn(Success(5555)) + when(audioRepository.setSeriesId(any, any)(any)).thenReturn(Success(5555L)) val result = writeService.updateAudio(5555, updatedAudioMeta, Some(filePartMock), testUser) result.isSuccess should be(true) diff --git a/audio-api/src/test/scala/no/ndla/audioapi/service/search/AudioSearchServiceTest.scala b/audio-api/src/test/scala/no/ndla/audioapi/service/search/AudioSearchServiceTest.scala index ba610c0eb4..f64b0e6f1d 100644 --- a/audio-api/src/test/scala/no/ndla/audioapi/service/search/AudioSearchServiceTest.scala +++ b/audio-api/src/test/scala/no/ndla/audioapi/service/search/AudioSearchServiceTest.scala @@ -23,18 +23,16 @@ import org.mockito.Mockito.when import scala.util.Success class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite with TestEnvironment { - import props.* - e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("http://localhost:9200")) - override val audioSearchService = new AudioSearchService - override val audioIndexService: AudioIndexService = new AudioIndexService { + override lazy val audioSearchService = new AudioSearchService + override lazy val audioIndexService: AudioIndexService = new AudioIndexService { override val indexShards = 1 } - override val seriesIndexService: SeriesIndexService = new SeriesIndexService { + override lazy val seriesIndexService: SeriesIndexService = new SeriesIndexService { override val indexShards = 1 } - override val searchConverterService = new SearchConverterService + override lazy val searchConverterService = new SearchConverterService val byNcSa: Copyright = Copyright( @@ -254,33 +252,33 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit } test("That getStartAtAndNumResults returns default values for None-input") { - audioSearchService.getStartAtAndNumResults(None, None) should equal((0, DefaultPageSize)) + audioSearchService.getStartAtAndNumResults(None, None) should equal((0, props.DefaultPageSize)) } test("That getStartAtAndNumResults returns SEARCH_MAX_PAGE_SIZE for value greater than SEARCH_MAX_PAGE_SIZE") { - audioSearchService.getStartAtAndNumResults(None, Some(10001)) should equal((0, MaxPageSize)) + audioSearchService.getStartAtAndNumResults(None, Some(10001)) should equal((0, props.MaxPageSize)) } test( "That getStartAtAndNumResults returns the correct calculated start at for page and page-size with default page-size" ) { val page = 74 - val expectedStartAt = (page - 1) * DefaultPageSize + val expectedStartAt = (page - 1) * props.DefaultPageSize audioSearchService.getStartAtAndNumResults(Some(page), None) should equal( - (expectedStartAt, DefaultPageSize) + (expectedStartAt, props.DefaultPageSize) ) } test("That getStartAtAndNumResults returns the correct calculated start at for page and page-size") { val page = 123 - val expectedStartAt = (page - 1) * MaxPageSize - audioSearchService.getStartAtAndNumResults(Some(page), Some(MaxPageSize)) should equal( - (expectedStartAt, MaxPageSize) + val expectedStartAt = (page - 1) * props.MaxPageSize + audioSearchService.getStartAtAndNumResults(Some(page), Some(props.MaxPageSize)) should equal( + (expectedStartAt, props.MaxPageSize) ) } test("That no language returns all documents ordered by title ascending") { - val Success(results) = audioSearchService.matchingQuery(searchSettings.copy()) + val Success(results) = audioSearchService.matchingQuery(searchSettings.copy()): @unchecked results.totalCount should be(6) results.results.head.id should be(4) results.results.last.id should be(6) @@ -288,15 +286,17 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That filtering on license only returns documents with given license for all languages") { val Success(results) = - audioSearchService.matchingQuery(searchSettings.copy(license = Some(License.PublicDomain.toString))) + audioSearchService.matchingQuery(searchSettings.copy(license = Some(License.PublicDomain.toString))): @unchecked results.totalCount should be(2) results.results.head.id should be(4) results.results.last.id should be(2) } test("That paging returns only hits on current page and not more than page-size") { - val Success(page1) = audioSearchService.matchingQuery(searchSettings.copy(page = Some(1), pageSize = Some(2))) - val Success(page2) = audioSearchService.matchingQuery(searchSettings.copy(page = Some(2), pageSize = Some(2))) + val Success(page1) = + audioSearchService.matchingQuery(searchSettings.copy(page = Some(1), pageSize = Some(2))): @unchecked + val Success(page2) = + audioSearchService.matchingQuery(searchSettings.copy(page = Some(2), pageSize = Some(2))): @unchecked page1.totalCount should be(6) page1.page.get should be(1) page1.results.size should be(2) @@ -314,7 +314,7 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit query = Some("Pingvinen"), language = Some("nb") ) - ) + ): @unchecked results.totalCount should be(1) results.results.head.id should be(2) } @@ -325,7 +325,7 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit query = Some("2"), language = Some("nb") ) - ) + ): @unchecked results.totalCount should be(1) results.results.head.id should be(2) } @@ -336,7 +336,7 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit query = Some("and"), language = Some("nb") ) - ) + ): @unchecked results.totalCount should be(1) results.results.head.id should be(4) } @@ -347,7 +347,7 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit query = Some("batmen"), language = Some("nb") ) - ) + ): @unchecked results.totalCount should be(0) } @@ -358,7 +358,7 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit language = Some("nb"), license = Some(License.Copyrighted.toString) ) - ) + ): @unchecked results.totalCount should be(1) results.results.head.id should be(1) } @@ -369,7 +369,7 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit query = Some("bilde + bil"), language = Some("nb") ) - ) + ): @unchecked search1.results.map(_.id) should equal(Seq.empty) val Success(search2) = audioSearchService.matchingQuery( @@ -377,13 +377,13 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit query = Some("ute + -går"), language = Some("nb") ) - ) + ): @unchecked search2.results.map(_.id) should equal(Seq(3)) } test("That searching for all languages and specifying no language should return the same") { - val Success(results1) = audioSearchService.matchingQuery(searchSettings.copy(language = Some("*"))) - val Success(results2) = audioSearchService.matchingQuery(searchSettings.copy(language = None)) + val Success(results1) = audioSearchService.matchingQuery(searchSettings.copy(language = Some("*"))): @unchecked + val Success(results2) = audioSearchService.matchingQuery(searchSettings.copy(language = None)): @unchecked results1.totalCount should be(results2.totalCount) results1.results.head should be(results2.results.head) @@ -392,12 +392,12 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit } test("That searching for 'nb' should return all results") { - val Success(results) = audioSearchService.matchingQuery(searchSettings.copy(language = Some("nb"))) + val Success(results) = audioSearchService.matchingQuery(searchSettings.copy(language = Some("nb"))): @unchecked results.totalCount should be(5) } test("That searching for 'en' should only return results with english title") { - val Success(result) = audioSearchService.matchingQuery(searchSettings.copy(language = Some("en"))) + val Success(result) = audioSearchService.matchingQuery(searchSettings.copy(language = Some("en"))): @unchecked result.totalCount should be(2) result.language should be("en") @@ -409,7 +409,7 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit } test("That searching for language not in predefined list should work") { - val Success(result) = audioSearchService.matchingQuery(searchSettings.copy(language = Some("ukr"))) + val Success(result) = audioSearchService.matchingQuery(searchSettings.copy(language = Some("ukr"))): @unchecked result.totalCount should be(1) result.language should be("ukr") @@ -418,14 +418,15 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit } test("That searching for language not in indexed data should not fail") { - val Success(result) = audioSearchService.matchingQuery(searchSettings.copy(language = Some("ait"))) // Arikem + val Success(result) = + audioSearchService.matchingQuery(searchSettings.copy(language = Some("ait"))): @unchecked // Arikem result.totalCount should be(0) result.language should be("ait") } test("That 'supported languages' should match all possible languages") { - val Success(result1) = audioSearchService.matchingQuery(searchSettings.copy(language = Some("en"))) - val Success(result2) = audioSearchService.matchingQuery(searchSettings.copy(language = Some("nb"))) + val Success(result1) = audioSearchService.matchingQuery(searchSettings.copy(language = Some("en"))): @unchecked + val Success(result2) = audioSearchService.matchingQuery(searchSettings.copy(language = Some("nb"))): @unchecked // 'Donald' with 'en', 'nb' and 'nn' result1.results.head.supportedLanguages should be(audio4.titles.map(_.language)) @@ -453,8 +454,10 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit } test("That hit is returned in the matched language") { - val Success(searchResultEn) = audioSearchService.matchingQuery(searchSettings.copy(query = Some("Unrelated"))) - val Success(searchResultNb) = audioSearchService.matchingQuery(searchSettings.copy(query = Some("Urelatert"))) + val Success(searchResultEn) = + audioSearchService.matchingQuery(searchSettings.copy(query = Some("Unrelated"))): @unchecked + val Success(searchResultNb) = + audioSearchService.matchingQuery(searchSettings.copy(query = Some("Urelatert"))): @unchecked searchResultNb.totalCount should be(1) searchResultNb.results.head.title.language should be("nb") @@ -466,35 +469,37 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit } test("That sorting by lastUpdated asc functions correctly") { - val Success(search) = audioSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByLastUpdatedAsc)) + val Success(search) = + audioSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByLastUpdatedAsc)): @unchecked search.totalCount should be(6) search.results.map(_.id) should be(Seq(5, 3, 2, 4, 6, 7)) } test("That sorting by lastUpdated desc functions correctly") { - val Success(search) = audioSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByLastUpdatedDesc)) + val Success(search) = + audioSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByLastUpdatedDesc)): @unchecked search.totalCount should be(6) search.results.map(_.id) should be(Seq(6, 7, 4, 2, 3, 5)) } test("That sorting by id asc functions correctly") { - val Success(search) = audioSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByIdAsc)) + val Success(search) = audioSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByIdAsc)): @unchecked search.totalCount should be(6) search.results.map(_.id) should be(Seq(2, 3, 4, 5, 6, 7)) } test("That sorting by id desc functions correctly") { - val Success(search) = audioSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByIdDesc)) + val Success(search) = audioSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByIdDesc)): @unchecked search.totalCount should be(6) search.results.map(_.id) should be(Seq(7, 6, 5, 4, 3, 2)) } test("That supportedLanguages are sorted correctly") { - val Success(result) = audioSearchService.matchingQuery(searchSettings.copy(query = Some("Unrelated"))) + val Success(result) = audioSearchService.matchingQuery(searchSettings.copy(query = Some("Unrelated"))): @unchecked result.results.head.supportedLanguages should be(Seq("nb", "en")) } @@ -505,11 +510,11 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(initialSearch) = audioSearchService.matchingQuery( searchSettings.copy(pageSize = Some(pageSize), sort = Sort.ByIdAsc, shouldScroll = true) - ) + ): @unchecked - val Success(scroll1) = audioSearchService.scroll(initialSearch.scrollId.get, "*") - val Success(scroll2) = audioSearchService.scroll(scroll1.scrollId.get, "*") - val Success(scroll3) = audioSearchService.scroll(scroll2.scrollId.get, "*") + val Success(scroll1) = audioSearchService.scroll(initialSearch.scrollId.get, "*"): @unchecked + val Success(scroll2) = audioSearchService.scroll(scroll1.scrollId.get, "*"): @unchecked + val Success(scroll3) = audioSearchService.scroll(scroll2.scrollId.get, "*"): @unchecked initialSearch.results.map(_.id) should be(expectedIds.head) scroll1.results.map(_.id) should be(expectedIds(1)) @@ -518,18 +523,19 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit } test("That filtering for audio-type works as expected") { - val Success(search1) = audioSearchService.matchingQuery(searchSettings.copy(audioType = None)) + val Success(search1) = audioSearchService.matchingQuery(searchSettings.copy(audioType = None)): @unchecked search1.totalCount should be(6) search1.results.head.id should be(4) search1.results.last.id should be(6) - val Success(search2) = audioSearchService.matchingQuery(searchSettings.copy(audioType = Some(AudioType.Podcast))) + val Success(search2) = + audioSearchService.matchingQuery(searchSettings.copy(audioType = Some(AudioType.Podcast))): @unchecked search2.totalCount should be(2) search2.results.map(_.id) should be(Seq(7, 6)) } test("That searching matches manuscript") { - val Success(search1) = audioSearchService.matchingQuery(searchSettings.copy(query = Some("manuscript"))) + val Success(search1) = audioSearchService.matchingQuery(searchSettings.copy(query = Some("manuscript"))): @unchecked search1.totalCount should be(2) search1.results.map(_.id) should be(Seq(2, 5)) @@ -537,35 +543,38 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That filtering for episodes of series works as expected") { val Success(search1) = - audioSearchService.matchingQuery(searchSettings.copy(seriesFilter = Some(true), sort = Sort.ByIdAsc)) + audioSearchService.matchingQuery(searchSettings.copy(seriesFilter = Some(true), sort = Sort.ByIdAsc)): @unchecked search1.totalCount should be(1) search1.results.map(_.id) should be(Seq(6)) val Success(search2) = - audioSearchService.matchingQuery(searchSettings.copy(seriesFilter = Some(false), sort = Sort.ByIdAsc)) + audioSearchService.matchingQuery(searchSettings.copy(seriesFilter = Some(false), sort = Sort.ByIdAsc)): @unchecked search2.totalCount should be(5) search2.results.map(_.id) should be(Seq(2, 3, 4, 5, 7)) val Success(search3) = - audioSearchService.matchingQuery(searchSettings.copy(seriesFilter = None, sort = Sort.ByIdAsc)) + audioSearchService.matchingQuery(searchSettings.copy(seriesFilter = None, sort = Sort.ByIdAsc)): @unchecked search3.totalCount should be(6) search3.results.map(_.id) should be(Seq(2, 3, 4, 5, 6, 7)) } test("That searching for podcast meta introductions works") { - val Success(search1) = audioSearchService.matchingQuery(searchSettings.copy(query = Some("podcastintroritehere"))) + val Success(search1) = + audioSearchService.matchingQuery(searchSettings.copy(query = Some("podcastintroritehere"))): @unchecked search1.totalCount should be(1) search1.results.map(_.id) should be(Seq(6)) val Success(search2) = - audioSearchService.matchingQuery(searchSettings.copy(query = Some("podcastintroritehere"), language = Some("en"))) + audioSearchService.matchingQuery( + searchSettings.copy(query = Some("podcastintroritehere"), language = Some("en")) + ): @unchecked search2.totalCount should be(0) search2.results.map(_.id) should be(Seq()) } test("That search result includes updatedBy field") { val Success(searchResult) = - audioSearchService.matchingQuery(searchSettings.copy(query = Some("Pingvinen"))) + audioSearchService.matchingQuery(searchSettings.copy(query = Some("Pingvinen"))): @unchecked searchResult.totalCount should be(1) searchResult.results.size should be(1) searchResult.results.head.lastUpdated should be(updated4) @@ -580,7 +589,7 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit language = Some("en"), sort = Sort.ByIdAsc ) - ) + ): @unchecked result1.results.map(_.id) should be(Seq(2, 3, 4, 5, 6, 7)) result1.results.head.title.language should be("nb") result1.results(1).title.language should be("nb") @@ -596,7 +605,7 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit language = Some("nb"), sort = Sort.ByIdAsc ) - ) + ): @unchecked result2.results.map(_.id) should be(Seq(2, 3, 4, 5, 6, 7)) result2.results.head.title.language should be("nb") result2.results(1).title.language should be("nb") @@ -614,7 +623,7 @@ class AudioSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit language = Some("nb"), sort = Sort.ByIdAsc ) - ) + ): @unchecked result1.results.map(_.id) should be(Seq(4)) } diff --git a/audio-api/src/test/scala/no/ndla/audioapi/service/search/SearchConverterServiceTest.scala b/audio-api/src/test/scala/no/ndla/audioapi/service/search/SearchConverterServiceTest.scala index cf9841f5d5..7da6a72ef4 100644 --- a/audio-api/src/test/scala/no/ndla/audioapi/service/search/SearchConverterServiceTest.scala +++ b/audio-api/src/test/scala/no/ndla/audioapi/service/search/SearchConverterServiceTest.scala @@ -20,7 +20,7 @@ import no.ndla.search.model.{SearchableLanguageList, SearchableLanguageValues} class SearchConverterServiceTest extends UnitSuite with TestEnvironment { - override val searchConverterService = new SearchConverterService + override lazy val searchConverterService = new SearchConverterService val byNcSa: Copyright = Copyright( diff --git a/audio-api/src/test/scala/no/ndla/audioapi/service/search/SeriesSearchServiceTest.scala b/audio-api/src/test/scala/no/ndla/audioapi/service/search/SeriesSearchServiceTest.scala index 6de5e6219a..913d6a3b1d 100644 --- a/audio-api/src/test/scala/no/ndla/audioapi/service/search/SeriesSearchServiceTest.scala +++ b/audio-api/src/test/scala/no/ndla/audioapi/service/search/SeriesSearchServiceTest.scala @@ -14,17 +14,15 @@ import no.ndla.audioapi.{TestData, TestEnvironment, UnitSuite} import no.ndla.common.model.domain as common import no.ndla.scalatestsuite.ElasticsearchIntegrationSuite -import scala.util.Success - class SeriesSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite with TestEnvironment { e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("http://localhost:9200")) - override val seriesSearchService = new SeriesSearchService - override val seriesIndexService: SeriesIndexService = new SeriesIndexService { + override lazy val seriesSearchService = new SeriesSearchService + override lazy val seriesIndexService: SeriesIndexService = new SeriesIndexService { override val indexShards = 1 } - override val searchConverterService = new SearchConverterService - override val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService val seriesToIndex: Seq[Series] = Seq( TestData.SampleSeries.copy( @@ -72,28 +70,27 @@ class SeriesSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSui test("That query search works as expected") { indexAndWait(seriesToIndex) - val Success(result1) = seriesSearchService.matchingQuery(settings.copy(query = Some("tiger"))) + val result1 = seriesSearchService.matchingQuery(settings.copy(query = Some("tiger"))).get result1.results.map(_.id) should be(Seq(2)) - val Success(result2) = seriesSearchService.matchingQuery(settings.copy(query = Some("Lyd med"))) + val result2 = seriesSearchService.matchingQuery(settings.copy(query = Some("Lyd med"))).get result2.results.map(_.id) should be(Seq(1, 2, 3)) - val Success(result3) = seriesSearchService.matchingQuery(settings.copy(query = Some("epler"))) + val result3 = seriesSearchService.matchingQuery(settings.copy(query = Some("epler"))).get result3.results.map(_.id) should be(Seq(1)) - val Success(result4) = - seriesSearchService.matchingQuery(settings.copy(query = Some("mixtepec"), language = Some("mix"))) + val result4 = seriesSearchService.matchingQuery(settings.copy(query = Some("mixtepec"), language = Some("mix"))).get result4.results.map(_.id) should be(Seq(3)) } test("That descriptions are searchable") { indexAndWait(seriesToIndex) - val Success(result1) = seriesSearchService.matchingQuery(settings.copy(query = Some("megabeskrivelse"))) + val result1 = seriesSearchService.matchingQuery(settings.copy(query = Some("megabeskrivelse"))).get result1.results.map(_.id) should be(Seq(1)) - val Success(result2) = - seriesSearchService.matchingQuery(settings.copy(query = Some("description"), language = Some("en"))) + val result2 = + seriesSearchService.matchingQuery(settings.copy(query = Some("description"), language = Some("en"))).get result2.results.map(_.id) should be(Seq(1)) } @@ -118,27 +115,31 @@ class SeriesSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSui ) indexAndWait(seriesToIndex) - val Success(result1) = seriesSearchService.matchingQuery( - settings.copy( - query = None, - fallback = true, - language = Some("nb"), - sort = Sort.ByIdAsc + val result1 = seriesSearchService + .matchingQuery( + settings.copy( + query = None, + fallback = true, + language = Some("nb"), + sort = Sort.ByIdAsc + ) ) - ) + .get result1.results.length should be(seriesToIndex.length) result1.results.map(_.id) should be(Seq(1, 2, 3)) result1.results.head.title.language should be("nb") result1.results.last.title.language should be("mix") - val Success(result2) = seriesSearchService.matchingQuery( - settings.copy( - query = None, - fallback = true, - language = Some("en"), - sort = Sort.ByIdAsc + val result2 = seriesSearchService + .matchingQuery( + settings.copy( + query = None, + fallback = true, + language = Some("en"), + sort = Sort.ByIdAsc + ) ) - ) + .get result2.results.length should be(seriesToIndex.length) result2.results.map(_.id) should be(Seq(1, 2, 3)) result2.results.head.title.language should be("en") diff --git a/audio-api/src/test/scala/no/ndla/audioapi/service/search/TagIndexServiceTest.scala b/audio-api/src/test/scala/no/ndla/audioapi/service/search/TagIndexServiceTest.scala index ee27a77567..ceac71cdfd 100644 --- a/audio-api/src/test/scala/no/ndla/audioapi/service/search/TagIndexServiceTest.scala +++ b/audio-api/src/test/scala/no/ndla/audioapi/service/search/TagIndexServiceTest.scala @@ -15,11 +15,11 @@ class TagIndexServiceTest extends ElasticsearchIntegrationSuite with TestEnviron e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("http://localhost:9200")) - override val tagIndexService: TagIndexService = new TagIndexService { + override lazy val tagIndexService: TagIndexService = new TagIndexService { override val indexShards = 1 } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService test("That indexing does not fail if no tags are present") { tagIndexService.createIndexAndAlias() diff --git a/audio-api/src/test/scala/no/ndla/audioapi/service/search/TagSearchServiceTest.scala b/audio-api/src/test/scala/no/ndla/audioapi/service/search/TagSearchServiceTest.scala index 236c82f8a3..7a48ea5653 100644 --- a/audio-api/src/test/scala/no/ndla/audioapi/service/search/TagSearchServiceTest.scala +++ b/audio-api/src/test/scala/no/ndla/audioapi/service/search/TagSearchServiceTest.scala @@ -19,12 +19,12 @@ class TagSearchServiceTest extends ElasticsearchIntegrationSuite with TestEnviro e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("http://localhost:9200")) - override val tagSearchService = new TagSearchService - override val tagIndexService: TagIndexService = new TagIndexService { + override lazy val tagSearchService = new TagSearchService + override lazy val tagIndexService: TagIndexService = new TagIndexService { override val indexShards = 1 } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService val audio1: AudioMetaInformation = TestData.sampleAudio.copy( tags = Seq( @@ -82,14 +82,14 @@ class TagSearchServiceTest extends ElasticsearchIntegrationSuite with TestEnviro } test("That searching for tags returns sensible results") { - val Success(result) = tagSearchService.matchingQuery("test", "nb", 1, 100) + val Success(result) = tagSearchService.matchingQuery("test", "nb", 1, 100): @unchecked result.totalCount should be(3) result.results should be(Seq("test", "testemer", "testing")) } test("That only prefixes are matched") { - val Success(result) = tagSearchService.matchingQuery("kylling", "nb", 1, 100) + val Success(result) = tagSearchService.matchingQuery("kylling", "nb", 1, 100): @unchecked result.totalCount should be(1) result.results should be(Seq("kyllingfilet")) diff --git a/build.mill b/build.mill index 7acc892cba..16f261e231 100644 --- a/build.mill +++ b/build.mill @@ -1,4 +1,4 @@ -//| mill-version: 1.0.2 +//| mill-version: 1.0.3 //| mvnDeps: [ //| "com.lihaoyi::mill-contrib-docker:$MILL_VERSION", //| "io.circe::circe-generic:0.14.9", diff --git a/common/src/main/scala-3/no/ndla/common/implicits/package.scala b/common/src/main/scala-3/no/ndla/common/implicits/package.scala index 21c3991f22..ade9375d6e 100644 --- a/common/src/main/scala-3/no/ndla/common/implicits/package.scala +++ b/common/src/main/scala-3/no/ndla/common/implicits/package.scala @@ -3,6 +3,7 @@ * Copyright (C) 2024 NDLA * * See LICENSE + * */ package no.ndla.common @@ -11,7 +12,7 @@ import io.circe.DecodingFailure.Reason import io.circe.syntax.EncoderOps import io.circe.{Decoder, DecodingFailure, Encoder} -import scala.reflect.ClassTag +import scala.annotation.unused import scala.util.{Failure, Success, Try} package object implicits { @@ -39,7 +40,7 @@ package object implicits { case class ControlFlowException(returnValue: Throwable) extends RuntimeException() - def permitTry[A: ClassTag](f: PermittedTryContext ?=> Try[A]): Try[A] = { + def permitTry[A](f: PermittedTryContext ?=> Try[A]): Try[A] = { try { f(using PermittedTryContext()) } catch { @@ -48,17 +49,39 @@ package object implicits { } } - implicit class ctxctx[A: ClassTag](self: Try[A]) { - def ?(using PermittedTryContext): A = { + implicit class ctxctx[A](self: Try[A]) { + def ?(using + @unused( + "This parameter is only to make sure we dont throw exceptions outside of a caught context" + ) ctx: PermittedTryContext + ): A = { self match { case Failure(ex) => throw ControlFlowException(ex) case Success(value) => value } } + + def ??(using + @unused( + "This parameter is only to make sure we dont throw exceptions outside of a caught context" + ) ctx: PermittedTryContext + ): Unit = { + self match { + case Failure(ex) => throw ControlFlowException(ex) + case Success(_) => () + } + } } extension [T](opt: Option[T]) { - def toTry(throwable: Throwable): Try[T] = Failure(throwable) + def toTry(throwable: Throwable): Try[T] = opt match { + case Some(value) => Success(value) + case None => Failure(throwable) + } + } + + extension [T](t: Try[T]) { + def unit: Try[Unit] = t.map(_ => ()) } implicit class StringOption(private val self: Option[String]) { @@ -73,7 +96,7 @@ package object implicits { implicit def eitherDecoder[A: Decoder, B: Decoder]: Decoder[Either[A, B]] = Decoder.instance { c => c.value.as[B] match { case Right(value) => Right(Right(value)) - case Left(_) => + case Left(_) => c.value.as[A] match { case Right(value) => Right(Left(value)) case Left(_) => Left(DecodingFailure(Reason.CustomReason(s"Could not match ${c.value} to Either type"), c)) diff --git a/common/src/main/scala/no/ndla/common/CirceUtil.scala b/common/src/main/scala/no/ndla/common/CirceUtil.scala index 5b8de2b29f..b5d796badb 100644 --- a/common/src/main/scala/no/ndla/common/CirceUtil.scala +++ b/common/src/main/scala/no/ndla/common/CirceUtil.scala @@ -10,11 +10,7 @@ package no.ndla.common import enumeratum.* import io.circe.* -import io.circe.generic.semiauto.deriveEncoder import io.circe.syntax.* -import io.circe.generic.encoding.DerivedAsObjectEncoder -import io.circe.{Decoder, Encoder} -import shapeless.Lazy import scala.util.{Failure, Try} @@ -52,14 +48,11 @@ object CirceUtil { json.mapObject(_.add("typename", Json.fromString(clazz.getSimpleName))) } - def deriveEncoderWithTypename[T](implicit encode: Lazy[DerivedAsObjectEncoder[T]]): Encoder[T] = { - val encoder = deriveEncoder[T] + inline def deriveEncoderWithTypename[T](using encoder: Encoder[T]): Encoder[T] = Encoder.instance[T] { value => - val json = encoder(value) - + val json = encoder.apply(value) addTypenameDiscriminator(json, value.getClass) } - } private val stringDecoder = implicitly[Decoder[String]] diff --git a/common/src/main/scala/no/ndla/common/Clock.scala b/common/src/main/scala/no/ndla/common/Clock.scala index bbc8dbc74d..5cf4675ab2 100644 --- a/common/src/main/scala/no/ndla/common/Clock.scala +++ b/common/src/main/scala/no/ndla/common/Clock.scala @@ -11,7 +11,7 @@ package no.ndla.common import no.ndla.common.model.NDLADate trait Clock { - val clock: SystemClock + lazy val clock: SystemClock class SystemClock { diff --git a/common/src/main/scala/no/ndla/common/UUIDUtil.scala b/common/src/main/scala/no/ndla/common/UUIDUtil.scala index 35f391314e..37970cd80d 100644 --- a/common/src/main/scala/no/ndla/common/UUIDUtil.scala +++ b/common/src/main/scala/no/ndla/common/UUIDUtil.scala @@ -11,7 +11,7 @@ package no.ndla.common import java.util.UUID trait UUIDUtil { - val uuidUtil: UUIDUtil + lazy val uuidUtil: UUIDUtil class UUIDUtil { def randomUUID(): UUID = { diff --git a/common/src/main/scala/no/ndla/common/aws/NdlaAWSTranscribeClient.scala b/common/src/main/scala/no/ndla/common/aws/NdlaAWSTranscribeClient.scala index 8b1f717808..9b94b025cd 100644 --- a/common/src/main/scala/no/ndla/common/aws/NdlaAWSTranscribeClient.scala +++ b/common/src/main/scala/no/ndla/common/aws/NdlaAWSTranscribeClient.scala @@ -14,7 +14,7 @@ import software.amazon.awssdk.services.transcribe.{TranscribeClient, TranscribeC import scala.util.{Failure, Try} trait NdlaAWSTranscribeClient { - val transcribeClient: NdlaAWSTranscribeClient + lazy val transcribeClient: NdlaAWSTranscribeClient class NdlaAWSTranscribeClient(region: Option[String]) { @@ -52,16 +52,16 @@ trait NdlaAWSTranscribeClient { .build() ) - if (includeSubtitles) { + val toBuild = if (includeSubtitles) { requestBuilder.subtitles( Subtitles .builder() .formats(SubtitleFormat.valueOf(outputSubtitleFormat)) .build() ) - } + } else { requestBuilder } - client.startTranscriptionJob(requestBuilder.build()) + client.startTranscriptionJob(toBuild.build()) } def getTranscriptionJob(jobName: String): Try[GetTranscriptionJobResponse] = { diff --git a/common/src/main/scala/no/ndla/common/aws/NdlaS3Client.scala b/common/src/main/scala/no/ndla/common/aws/NdlaS3Client.scala index b677e349b1..6fded0ed89 100644 --- a/common/src/main/scala/no/ndla/common/aws/NdlaS3Client.scala +++ b/common/src/main/scala/no/ndla/common/aws/NdlaS3Client.scala @@ -17,7 +17,7 @@ import software.amazon.awssdk.services.s3.{S3Client, S3ClientBuilder} import scala.util.Try trait NdlaS3Client { - val s3Client: NdlaS3Client + lazy val s3Client: NdlaS3Client class NdlaS3Client(bucket: String, region: Option[String]) { @@ -82,7 +82,7 @@ trait NdlaS3Client { ) } - def updateMetadata(key: String, metadata: java.util.Map[String, String]): Try[_] = Try { + def updateMetadata(key: String, metadata: java.util.Map[String, String]): Try[?] = Try { val cor = CopyObjectRequest .builder() diff --git a/common/src/main/scala/no/ndla/common/brightcove/NdlaBrightcoveClient.scala b/common/src/main/scala/no/ndla/common/brightcove/NdlaBrightcoveClient.scala index 1888382a2b..5ff84afcdd 100644 --- a/common/src/main/scala/no/ndla/common/brightcove/NdlaBrightcoveClient.scala +++ b/common/src/main/scala/no/ndla/common/brightcove/NdlaBrightcoveClient.scala @@ -8,8 +8,8 @@ package no.ndla.common.brightcove -import io.circe.Json -import io.circe.generic.codec.DerivedAsObjectCodec.deriveCodec +import io.circe.generic.semiauto.deriveDecoder +import io.circe.{Decoder, Json} import io.circe.parser.* import sttp.client3.{HttpClientSyncBackend, UriContext, basicRequest} import no.ndla.common.configuration.HasBaseProps @@ -24,9 +24,13 @@ import scala.util.{Failure, Success, Try} case class TokenResponse(access_token: String, token_type: String, expires_in: Int) +object TokenResponse { + implicit def decoder: Decoder[TokenResponse] = deriveDecoder[TokenResponse] +} + trait NdlaBrightcoveClient { this: HasBaseProps => - val brightcoveClient: NdlaBrightcoveClient + lazy val brightcoveClient: NdlaBrightcoveClient class NdlaBrightcoveClient { private val backend = HttpClientSyncBackend() diff --git a/common/src/main/scala/no/ndla/common/configuration/BaseComponentRegistry.scala b/common/src/main/scala/no/ndla/common/configuration/BaseComponentRegistry.scala index 36cbf29ea7..96086500b6 100644 --- a/common/src/main/scala/no/ndla/common/configuration/BaseComponentRegistry.scala +++ b/common/src/main/scala/no/ndla/common/configuration/BaseComponentRegistry.scala @@ -11,6 +11,6 @@ package no.ndla.common.configuration import no.ndla.common.Warmup trait BaseComponentRegistry[PropType <: BaseProps] { - val props: PropType - val healthController: Warmup + lazy val props: PropType + lazy val healthController: Warmup } diff --git a/common/src/main/scala/no/ndla/common/configuration/BaseProps.scala b/common/src/main/scala/no/ndla/common/configuration/BaseProps.scala index 70d8acf971..78f02d2639 100644 --- a/common/src/main/scala/no/ndla/common/configuration/BaseProps.scala +++ b/common/src/main/scala/no/ndla/common/configuration/BaseProps.scala @@ -50,8 +50,9 @@ trait BaseProps extends StrictLogging { val prop = Prop.successful[T](key, value) loadedProps.get(key) match { case Some(existing: Prop[T] @unchecked) => existing.setValue(value) - case Some(_) => throw new RuntimeException(s"Bad type when updating prop $key") - case None => loadedProps.put(key, prop): Unit + case Some(_) => + throw new RuntimeException("Prop with key " + key + " exists with a different type, this is a super weird bug.") + case None => loadedProps.put(key, prop): Unit } prop } diff --git a/common/src/main/scala/no/ndla/common/configuration/HasBaseProps.scala b/common/src/main/scala/no/ndla/common/configuration/HasBaseProps.scala index 3bb0e8fb5b..4a2b6ad9ed 100644 --- a/common/src/main/scala/no/ndla/common/configuration/HasBaseProps.scala +++ b/common/src/main/scala/no/ndla/common/configuration/HasBaseProps.scala @@ -8,4 +8,4 @@ package no.ndla.common.configuration -trait HasBaseProps { val props: BaseProps } +trait HasBaseProps { lazy val props: BaseProps } diff --git a/common/src/main/scala/no/ndla/common/model/api/AuthorDTO.scala b/common/src/main/scala/no/ndla/common/model/api/AuthorDTO.scala index eb994141e6..fe5ca4ae2f 100644 --- a/common/src/main/scala/no/ndla/common/model/api/AuthorDTO.scala +++ b/common/src/main/scala/no/ndla/common/model/api/AuthorDTO.scala @@ -12,6 +12,7 @@ import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} import no.ndla.common.model.domain import no.ndla.common.model.domain.ContributorType +import sttp.tapir.Schema import sttp.tapir.Schema.annotations.description @description("Information about an author") @@ -30,4 +31,5 @@ case class AuthorDTO( object AuthorDTO { implicit def encoder: Encoder[AuthorDTO] = deriveEncoder[AuthorDTO] implicit def decoder: Decoder[AuthorDTO] = deriveDecoder[AuthorDTO] + implicit def schema: Schema[AuthorDTO] = Schema.derived[AuthorDTO] } diff --git a/common/src/main/scala/no/ndla/common/model/api/CommentDTO.scala b/common/src/main/scala/no/ndla/common/model/api/CommentDTO.scala index efb8fd117c..509ed49e76 100644 --- a/common/src/main/scala/no/ndla/common/model/api/CommentDTO.scala +++ b/common/src/main/scala/no/ndla/common/model/api/CommentDTO.scala @@ -24,6 +24,7 @@ case class CommentDTO( ) object CommentDTO { - implicit def encoder: Encoder[CommentDTO] = deriveEncoder - implicit def decoder: Decoder[CommentDTO] = deriveDecoder + implicit def encoder: Encoder[CommentDTO] = deriveEncoder + implicit def decoder: Decoder[CommentDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[CommentDTO] = sttp.tapir.Schema.derived[CommentDTO] } diff --git a/common/src/main/scala/no/ndla/common/model/api/DisclaimerDTO.scala b/common/src/main/scala/no/ndla/common/model/api/DisclaimerDTO.scala index 60ef2514a0..d1b1337026 100644 --- a/common/src/main/scala/no/ndla/common/model/api/DisclaimerDTO.scala +++ b/common/src/main/scala/no/ndla/common/model/api/DisclaimerDTO.scala @@ -21,6 +21,7 @@ case class DisclaimerDTO( object DisclaimerDTO { def fromLanguageValue(lv: WithLanguageAndValue[String]): DisclaimerDTO = DisclaimerDTO(lv.value, lv.language) - implicit def encoder: Encoder[DisclaimerDTO] = deriveEncoder - implicit def decoder: Decoder[DisclaimerDTO] = deriveDecoder + implicit def encoder: Encoder[DisclaimerDTO] = deriveEncoder + implicit def decoder: Decoder[DisclaimerDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[DisclaimerDTO] = sttp.tapir.Schema.derived[DisclaimerDTO] } diff --git a/common/src/main/scala/no/ndla/common/model/api/DraftCopyrightDTO.scala b/common/src/main/scala/no/ndla/common/model/api/DraftCopyrightDTO.scala index 5b745ad4f7..75f6ebbf3b 100644 --- a/common/src/main/scala/no/ndla/common/model/api/DraftCopyrightDTO.scala +++ b/common/src/main/scala/no/ndla/common/model/api/DraftCopyrightDTO.scala @@ -11,6 +11,7 @@ package no.ndla.common.model.api import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} import no.ndla.common.model.NDLADate +import sttp.tapir.Schema import sttp.tapir.Schema.annotations.description @description("Description of copyright information") @@ -28,4 +29,5 @@ case class DraftCopyrightDTO( object DraftCopyrightDTO { implicit def encoder: Encoder[DraftCopyrightDTO] = deriveEncoder[DraftCopyrightDTO] implicit def decoder: Decoder[DraftCopyrightDTO] = deriveDecoder[DraftCopyrightDTO] + implicit def schema: Schema[DraftCopyrightDTO] = Schema.derived[DraftCopyrightDTO] } diff --git a/common/src/main/scala/no/ndla/common/model/api/FrontPageDTO.scala b/common/src/main/scala/no/ndla/common/model/api/FrontPageDTO.scala index 0299693143..f8497f7bc6 100644 --- a/common/src/main/scala/no/ndla/common/model/api/FrontPageDTO.scala +++ b/common/src/main/scala/no/ndla/common/model/api/FrontPageDTO.scala @@ -13,6 +13,10 @@ import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.syntax.EncoderOps import io.circe.{Decoder, Encoder} import sttp.tapir.Schema.annotations.description +import sttp.tapir.Schema +import sttp.tapir.SchemaType.SProduct +import sttp.tapir.FieldName +import sttp.tapir.SchemaType @description("The Menu object") case class MenuDTO( @@ -38,6 +42,13 @@ object MenuDTO { implicit val encodeMenuData: Encoder[MenuDataDTO] = Encoder.instance { case menu: MenuDTO => menu.asJson } implicit val decodeMenuData: Decoder[MenuDataDTO] = Decoder[MenuDTO].widen + + import sttp.tapir.generic.auto.* + implicit def schema: Schema[MenuDTO] = Schema.derivedSchema } sealed trait MenuDataDTO {} + +object MenuDataDTO { + implicit def schema: Schema[MenuDataDTO] = MenuDTO.schema.as +} diff --git a/common/src/main/scala/no/ndla/common/model/api/LicenseDTO.scala b/common/src/main/scala/no/ndla/common/model/api/LicenseDTO.scala index d265e4df78..90f76fc78e 100644 --- a/common/src/main/scala/no/ndla/common/model/api/LicenseDTO.scala +++ b/common/src/main/scala/no/ndla/common/model/api/LicenseDTO.scala @@ -20,6 +20,7 @@ case class LicenseDTO( ) object LicenseDTO { - implicit def encoder: Encoder[LicenseDTO] = deriveEncoder[LicenseDTO] - implicit def decoder: Decoder[LicenseDTO] = deriveDecoder[LicenseDTO] + implicit def encoder: Encoder[LicenseDTO] = deriveEncoder[LicenseDTO] + implicit def decoder: Decoder[LicenseDTO] = deriveDecoder[LicenseDTO] + implicit def schema: sttp.tapir.Schema[LicenseDTO] = sttp.tapir.Schema.derived } diff --git a/common/src/main/scala/no/ndla/common/model/api/NewCommentDTO.scala b/common/src/main/scala/no/ndla/common/model/api/NewCommentDTO.scala index c3ed27898d..3d50e2afca 100644 --- a/common/src/main/scala/no/ndla/common/model/api/NewCommentDTO.scala +++ b/common/src/main/scala/no/ndla/common/model/api/NewCommentDTO.scala @@ -10,6 +10,7 @@ package no.ndla.common.model.api import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} +import sttp.tapir.Schema import sttp.tapir.Schema.annotations.description @description("Information about a comment attached to an article") @@ -21,4 +22,5 @@ case class NewCommentDTO( object NewCommentDTO { implicit def encoder: Encoder[NewCommentDTO] = deriveEncoder implicit def decoder: Decoder[NewCommentDTO] = deriveDecoder + implicit def schema: Schema[NewCommentDTO] = Schema.derived } diff --git a/common/src/main/scala/no/ndla/common/model/api/RelatedContentLinkDTO.scala b/common/src/main/scala/no/ndla/common/model/api/RelatedContentLinkDTO.scala index cb3b2d3bd2..a76e465a6a 100644 --- a/common/src/main/scala/no/ndla/common/model/api/RelatedContentLinkDTO.scala +++ b/common/src/main/scala/no/ndla/common/model/api/RelatedContentLinkDTO.scala @@ -10,6 +10,7 @@ package no.ndla.common.model.api import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} +import sttp.tapir.Schema import sttp.tapir.Schema.annotations.description @description("External link related to the article") @@ -21,4 +22,5 @@ case class RelatedContentLinkDTO( object RelatedContentLinkDTO { implicit def encoder: Encoder[RelatedContentLinkDTO] = deriveEncoder implicit def decoder: Decoder[RelatedContentLinkDTO] = deriveDecoder + implicit def schema: Schema[RelatedContentLinkDTO] = Schema.derived } diff --git a/common/src/main/scala/no/ndla/common/model/api/ResponsibleDTO.scala b/common/src/main/scala/no/ndla/common/model/api/ResponsibleDTO.scala index 03a84af8d1..d41d59f9ad 100644 --- a/common/src/main/scala/no/ndla/common/model/api/ResponsibleDTO.scala +++ b/common/src/main/scala/no/ndla/common/model/api/ResponsibleDTO.scala @@ -20,6 +20,7 @@ case class ResponsibleDTO( ) object ResponsibleDTO { - implicit def encoder: Encoder[ResponsibleDTO] = deriveEncoder - implicit def decoder: Decoder[ResponsibleDTO] = deriveDecoder + implicit def encoder: Encoder[ResponsibleDTO] = deriveEncoder + implicit def decoder: Decoder[ResponsibleDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[ResponsibleDTO] = sttp.tapir.Schema.derived[ResponsibleDTO] } diff --git a/common/src/main/scala/no/ndla/common/model/api/RevisionMetaDTO.scala b/common/src/main/scala/no/ndla/common/model/api/RevisionMetaDTO.scala index f3347f8763..0efe6a57bd 100644 --- a/common/src/main/scala/no/ndla/common/model/api/RevisionMetaDTO.scala +++ b/common/src/main/scala/no/ndla/common/model/api/RevisionMetaDTO.scala @@ -12,6 +12,7 @@ import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} import sttp.tapir.Schema.annotations.description import no.ndla.common.model.NDLADate +import sttp.tapir.Schema @description("Information about the editorial notes") case class RevisionMetaDTO( @@ -24,4 +25,5 @@ case class RevisionMetaDTO( object RevisionMetaDTO { implicit def encoder: Encoder[RevisionMetaDTO] = deriveEncoder[RevisionMetaDTO] implicit def decoder: Decoder[RevisionMetaDTO] = deriveDecoder[RevisionMetaDTO] + implicit def schema: Schema[RevisionMetaDTO] = Schema.derived[RevisionMetaDTO] } diff --git a/common/src/main/scala/no/ndla/common/model/api/UpdateOrDelete.scala b/common/src/main/scala/no/ndla/common/model/api/UpdateOrDelete.scala index fc51a017b4..93d954ef15 100644 --- a/common/src/main/scala/no/ndla/common/model/api/UpdateOrDelete.scala +++ b/common/src/main/scala/no/ndla/common/model/api/UpdateOrDelete.scala @@ -42,8 +42,8 @@ object UpdateOrDelete { } } - private[this] val marker: String = s"$$marker-${UUID.randomUUID()}-marker$$" - private[this] val markerJson: Json = Json.fromString(marker) + private val marker: String = s"$$marker-${UUID.randomUUID()}-marker$$" + private val markerJson: Json = Json.fromString(marker) implicit def encodeUpdateOrDelete[A](implicit encodeA: Encoder[A]): Encoder[UpdateOrDelete[A]] = Encoder.instance { case UpdateWith(a) => encodeA(a) diff --git a/common/src/main/scala/no/ndla/common/model/api/UpdatedCommentDTO.scala b/common/src/main/scala/no/ndla/common/model/api/UpdatedCommentDTO.scala index 40a3ccc7e8..179802457e 100644 --- a/common/src/main/scala/no/ndla/common/model/api/UpdatedCommentDTO.scala +++ b/common/src/main/scala/no/ndla/common/model/api/UpdatedCommentDTO.scala @@ -10,6 +10,7 @@ package no.ndla.common.model.api import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} +import sttp.tapir.Schema import sttp.tapir.Schema.annotations.description @description("Information about a comment attached to an article") @@ -23,4 +24,5 @@ case class UpdatedCommentDTO( object UpdatedCommentDTO { implicit def encoder: Encoder[UpdatedCommentDTO] = deriveEncoder implicit def decoder: Decoder[UpdatedCommentDTO] = deriveDecoder + implicit def schema: Schema[UpdatedCommentDTO] = Schema.derived[UpdatedCommentDTO] } diff --git a/common/src/main/scala/no/ndla/common/model/api/frontpage/SubjectPageDTO.scala b/common/src/main/scala/no/ndla/common/model/api/frontpage/SubjectPageDTO.scala index 9e4720293e..e384aa22ff 100644 --- a/common/src/main/scala/no/ndla/common/model/api/frontpage/SubjectPageDTO.scala +++ b/common/src/main/scala/no/ndla/common/model/api/frontpage/SubjectPageDTO.scala @@ -10,6 +10,7 @@ package no.ndla.common.model.api.frontpage import io.circe.* import io.circe.generic.semiauto.* +import sttp.tapir.Schema case class SubjectPageDTO( id: Long, @@ -27,4 +28,6 @@ case class SubjectPageDTO( object SubjectPageDTO { implicit def encoder: Encoder[SubjectPageDTO] = deriveEncoder[SubjectPageDTO] implicit def decoder: Decoder[SubjectPageDTO] = deriveDecoder[SubjectPageDTO] + import sttp.tapir.generic.auto.* + implicit def schema: Schema[SubjectPageDTO] = Schema.derivedSchema } diff --git a/common/src/main/scala/no/ndla/common/model/domain/ArticleType.scala b/common/src/main/scala/no/ndla/common/model/domain/ArticleType.scala index 69b5ca5a62..255bbc6db9 100644 --- a/common/src/main/scala/no/ndla/common/model/domain/ArticleType.scala +++ b/common/src/main/scala/no/ndla/common/model/domain/ArticleType.scala @@ -9,6 +9,9 @@ package no.ndla.common.model.domain import enumeratum.* +import sttp.tapir.Codec.PlainCodec +import sttp.tapir.Schema +import sttp.tapir.codec.enumeratum.* import no.ndla.common.errors.ValidationException sealed abstract class ArticleType(override val entryName: String) extends EnumEntry @@ -18,6 +21,9 @@ object ArticleType extends Enum[ArticleType] with CirceEnum[ArticleType] { case object TopicArticle extends ArticleType("topic-article") case object FrontpageArticle extends ArticleType("frontpage-article") + implicit def schema: Schema[ArticleType] = schemaForEnumEntry[ArticleType] + implicit def codec: PlainCodec[ArticleType] = plainCodecEnumEntry[ArticleType] + val values: IndexedSeq[ArticleType] = findValues def all: Seq[String] = ArticleType.values.map(_.entryName) diff --git a/common/src/main/scala/no/ndla/common/model/domain/Priority.scala b/common/src/main/scala/no/ndla/common/model/domain/Priority.scala index 83e4996557..0718da88e9 100644 --- a/common/src/main/scala/no/ndla/common/model/domain/Priority.scala +++ b/common/src/main/scala/no/ndla/common/model/domain/Priority.scala @@ -38,6 +38,6 @@ object Priority extends Enum[Priority] with CirceEnum[Priority] { ) } - implicit val schema: Schema[Priority] = schemaForEnumEntry[Priority] - implicit val codec: PlainCodec[Priority] = plainCodecEnumEntry[Priority] + implicit def schema: Schema[Priority] = schemaForEnumEntry[Priority] + implicit def codec: PlainCodec[Priority] = plainCodecEnumEntry[Priority] } diff --git a/common/src/main/scala/no/ndla/common/model/domain/article/PartialPublishArticleDTO.scala b/common/src/main/scala/no/ndla/common/model/domain/article/PartialPublishArticleDTO.scala index 01de2ce6fe..0309be2b7c 100644 --- a/common/src/main/scala/no/ndla/common/model/domain/article/PartialPublishArticleDTO.scala +++ b/common/src/main/scala/no/ndla/common/model/domain/article/PartialPublishArticleDTO.scala @@ -36,4 +36,6 @@ object PartialPublishArticleDTO { implicit def eitherDec: Decoder[Either[RelatedContentLinkDTO, Long]] = eitherDecoder[RelatedContentLinkDTO, Long] implicit val encoder: Encoder.AsObject[PartialPublishArticleDTO] = UpdateOrDelete.filterMarkers(deriveEncoder[PartialPublishArticleDTO]) implicit val decoder: Decoder[PartialPublishArticleDTO] = deriveDecoder[PartialPublishArticleDTO] + import sttp.tapir.generic.auto.* + implicit def schema: sttp.tapir.Schema[PartialPublishArticleDTO] = sttp.tapir.Schema.derivedSchema } diff --git a/common/src/main/scala/no/ndla/common/model/domain/article/PartialPublishArticlesBulkDTO.scala b/common/src/main/scala/no/ndla/common/model/domain/article/PartialPublishArticlesBulkDTO.scala index 01bad821c9..b18a16a130 100644 --- a/common/src/main/scala/no/ndla/common/model/domain/article/PartialPublishArticlesBulkDTO.scala +++ b/common/src/main/scala/no/ndla/common/model/domain/article/PartialPublishArticlesBulkDTO.scala @@ -22,6 +22,5 @@ case class PartialPublishArticlesBulkDTO( object PartialPublishArticlesBulkDTO { implicit val encoder: Encoder[PartialPublishArticlesBulkDTO] = deriveEncoder implicit val decoder: Decoder[PartialPublishArticlesBulkDTO] = deriveDecoder - - implicit val schema: Schema[PartialPublishArticlesBulkDTO] = Schema.any[PartialPublishArticlesBulkDTO] + implicit def schema: Schema[PartialPublishArticlesBulkDTO] = Schema.any[PartialPublishArticlesBulkDTO] } diff --git a/common/src/main/scala/no/ndla/common/model/domain/concept/Concept.scala b/common/src/main/scala/no/ndla/common/model/domain/concept/Concept.scala index fe37a1af4e..6847358cbb 100644 --- a/common/src/main/scala/no/ndla/common/model/domain/concept/Concept.scala +++ b/common/src/main/scala/no/ndla/common/model/domain/concept/Concept.scala @@ -14,6 +14,7 @@ import no.ndla.common.model.NDLADate import no.ndla.common.model.domain.draft.DraftCopyright import no.ndla.common.model.domain.{Content, Responsible, Tag, Title} import no.ndla.language.Language.getSupportedLanguages +import sttp.tapir.Schema case class Concept( id: Option[Long], @@ -39,4 +40,6 @@ case class Concept( object Concept { implicit val encoder: Encoder[Concept] = deriveEncoder implicit val decoder: Decoder[Concept] = deriveDecoder + import sttp.tapir.generic.auto.* + implicit def schema: Schema[Concept] = Schema.derivedSchema } diff --git a/common/src/main/scala/no/ndla/common/model/domain/draft/Draft.scala b/common/src/main/scala/no/ndla/common/model/domain/draft/Draft.scala index 8e124fb18c..6ce33360d8 100644 --- a/common/src/main/scala/no/ndla/common/model/domain/draft/Draft.scala +++ b/common/src/main/scala/no/ndla/common/model/domain/draft/Draft.scala @@ -69,4 +69,6 @@ object Draft { implicit def eitherDec: Decoder[Either[RelatedContentLink, Long]] = eitherDecoder[RelatedContentLink, Long] implicit val encoder: Encoder[Draft] = deriveEncoder implicit val decoder: Decoder[Draft] = deriveDecoder + import sttp.tapir.generic.auto.* + implicit def schema: sttp.tapir.Schema[Draft] = sttp.tapir.Schema.derivedSchema } diff --git a/common/src/main/scala/no/ndla/common/model/domain/frontpage/MovieTheme.scala b/common/src/main/scala/no/ndla/common/model/domain/frontpage/MovieTheme.scala index d7e2103e9e..5551837fbf 100644 --- a/common/src/main/scala/no/ndla/common/model/domain/frontpage/MovieTheme.scala +++ b/common/src/main/scala/no/ndla/common/model/domain/frontpage/MovieTheme.scala @@ -9,9 +9,19 @@ package no.ndla.common.model.domain.frontpage import no.ndla.language.model.LanguageField +import io.circe.{Encoder, Decoder} +import io.circe.generic.semiauto.{deriveEncoder, deriveDecoder} case class MovieTheme(name: Seq[MovieThemeName], movies: Seq[String]) +object MovieTheme { + implicit val encoder: Encoder[MovieTheme] = deriveEncoder + implicit val decoder: Decoder[MovieTheme] = deriveDecoder +} case class MovieThemeName(name: String, language: String) extends LanguageField[String] { override def value: String = name override def isEmpty: Boolean = name.isEmpty } +object MovieThemeName { + implicit val encoder: Encoder[MovieThemeName] = deriveEncoder + implicit val decoder: Decoder[MovieThemeName] = deriveDecoder +} diff --git a/common/src/main/scala/no/ndla/common/model/domain/learningpath/LearningPath.scala b/common/src/main/scala/no/ndla/common/model/domain/learningpath/LearningPath.scala index fda691ce70..e611f07ea1 100644 --- a/common/src/main/scala/no/ndla/common/model/domain/learningpath/LearningPath.scala +++ b/common/src/main/scala/no/ndla/common/model/domain/learningpath/LearningPath.scala @@ -14,6 +14,7 @@ import no.ndla.common.model.NDLADate import no.ndla.common.model.domain.{Comment, Content, Responsible, Tag, Title} import no.ndla.language.Language.getSupportedLanguages import no.ndla.common.model.domain.Priority +import sttp.tapir.Schema case class LearningPath( id: Option[Long], @@ -64,4 +65,7 @@ object LearningPath { if (learningsteps.succeeded) learningsteps.delete else obj } + + import sttp.tapir.generic.auto.* + implicit def schema: Schema[LearningPath] = Schema.derivedSchema } diff --git a/common/src/main/scala/no/ndla/common/model/domain/learningpath/LearningPathStatus.scala b/common/src/main/scala/no/ndla/common/model/domain/learningpath/LearningPathStatus.scala index 6ccbedb6f7..c6a49902dd 100644 --- a/common/src/main/scala/no/ndla/common/model/domain/learningpath/LearningPathStatus.scala +++ b/common/src/main/scala/no/ndla/common/model/domain/learningpath/LearningPathStatus.scala @@ -10,6 +10,9 @@ package no.ndla.common.model.domain.learningpath import enumeratum.* import no.ndla.common.errors.{ValidationException, ValidationMessage} +import sttp.tapir.Codec.PlainCodec +import sttp.tapir.Schema +import sttp.tapir.codec.enumeratum.* import scala.util.{Failure, Success, Try} @@ -23,6 +26,9 @@ object LearningPathStatus extends Enum[LearningPathStatus] with CirceEnum[ case object SUBMITTED extends LearningPathStatus case object READY_FOR_SHARING extends LearningPathStatus + implicit val schema: Schema[LearningPathStatus] = schemaForEnumEntry[LearningPathStatus] + implicit val codec: PlainCodec[LearningPathStatus] = plainCodecEnumEntry[LearningPathStatus] + override def values: IndexedSeq[LearningPathStatus] = findValues def valueOf(s: String): Option[LearningPathStatus] = { diff --git a/common/src/main/scala/no/ndla/common/model/domain/myndla/UserRole.scala b/common/src/main/scala/no/ndla/common/model/domain/myndla/UserRole.scala index 23798f60a5..29a1f51efe 100644 --- a/common/src/main/scala/no/ndla/common/model/domain/myndla/UserRole.scala +++ b/common/src/main/scala/no/ndla/common/model/domain/myndla/UserRole.scala @@ -9,7 +9,6 @@ package no.ndla.common.model.domain.myndla import enumeratum.* -import enumeratum.{CirceEnum, EnumEntry} import sttp.tapir.Schema import sttp.tapir.codec.enumeratum.* diff --git a/common/src/main/scala/no/ndla/common/model/domain/myndla/auth/AuthUtility.scala b/common/src/main/scala/no/ndla/common/model/domain/myndla/auth/AuthUtility.scala index 6ee942cd17..a658489e19 100644 --- a/common/src/main/scala/no/ndla/common/model/domain/myndla/auth/AuthUtility.scala +++ b/common/src/main/scala/no/ndla/common/model/domain/myndla/auth/AuthUtility.scala @@ -35,7 +35,7 @@ object AuthUtility { .requiredScopes(Seq.empty) EndpointInput.Auth( - input = sttp.tapir.header("FeideAuthorization")(feideTokenAuthCodec), + input = sttp.tapir.header("FeideAuthorization")(using feideTokenAuthCodec), challenge = WWWAuthenticateChallenge.bearer, authType = authType, info = AuthInfo.Empty.securitySchemeName("oauth2") diff --git a/common/src/test/scala/no/ndla/common/ContentURIUtilTest.scala b/common/src/test/scala/no/ndla/common/ContentURIUtilTest.scala index 24acf9bfeb..32af0773fb 100644 --- a/common/src/test/scala/no/ndla/common/ContentURIUtilTest.scala +++ b/common/src/test/scala/no/ndla/common/ContentURIUtilTest.scala @@ -21,10 +21,12 @@ class ContentURIUtilTest extends UnitTestSuiteBase { ContentURIUtil.parseArticleIdAndRevision("15") should be((Success(15), None)) ContentURIUtil.parseArticleIdAndRevision("15#100") should be((Success(15), Some(100))) - val (failed, Some(100)) = ContentURIUtil.parseArticleIdAndRevision("#100") - failed.isFailure should be(true) - val (failed2, None) = ContentURIUtil.parseArticleIdAndRevision("") + val (id1, rev1) = ContentURIUtil.parseArticleIdAndRevision("#100") + id1.isFailure should be(true) + rev1 should be(Some(100)) + val (failed2, rev2) = ContentURIUtil.parseArticleIdAndRevision("") failed2.isFailure should be(true) + rev2 should be(None) } test("That non-matching idString will fail and not throw exception") { diff --git a/common/src/test/scala/no/ndla/common/converter/CommonConverterTest.scala b/common/src/test/scala/no/ndla/common/converter/CommonConverterTest.scala index 989b9b9954..093873c62f 100644 --- a/common/src/test/scala/no/ndla/common/converter/CommonConverterTest.scala +++ b/common/src/test/scala/no/ndla/common/converter/CommonConverterTest.scala @@ -18,8 +18,8 @@ import org.mockito.Mockito.when import java.util.UUID class CommonConverterTest extends UnitTestSuiteBase with CommonConverter with Clock with UUIDUtil { - val clock: SystemClock = mock[SystemClock] - lazy val uuidUtil: UUIDUtil = mock[UUIDUtil] + override lazy val clock: SystemClock = mock[SystemClock] + override lazy val uuidUtil: UUIDUtil = mock[UUIDUtil] test("that mergeUpdatedCommentsWithExisting creates and updates comments correctly") { val uuid = UUID.randomUUID() val now = NDLADate.now() diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/ComponentRegistry.scala b/concept-api/src/main/scala/no/ndla/conceptapi/ComponentRegistry.scala index e79d4ff5c1..6cfc99ab73 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/ComponentRegistry.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/ComponentRegistry.scala @@ -61,34 +61,34 @@ class ComponentRegistry(properties: ConceptApiProperties) with DraftSearchSettingsHelper with SwaggerDocControllerConfig with ConceptControllerHelpers { - override val props: ConceptApiProperties = properties - override val migrator: DBMigrator = DBMigrator( + override lazy val props: ConceptApiProperties = properties + override lazy val migrator: DBMigrator = DBMigrator( new V23__SubjectNameAsTags(props), new V25__SubjectNameAsTagsPublished(props) ) override lazy val dataSource: HikariDataSource = DataSource.getHikariDataSource - lazy val draftConceptRepository = new DraftConceptRepository - lazy val publishedConceptRepository = new PublishedConceptRepository + override lazy val draftConceptRepository = new DraftConceptRepository + override lazy val publishedConceptRepository = new PublishedConceptRepository - lazy val draftConceptSearchService = new DraftConceptSearchService - lazy val searchConverterService = new SearchConverterService - lazy val draftConceptIndexService = new DraftConceptIndexService - lazy val publishedConceptIndexService = new PublishedConceptIndexService - lazy val publishedConceptSearchService = new PublishedConceptSearchService + override lazy val draftConceptSearchService = new DraftConceptSearchService + override lazy val searchConverterService = new SearchConverterService + override lazy val draftConceptIndexService = new DraftConceptIndexService + override lazy val publishedConceptIndexService = new PublishedConceptIndexService + override lazy val publishedConceptSearchService = new PublishedConceptSearchService var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer) - lazy val ndlaClient = new NdlaClient - lazy val searchApiClient = new SearchApiClient - lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient + override lazy val ndlaClient = new NdlaClient + override lazy val searchApiClient = new SearchApiClient + override lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient - lazy val writeService = new WriteService - lazy val readService = new ReadService - lazy val converterService = new ConverterService - lazy val clock = new SystemClock - lazy val contentValidator = new ContentValidator + override lazy val writeService = new WriteService + override lazy val readService = new ReadService + override lazy val converterService = new ConverterService + override lazy val clock = new SystemClock + override lazy val contentValidator = new ContentValidator lazy val draftConceptController = new DraftConceptController lazy val publishedConceptController = new PublishedConceptController diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/ConceptApiProperties.scala b/concept-api/src/main/scala/no/ndla/conceptapi/ConceptApiProperties.scala index 1c538d864f..18e13d641b 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/ConceptApiProperties.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/ConceptApiProperties.scala @@ -17,7 +17,7 @@ import no.ndla.validation.ResourceType import scala.util.Properties.* trait Props extends HasBaseProps with HasDatabaseProps { - val props: ConceptApiProperties + lazy val props: ConceptApiProperties } class ConceptApiProperties extends BaseProps with DatabaseProps with StrictLogging { diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/controller/ConceptControllerHelpers.scala b/concept-api/src/main/scala/no/ndla/conceptapi/controller/ConceptControllerHelpers.scala index 14163a4564..097f2e5303 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/controller/ConceptControllerHelpers.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/controller/ConceptControllerHelpers.scala @@ -19,8 +19,6 @@ import sttp.tapir.model.Delimited trait ConceptControllerHelpers { this: Props => - import props.* - object ConceptControllerHelpers { val pathConceptId: EndpointInput.PathCapture[Long] = @@ -76,7 +74,7 @@ trait ConceptControllerHelpers { val scrollId: EndpointInput.Query[Option[String]] = query[Option[String]]("search-context") .description( - s"""A unique string obtained from a search you want to keep scrolling in. To obtain one from a search, provide one of the following values: ${InitialScrollContextKeywords + s"""A unique string obtained from a search you want to keep scrolling in. To obtain one from a search, provide one of the following values: ${props.InitialScrollContextKeywords .mkString("[", ",", "]")}. |When scrolling, the parameters from the initial search is used, except in the case of '${this.language.name}' and '${this.fallback.name}'. |This value may change between scrolls. Always use the one in the latest scroll result. diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/controller/DraftConceptController.scala b/concept-api/src/main/scala/no/ndla/conceptapi/controller/DraftConceptController.scala index c4fc6c06a9..ebcdebbe37 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/controller/DraftConceptController.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/controller/DraftConceptController.scala @@ -35,11 +35,9 @@ import scala.util.{Failure, Success, Try} trait DraftConceptController { this: WriteService & ReadService & DraftConceptSearchService & SearchConverterService & ConverterService & Props & ConceptControllerHelpers & ErrorHandling & TapirController => - val draftConceptController: DraftConceptController + lazy val draftConceptController: DraftConceptController class DraftConceptController extends TapirController { - import props.* - override val serviceName: String = "drafts" override val prefix: EndpointInput[Unit] = "concept-api" / "v1" / serviceName @@ -68,7 +66,7 @@ trait DraftConceptController { orFunction: => Try[(ConceptSearchResultDTO, DynamicHeaders)] ): Try[(ConceptSearchResultDTO, DynamicHeaders)] = scrollId match { - case Some(scroll) if !InitialScrollContextKeywords.contains(scroll) => + case Some(scroll) if !props.InitialScrollContextKeywords.contains(scroll) => draftConceptSearchService.scroll(scroll, language.code) match { case Success(scrollResult) => val body = searchConverterService.asApiConceptSearchResult(scrollResult) @@ -191,7 +189,7 @@ trait DraftConceptController { ) => scrollSearchOr(scrollId, language) { val sort = Sort.valueOf(sortStr) - val shouldScroll = scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = scrollId.exists(props.InitialScrollContextKeywords.contains) search( query, @@ -240,20 +238,20 @@ trait DraftConceptController { .errorOut(errorOutputsFor(400, 403, 404)) .serverLogicPure { searchParams => val scrollId = searchParams.scrollId - val lang = searchParams.language.getOrElse(LanguageCode(DefaultLanguage)) + val lang = searchParams.language.getOrElse(LanguageCode(props.DefaultLanguage)) scrollSearchOr(scrollId, lang) { val query = searchParams.query val sort = searchParams.sort val language = searchParams.language.getOrElse(LanguageCode(AllLanguages)) - val pageSize = searchParams.pageSize.getOrElse(DefaultPageSize) + val pageSize = searchParams.pageSize.getOrElse(props.DefaultPageSize) val page = searchParams.page.getOrElse(1) val idList = searchParams.ids val fallback = searchParams.fallback.getOrElse(false) val tagsToFilterBy = searchParams.tags val statusFilter = searchParams.status val userFilter = searchParams.users - val shouldScroll = searchParams.scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = searchParams.scrollId.exists(props.InitialScrollContextKeywords.contains) val embedResource = searchParams.embedResource.getOrElse(List.empty) val embedId = searchParams.embedId val responsibleId = searchParams.responsibleIds diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/controller/InternController.scala b/concept-api/src/main/scala/no/ndla/conceptapi/controller/InternController.scala index 3aa391b5c1..6976774abf 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/controller/InternController.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/controller/InternController.scala @@ -26,12 +26,11 @@ import scala.concurrent.{Await, ExecutionContext, ExecutionContextExecutorServic import scala.language.postfixOps import scala.util.{Failure, Success, Try} import sttp.tapir.* -import sttp.tapir.generic.auto.* trait InternController { this: IndexService & DraftConceptIndexService & PublishedConceptIndexService & ConverterService & ReadService & DraftConceptRepository & PublishedConceptRepository & ErrorHandling & TapirController => - val internController: InternController + lazy val internController: InternController class InternController extends TapirController { override val prefix: EndpointInput[Unit] = "intern" diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/controller/PublishedConceptController.scala b/concept-api/src/main/scala/no/ndla/conceptapi/controller/PublishedConceptController.scala index 02059b9dbb..9ec166a986 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/controller/PublishedConceptController.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/controller/PublishedConceptController.scala @@ -32,11 +32,10 @@ import scala.util.{Failure, Success, Try} trait PublishedConceptController { this: WriteService & ReadService & PublishedConceptSearchService & SearchConverterService & Props & ConceptControllerHelpers & ErrorHandling & TapirController => - val publishedConceptController: PublishedConceptController + lazy val publishedConceptController: PublishedConceptController class PublishedConceptController extends TapirController { import ConceptControllerHelpers.* - import props.* override val serviceName: String = "concepts" override val prefix: EndpointInput[Unit] = "concept-api" / "v1" / serviceName @@ -52,7 +51,7 @@ trait PublishedConceptController { orFunction: => Try[(ConceptSearchResultDTO, DynamicHeaders)] ): Try[(ConceptSearchResultDTO, DynamicHeaders)] = scrollId match { - case Some(scroll) if !InitialScrollContextKeywords.contains(scroll) => + case Some(scroll) if !props.InitialScrollContextKeywords.contains(scroll) => publishedConceptSearchService.scroll(scroll, language.code) match { case Success(scrollResult) => val body = searchConverterService.asApiConceptSearchResult(scrollResult) @@ -165,7 +164,7 @@ trait PublishedConceptController { ) => scrollSearchOr(scrollId, language) { val sort = Sort.valueOf(sortStr) - val shouldScroll = scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = scrollId.exists(props.InitialScrollContextKeywords.contains) search( query, @@ -195,18 +194,18 @@ trait PublishedConceptController { .out(EndpointOutput.derived[DynamicHeaders]) .errorOut(errorOutputsFor(400, 403, 404)) .serverLogicPure { searchParams => - val lang = searchParams.language.getOrElse(LanguageCode(DefaultLanguage)) + val lang = searchParams.language.getOrElse(LanguageCode(props.DefaultLanguage)) scrollSearchOr(searchParams.scrollId, lang) { val query = searchParams.query val sort = searchParams.sort val language = searchParams.language.getOrElse(LanguageCode(Language.AllLanguages)) - val pageSize = searchParams.pageSize.getOrElse(DefaultPageSize) + val pageSize = searchParams.pageSize.getOrElse(props.DefaultPageSize) val page = searchParams.page.getOrElse(1) val idList = searchParams.ids val fallback = searchParams.fallback.getOrElse(false) val tagsToFilterBy = searchParams.tags val exactTitleMatch = searchParams.exactMatch.getOrElse(false) - val shouldScroll = searchParams.scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = searchParams.scrollId.exists(props.InitialScrollContextKeywords.contains) val embedResource = searchParams.embedResource val embedId = searchParams.embedId val conceptType = searchParams.conceptType diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/model/api/ConceptDomainDump.scala b/concept-api/src/main/scala/no/ndla/conceptapi/model/api/ConceptDomainDump.scala index df93728a0a..7edc6d8e00 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/model/api/ConceptDomainDump.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/model/api/ConceptDomainDump.scala @@ -24,4 +24,6 @@ case class ConceptDomainDump( object ConceptDomainDump { implicit val encoder: Encoder[ConceptDomainDump] = deriveEncoder implicit val decoder: Decoder[ConceptDomainDump] = deriveDecoder + import sttp.tapir.generic.auto.* + implicit def schema: sttp.tapir.Schema[ConceptDomainDump] = sttp.tapir.Schema.derivedSchema } diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/model/api/TagsSearchResultDTO.scala b/concept-api/src/main/scala/no/ndla/conceptapi/model/api/TagsSearchResultDTO.scala index 31f3dcd908..f3c84bdb04 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/model/api/TagsSearchResultDTO.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/model/api/TagsSearchResultDTO.scala @@ -11,6 +11,7 @@ package no.ndla.conceptapi.model.api import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} import sttp.tapir.Schema.annotations.description +import sttp.tapir.Schema @description("Information about tags-search-results") case class TagsSearchResultDTO( @@ -24,4 +25,5 @@ case class TagsSearchResultDTO( object TagsSearchResultDTO { implicit val encoder: Encoder[TagsSearchResultDTO] = deriveEncoder implicit val decoder: Decoder[TagsSearchResultDTO] = deriveDecoder + implicit def schema: Schema[TagsSearchResultDTO] = Schema.derived } diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/repository/DraftConceptRepository.scala b/concept-api/src/main/scala/no/ndla/conceptapi/repository/DraftConceptRepository.scala index 5957277cd1..c5f156cac2 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/repository/DraftConceptRepository.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/repository/DraftConceptRepository.scala @@ -23,7 +23,7 @@ import scala.util.{Failure, Success, Try} trait DraftConceptRepository { this: DataSource & Props & ErrorHandling => - val draftConceptRepository: DraftConceptRepository + lazy val draftConceptRepository: DraftConceptRepository class DraftConceptRepository extends StrictLogging with Repository[Concept] { def insert(concept: Concept)(implicit session: DBSession = AutoSession): Concept = { @@ -229,7 +229,7 @@ trait DraftConceptRepository { s"${DBConcept.schemaName.getOrElse(props.MetaSchema)}.${DBConcept.tableName}_id_seq" ) - sql"alter sequence $sequenceName restart with $idToStartAt;".executeUpdate(): Unit + val _ = sql"alter sequence $sequenceName restart with $idToStartAt;".executeUpdate() } def getTags(input: String, pageSize: Int, offset: Int, language: String)(implicit diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/repository/PublishedConceptRepository.scala b/concept-api/src/main/scala/no/ndla/conceptapi/repository/PublishedConceptRepository.scala index 13fd3c9957..625c70db22 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/repository/PublishedConceptRepository.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/repository/PublishedConceptRepository.scala @@ -22,7 +22,7 @@ import scala.util.{Failure, Success, Try} trait PublishedConceptRepository { this: DataSource => - val publishedConceptRepository: PublishedConceptRepository + lazy val publishedConceptRepository: PublishedConceptRepository class PublishedConceptRepository extends StrictLogging with Repository[Concept] { def insertOrUpdate(concept: Concept)(implicit session: DBSession = AutoSession): Try[Concept] = { diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/service/ConverterService.scala b/concept-api/src/main/scala/no/ndla/conceptapi/service/ConverterService.scala index 30733e54b6..083ad8eb4e 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/service/ConverterService.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/service/ConverterService.scala @@ -46,7 +46,7 @@ import scala.util.{Failure, Success, Try} trait ConverterService { this: Clock & DraftConceptRepository & StateTransitionRules & Props => - val converterService: ConverterService + lazy val converterService: ConverterService class ConverterService extends StrictLogging { diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/service/ReadService.scala b/concept-api/src/main/scala/no/ndla/conceptapi/service/ReadService.scala index 18c9f4415e..2311ae192f 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/service/ReadService.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/service/ReadService.scala @@ -18,7 +18,7 @@ import scala.util.{Failure, Try} trait ReadService { this: DraftConceptRepository & PublishedConceptRepository & ConverterService => - val readService: ReadService + lazy val readService: ReadService class ReadService { diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/service/WriteService.scala b/concept-api/src/main/scala/no/ndla/conceptapi/service/WriteService.scala index 13a94f6565..be7cac242c 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/service/WriteService.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/service/WriteService.scala @@ -31,7 +31,7 @@ import no.ndla.common.errors.OperationNotAllowedException trait WriteService { this: DraftConceptRepository & PublishedConceptRepository & ConverterService & ContentValidator & DraftConceptIndexService & PublishedConceptIndexService & StrictLogging & SearchApiClient & Clock => - val writeService: WriteService + lazy val writeService: WriteService class WriteService { @@ -125,7 +125,7 @@ trait WriteService { implicit val ec: ExecutionContextExecutorService = ExecutionContext.fromExecutorService(executor) draftConceptIndexService.indexDocument(toIndex): Unit - searchApiClient.indexDocument("concept", toIndex, Some(user)): Unit + val _ = searchApiClient.indexDocument("concept", toIndex, Some(user)) } def updateConcept(id: Long, updatedConcept: api.UpdatedConceptDTO, user: TokenUser): Try[api.ConceptDTO] = { diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/service/search/DraftConceptIndexService.scala b/concept-api/src/main/scala/no/ndla/conceptapi/service/search/DraftConceptIndexService.scala index ea7ae3f255..5c5e3938db 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/service/search/DraftConceptIndexService.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/service/search/DraftConceptIndexService.scala @@ -8,16 +8,15 @@ package no.ndla.conceptapi.service.search -import com.typesafe.scalalogging.StrictLogging import no.ndla.common.model.domain.concept.Concept import no.ndla.conceptapi.Props import no.ndla.conceptapi.repository.{DraftConceptRepository, Repository} trait DraftConceptIndexService { this: IndexService & DraftConceptRepository & SearchConverterService & Props => - val draftConceptIndexService: DraftConceptIndexService + lazy val draftConceptIndexService: DraftConceptIndexService - class DraftConceptIndexService extends StrictLogging with IndexService { + class DraftConceptIndexService extends IndexService { override val documentType: String = props.ConceptSearchDocument override val searchIndex: String = props.DraftConceptSearchIndex override val repository: Repository[Concept] = draftConceptRepository diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/service/search/DraftConceptSearchService.scala b/concept-api/src/main/scala/no/ndla/conceptapi/service/search/DraftConceptSearchService.scala index 4c606360cd..67df9ff345 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/service/search/DraftConceptSearchService.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/service/search/DraftConceptSearchService.scala @@ -29,11 +29,10 @@ import scala.util.{Failure, Success, Try} trait DraftConceptSearchService { this: Elastic4sClient & SearchService & DraftConceptIndexService & ConverterService & SearchConverterService & Props & ErrorHandling & DraftSearchSettingsHelper => - val draftConceptSearchService: DraftConceptSearchService + lazy val draftConceptSearchService: DraftConceptSearchService class DraftConceptSearchService extends StrictLogging with SearchService[api.ConceptSummaryDTO] { - import props.* - override val searchIndex: String = DraftConceptSearchIndex + override val searchIndex: String = props.DraftConceptSearchIndex override def hitToApiModel(hitString: String, language: String): api.ConceptSummaryDTO = searchConverterService.hitAsConceptSummary(hitString, language) @@ -102,9 +101,9 @@ trait DraftConceptSearchService { val (startAt, numResults) = getStartAtAndNumResults(settings.page, settings.pageSize) val requestedResultWindow = settings.pageSize * settings.page - if (requestedResultWindow > ElasticSearchIndexMaxResultWindow) { + if (requestedResultWindow > props.ElasticSearchIndexMaxResultWindow) { logger.info( - s"Max supported results are $ElasticSearchIndexMaxResultWindow, user requested $requestedResultWindow" + s"Max supported results are ${props.ElasticSearchIndexMaxResultWindow}, user requested $requestedResultWindow" ) Failure(new ResultWindowTooLargeException()) } else { @@ -121,7 +120,7 @@ trait DraftConceptSearchService { val searchWithScroll = if (startAt == 0 && settings.shouldScroll) { - searchToExecute.scroll(ElasticSearchScrollKeepAlive) + searchToExecute.scroll(props.ElasticSearchScrollKeepAlive) } else { searchToExecute } e4sClient.execute(searchWithScroll) match { diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/service/search/IndexService.scala b/concept-api/src/main/scala/no/ndla/conceptapi/service/search/IndexService.scala index dd34d98631..53c6f11ad1 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/service/search/IndexService.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/service/search/IndexService.scala @@ -26,7 +26,8 @@ import scala.util.{Failure, Success, Try} trait IndexService { this: Elastic4sClient & BaseIndexService & Props & SearchConverterService & SearchLanguage => - trait IndexService extends BaseIndexService with StrictLogging { + + abstract class IndexService extends BaseIndexService with StrictLogging { val repository: Repository[Concept] override val MaxResultWindowOption: Int = props.ElasticSearchIndexMaxResultWindow diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/service/search/PublishedConceptIndexService.scala b/concept-api/src/main/scala/no/ndla/conceptapi/service/search/PublishedConceptIndexService.scala index ab8fb33c5e..c055f9e56d 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/service/search/PublishedConceptIndexService.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/service/search/PublishedConceptIndexService.scala @@ -8,16 +8,15 @@ package no.ndla.conceptapi.service.search -import com.typesafe.scalalogging.StrictLogging import no.ndla.common.model.domain.concept.Concept import no.ndla.conceptapi.Props import no.ndla.conceptapi.repository.{PublishedConceptRepository, Repository} trait PublishedConceptIndexService { this: IndexService & PublishedConceptRepository & SearchConverterService & Props => - val publishedConceptIndexService: PublishedConceptIndexService + lazy val publishedConceptIndexService: PublishedConceptIndexService - class PublishedConceptIndexService extends StrictLogging with IndexService { + class PublishedConceptIndexService extends IndexService { override val documentType: String = props.ConceptSearchDocument override val searchIndex: String = props.PublishedConceptSearchIndex override val repository: Repository[Concept] = publishedConceptRepository diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/service/search/PublishedConceptSearchService.scala b/concept-api/src/main/scala/no/ndla/conceptapi/service/search/PublishedConceptSearchService.scala index 0b9a91adf5..88f55d7f60 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/service/search/PublishedConceptSearchService.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/service/search/PublishedConceptSearchService.scala @@ -29,11 +29,10 @@ import scala.util.{Failure, Success, Try} trait PublishedConceptSearchService { this: Elastic4sClient & SearchService & PublishedConceptIndexService & ConverterService & SearchConverterService & Props & ErrorHandling & SearchSettingsHelper => - val publishedConceptSearchService: PublishedConceptSearchService + lazy val publishedConceptSearchService: PublishedConceptSearchService class PublishedConceptSearchService extends StrictLogging with SearchService[api.ConceptSummaryDTO] { - import props.* - override val searchIndex: String = PublishedConceptSearchIndex + override val searchIndex: String = props.PublishedConceptSearchIndex override def hitToApiModel(hitString: String, language: String): api.ConceptSummaryDTO = searchConverterService.hitAsConceptSummary(hitString, language) @@ -94,9 +93,9 @@ trait PublishedConceptSearchService { val (startAt, numResults) = getStartAtAndNumResults(settings.page, settings.pageSize) val requestedResultWindow = settings.pageSize * settings.page - if (requestedResultWindow > ElasticSearchIndexMaxResultWindow) { + if (requestedResultWindow > props.ElasticSearchIndexMaxResultWindow) { logger.info( - s"Max supported results are $ElasticSearchIndexMaxResultWindow, user requested $requestedResultWindow" + s"Max supported results are ${props.ElasticSearchIndexMaxResultWindow}, user requested $requestedResultWindow" ) Failure(new ResultWindowTooLargeException()) } else { @@ -113,7 +112,7 @@ trait PublishedConceptSearchService { val searchWithScroll = if (startAt == 0 && settings.shouldScroll) { - searchToExecute.scroll(ElasticSearchScrollKeepAlive) + searchToExecute.scroll(props.ElasticSearchScrollKeepAlive) } else { searchToExecute } e4sClient.execute(searchWithScroll) match { diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/service/search/SearchConverterService.scala b/concept-api/src/main/scala/no/ndla/conceptapi/service/search/SearchConverterService.scala index 5dad8dc9d3..c85b76a6ac 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/service/search/SearchConverterService.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/service/search/SearchConverterService.scala @@ -31,7 +31,7 @@ import org.jsoup.Jsoup trait SearchConverterService { this: ConverterService => - val searchConverterService: SearchConverterService + lazy val searchConverterService: SearchConverterService class SearchConverterService extends StrictLogging { private def getEmbedResourcesAndIdsToIndex( diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/service/search/SearchService.scala b/concept-api/src/main/scala/no/ndla/conceptapi/service/search/SearchService.scala index c3bc5c7c9a..6c3d640152 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/service/search/SearchService.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/service/search/SearchService.scala @@ -31,14 +31,12 @@ trait SearchService { this: Elastic4sClient & SearchConverterService & StrictLogging & Props => trait SearchService[T] { - import props.* - val searchIndex: String def scroll(scrollId: String, language: String): Try[SearchResult[T]] = e4sClient .execute { - searchScroll(scrollId, ElasticSearchScrollKeepAlive) + searchScroll(scrollId, props.ElasticSearchScrollKeepAlive) } .map(response => { val hits = getHits(response.result, language) @@ -129,7 +127,7 @@ trait SearchService { def getSortDefinition(sort: Sort, language: String): FieldSort = { val sortLanguage = language match { - case NoLanguage => DefaultLanguage + case NoLanguage => props.DefaultLanguage case _ => language } @@ -193,7 +191,7 @@ trait SearchService { } def getStartAtAndNumResults(page: Int, pageSize: Int): (Int, Int) = { - val numResults = max(pageSize.min(MaxPageSize), 0) + val numResults = max(pageSize.min(props.MaxPageSize), 0) val startAt = (page - 1).max(0) * numResults (startAt, numResults) diff --git a/concept-api/src/main/scala/no/ndla/conceptapi/validation/ContentValidator.scala b/concept-api/src/main/scala/no/ndla/conceptapi/validation/ContentValidator.scala index e1d028e4d0..30a204d559 100644 --- a/concept-api/src/main/scala/no/ndla/conceptapi/validation/ContentValidator.scala +++ b/concept-api/src/main/scala/no/ndla/conceptapi/validation/ContentValidator.scala @@ -25,7 +25,7 @@ import scala.util.{Failure, Success, Try} trait ContentValidator { this: DraftConceptRepository & ConverterService & Props => - val contentValidator: ContentValidator + lazy val contentValidator: ContentValidator class ContentValidator { private val inlineHtmlTags = props.InlineHtmlTags diff --git a/concept-api/src/test/scala/no/ndla/conceptapi/TestEnvironment.scala b/concept-api/src/test/scala/no/ndla/conceptapi/TestEnvironment.scala index deb83cf012..e09ee498dc 100644 --- a/concept-api/src/test/scala/no/ndla/conceptapi/TestEnvironment.scala +++ b/concept-api/src/test/scala/no/ndla/conceptapi/TestEnvironment.scala @@ -68,33 +68,33 @@ trait TestEnvironment override def IntroductionHtmlTags: Set[String] = Set("br", "code", "em", "p", "span", "strong", "sub", "sup") } - val migrator: DBMigrator = mock[DBMigrator] - val draftConceptRepository: DraftConceptRepository = mock[DraftConceptRepository] - val publishedConceptRepository: PublishedConceptRepository = mock[PublishedConceptRepository] + override lazy val migrator: DBMigrator = mock[DBMigrator] + override lazy val draftConceptRepository: DraftConceptRepository = mock[DraftConceptRepository] + override lazy val publishedConceptRepository: PublishedConceptRepository = mock[PublishedConceptRepository] - val draftConceptController: DraftConceptController = mock[DraftConceptController] - val publishedConceptController: PublishedConceptController = mock[PublishedConceptController] - val internController: InternController = mock[InternController] - val healthController: TapirHealthController = mock[TapirHealthController] + override lazy val draftConceptController: DraftConceptController = mock[DraftConceptController] + override lazy val publishedConceptController: PublishedConceptController = mock[PublishedConceptController] + override lazy val internController: InternController = mock[InternController] + override lazy val healthController: TapirHealthController = mock[TapirHealthController] - val searchConverterService: SearchConverterService = mock[SearchConverterService] - val draftConceptIndexService: DraftConceptIndexService = mock[DraftConceptIndexService] - val draftConceptSearchService: DraftConceptSearchService = mock[DraftConceptSearchService] - val publishedConceptIndexService: PublishedConceptIndexService = mock[PublishedConceptIndexService] - val publishedConceptSearchService: PublishedConceptSearchService = mock[PublishedConceptSearchService] + override lazy val searchConverterService: SearchConverterService = mock[SearchConverterService] + override lazy val draftConceptIndexService: DraftConceptIndexService = mock[DraftConceptIndexService] + override lazy val draftConceptSearchService: DraftConceptSearchService = mock[DraftConceptSearchService] + override lazy val publishedConceptIndexService: PublishedConceptIndexService = mock[PublishedConceptIndexService] + override lazy val publishedConceptSearchService: PublishedConceptSearchService = mock[PublishedConceptSearchService] - var e4sClient: NdlaE4sClient = mock[NdlaE4sClient] - val mockitoSugar: MockitoSugar = mock[MockitoSugar] - val dataSource: HikariDataSource = mock[HikariDataSource] - val writeService: WriteService = mock[WriteService] - val readService: ReadService = mock[ReadService] - val converterService: ConverterService = mock[ConverterService] - val contentValidator: ContentValidator = mock[ContentValidator] - val clock: SystemClock = mock[SystemClock] + var e4sClient: NdlaE4sClient = mock[NdlaE4sClient] + val mockitoSugar: MockitoSugar = mock[MockitoSugar] + override lazy val dataSource: HikariDataSource = mock[HikariDataSource] + override lazy val writeService: WriteService = mock[WriteService] + override lazy val readService: ReadService = mock[ReadService] + override lazy val converterService: ConverterService = mock[ConverterService] + override lazy val contentValidator: ContentValidator = mock[ContentValidator] + override lazy val clock: SystemClock = mock[SystemClock] - val ndlaClient: NdlaClient = mock[NdlaClient] - val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] - val searchApiClient: SearchApiClient = mock[SearchApiClient] + override lazy val ndlaClient: NdlaClient = mock[NdlaClient] + override lazy val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] + override lazy val searchApiClient: SearchApiClient = mock[SearchApiClient] def services: List[TapirController] = List.empty val swagger: SwaggerController = mock[SwaggerController] diff --git a/concept-api/src/test/scala/no/ndla/conceptapi/repository/DraftConceptRepositoryTest.scala b/concept-api/src/test/scala/no/ndla/conceptapi/repository/DraftConceptRepositoryTest.scala index be0fd6a421..c44161976a 100644 --- a/concept-api/src/test/scala/no/ndla/conceptapi/repository/DraftConceptRepositoryTest.scala +++ b/concept-api/src/test/scala/no/ndla/conceptapi/repository/DraftConceptRepositoryTest.scala @@ -22,9 +22,9 @@ import scala.util.{Failure, Success, Try} class DraftConceptRepositoryTest extends DatabaseIntegrationSuite with UnitSuite with TestEnvironment { - override val dataSource: HikariDataSource = testDataSource.get - override val migrator = new DBMigrator - var repository: DraftConceptRepository = _ + override lazy val dataSource: HikariDataSource = testDataSource.get + override lazy val migrator = new DBMigrator + var repository: DraftConceptRepository = _ def emptyTestDatabase: Boolean = { DB autoCommit (implicit session => { diff --git a/concept-api/src/test/scala/no/ndla/conceptapi/repository/PublishedConceptRepositoryTest.scala b/concept-api/src/test/scala/no/ndla/conceptapi/repository/PublishedConceptRepositoryTest.scala index 333fe85bc6..ab815eae1e 100644 --- a/concept-api/src/test/scala/no/ndla/conceptapi/repository/PublishedConceptRepositoryTest.scala +++ b/concept-api/src/test/scala/no/ndla/conceptapi/repository/PublishedConceptRepositoryTest.scala @@ -22,9 +22,9 @@ import scala.util.{Success, Try} class PublishedConceptRepositoryTest extends DatabaseIntegrationSuite with TestEnvironment { - override val dataSource: HikariDataSource = testDataSource.get - override val migrator = new DBMigrator - var repository: PublishedConceptRepository = _ + override lazy val dataSource: HikariDataSource = testDataSource.get + override lazy val migrator = new DBMigrator + var repository: PublishedConceptRepository = _ def emptyTestDatabase: Boolean = { DB autoCommit (implicit session => { @@ -203,9 +203,9 @@ class PublishedConceptRepositoryTest extends DatabaseIntegrationSuite with TestE updated = NDLADate.fromUnixTime(0) ) - val Success(ins1) = repository.insertOrUpdate(con1) - val Success(ins2) = repository.insertOrUpdate(con2) - val Success(ins3) = repository.insertOrUpdate(con3) + val Success(ins1) = repository.insertOrUpdate(con1): @unchecked + val Success(ins2) = repository.insertOrUpdate(con2): @unchecked + val Success(ins3) = repository.insertOrUpdate(con3): @unchecked repository.getByPage(10, 0).sortBy(_.id.get) should be(Seq(ins1, ins2, ins3)) } diff --git a/concept-api/src/test/scala/no/ndla/conceptapi/service/ConverterServiceTest.scala b/concept-api/src/test/scala/no/ndla/conceptapi/service/ConverterServiceTest.scala index bdc2be632e..0c1f7a2e94 100644 --- a/concept-api/src/test/scala/no/ndla/conceptapi/service/ConverterServiceTest.scala +++ b/concept-api/src/test/scala/no/ndla/conceptapi/service/ConverterServiceTest.scala @@ -23,8 +23,8 @@ import scala.util.{Failure, Success} class ConverterServiceTest extends UnitSuite with TestEnvironment { - override val converterService = new ConverterService - val userInfo: TokenUser = TokenUser("", Set(CONCEPT_API_WRITE, CONCEPT_API_ADMIN), None) + override lazy val converterService = new ConverterService + val userInfo: TokenUser = TokenUser("", Set(CONCEPT_API_WRITE, CONCEPT_API_ADMIN), None) test("toApiConcept converts a domain.Concept to an api.Concept with defined language") { converterService.toApiConcept(TestData.domainConcept, "nn", fallback = false, Some(userInfo)) should be( @@ -387,7 +387,7 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { ) val newConcept = TestData.emptyApiNewConcept.copy(conceptType = "gloss", glossData = Some(newGlossData)) - val Failure(result1) = converterService.toDomainConcept(newConcept, TestData.userWithWriteAccess) + val Failure(result1) = converterService.toDomainConcept(newConcept, TestData.userWithWriteAccess): @unchecked result1.getMessage should include("'ikke' is not a valid gloss type") // val newConcept2 = @@ -464,7 +464,7 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { val existingConcept = TestData.domainConcept.copy(conceptType = concept.ConceptType.CONCEPT, glossData = None) val Failure(result) = - converterService.toDomainConcept(existingConcept, updatedConcept, TestData.userWithWriteAccess) + converterService.toDomainConcept(existingConcept, updatedConcept, TestData.userWithWriteAccess): @unchecked result.getMessage should include("'ikke eksisterende' is not a valid gloss type") } @@ -556,7 +556,7 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { ) ) - val Failure(result) = converterService.toDomainGlossData(apiGlossData) + val Failure(result) = converterService.toDomainGlossData(apiGlossData): @unchecked result.getMessage should include("'nonexistent' is not a valid gloss type") } diff --git a/concept-api/src/test/scala/no/ndla/conceptapi/service/ReadServiceTest.scala b/concept-api/src/test/scala/no/ndla/conceptapi/service/ReadServiceTest.scala index 03e83982a5..909f06a906 100644 --- a/concept-api/src/test/scala/no/ndla/conceptapi/service/ReadServiceTest.scala +++ b/concept-api/src/test/scala/no/ndla/conceptapi/service/ReadServiceTest.scala @@ -18,7 +18,7 @@ import org.mockito.ArgumentMatchers.any import org.mockito.Mockito.when class ReadServiceTest extends UnitSuite with TestEnvironment { - override val converterService = new ConverterService + override lazy val converterService = new ConverterService val service = new ReadService() val userInfo: TokenUser = TokenUser("", Set(CONCEPT_API_WRITE), None) diff --git a/concept-api/src/test/scala/no/ndla/conceptapi/service/WriteServiceTest.scala b/concept-api/src/test/scala/no/ndla/conceptapi/service/WriteServiceTest.scala index bc095319a7..403ae24f81 100644 --- a/concept-api/src/test/scala/no/ndla/conceptapi/service/WriteServiceTest.scala +++ b/concept-api/src/test/scala/no/ndla/conceptapi/service/WriteServiceTest.scala @@ -26,7 +26,7 @@ import org.mockito.Mockito.{reset, times, verify, when} import org.mockito.stubbing.OngoingStubbing class WriteServiceTest extends UnitSuite with TestEnvironment { - override val converterService = new ConverterService + override lazy val converterService = new ConverterService val today: NDLADate = NDLADate.now() val yesterday: NDLADate = NDLADate.now().minusDays(1) @@ -181,7 +181,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { } test("That delete concept should fail when only one language") { - val Failure(result) = service.deleteLanguage(concept.id, "nb", userInfo) + val Failure(result) = service.deleteLanguage(concept.id, "nb", userInfo): @unchecked result.getMessage should equal("Only one language left") } diff --git a/concept-api/src/test/scala/no/ndla/conceptapi/service/search/DraftConceptSearchServiceTest.scala b/concept-api/src/test/scala/no/ndla/conceptapi/service/search/DraftConceptSearchServiceTest.scala index 6203e8a282..f88e2363c0 100644 --- a/concept-api/src/test/scala/no/ndla/conceptapi/service/search/DraftConceptSearchServiceTest.scala +++ b/concept-api/src/test/scala/no/ndla/conceptapi/service/search/DraftConceptSearchServiceTest.scala @@ -17,7 +17,6 @@ import no.ndla.conceptapi.model.search.DraftSearchSettings import no.ndla.language.Language import no.ndla.scalatestsuite.ElasticsearchIntegrationSuite -import scala.util.Success import no.ndla.common.model.NDLADate import no.ndla.common.model.domain.concept.{ Concept, @@ -34,19 +33,18 @@ import no.ndla.mapping.License import java.util.UUID class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with TestEnvironment { - import props.{DefaultLanguage, DefaultPageSize} e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("http://localhost:9200")) - val indexName: String = UUID.randomUUID().toString - override val draftConceptSearchService: DraftConceptSearchService = new DraftConceptSearchService { + val indexName: String = UUID.randomUUID().toString + override lazy val draftConceptSearchService: DraftConceptSearchService = new DraftConceptSearchService { override val searchIndex: String = indexName } - override val draftConceptIndexService: DraftConceptIndexService = new DraftConceptIndexService { + override lazy val draftConceptIndexService: DraftConceptIndexService = new DraftConceptIndexService { override val indexShards = 1 override val searchIndex: String = indexName } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService val byNcSa: DraftCopyright = DraftCopyright( Some(License.CC_BY_NC_SA.toString), @@ -221,7 +219,7 @@ class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with T val searchSettings: DraftSearchSettings = DraftSearchSettings( withIdIn = List.empty, - searchLanguage = DefaultLanguage, + searchLanguage = props.DefaultLanguage, page = 1, pageSize = 10, sort = Sort.ByIdAsc, @@ -270,24 +268,23 @@ class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with T "That getStartAtAndNumResults returns the correct calculated start at for page and page-size with default page-size" ) { val page = 74 - val expectedStartAt = (page - 1) * DefaultPageSize - draftConceptSearchService.getStartAtAndNumResults(page, DefaultPageSize) should equal( - (expectedStartAt, DefaultPageSize) + val expectedStartAt = (page - 1) * props.DefaultPageSize + draftConceptSearchService.getStartAtAndNumResults(page, props.DefaultPageSize) should equal( + (expectedStartAt, props.DefaultPageSize) ) } test("That getStartAtAndNumResults returns the correct calculated start at for page and page-size") { val page = 123 - val expectedStartAt = (page - 1) * DefaultPageSize - draftConceptSearchService.getStartAtAndNumResults(page, DefaultPageSize) should equal( - (expectedStartAt, DefaultPageSize) + val expectedStartAt = (page - 1) * props.DefaultPageSize + draftConceptSearchService.getStartAtAndNumResults(page, props.DefaultPageSize) should equal( + (expectedStartAt, props.DefaultPageSize) ) } test("That all returns all documents ordered by id ascending") { - val Success(results) = - draftConceptSearchService.all(searchSettings.copy(sort = Sort.ByIdAsc)) - val hits = results.results + val results = draftConceptSearchService.all(searchSettings.copy(sort = Sort.ByIdAsc)).get + val hits = results.results results.totalCount should be(11) hits.head.id should be(1) hits(1).id should be(2) @@ -302,17 +299,16 @@ class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with T } test("That all returns all documents ordered by id descending") { - val Success(results) = - draftConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByIdDesc)) - val hits = results.results + val results = draftConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByIdDesc)).get + val hits = results.results results.totalCount should be(11) hits.head.id should be(13) hits.last.id should be(1) } test("That all returns all documents ordered by title ascending") { - val Success(results) = - draftConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByTitleAsc, fallback = true)) + val results = + draftConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByTitleAsc, fallback = true)).get val hits = results.results results.totalCount should be(12) @@ -331,8 +327,8 @@ class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with T } test("That all returns all documents ordered by title descending") { - val Success(results) = - draftConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByTitleDesc, fallback = true)) + val results = + draftConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByTitleDesc, fallback = true)).get val hits = results.results results.totalCount should be(12) hits.head.id should be(7) @@ -351,27 +347,23 @@ class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with T } test("That all returns all documents ordered by lastUpdated descending") { - val Success(results) = - draftConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByLastUpdatedDesc)) - val hits = results.results + val results = draftConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByLastUpdatedDesc)).get + val hits = results.results results.totalCount should be(11) hits.map(_.id) should be(Seq(10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 13)) } test("That all filtered by id only returns documents with the given ids") { - val Success(results) = - draftConceptSearchService.all(searchSettings.copy(withIdIn = List(1, 3))) - val hits = results.results + val results = draftConceptSearchService.all(searchSettings.copy(withIdIn = List(1, 3))).get + val hits = results.results results.totalCount should be(2) hits.head.id should be(1) hits.last.id should be(3) } test("That paging returns only hits on current page and not more than page-size") { - val Success(page1) = - draftConceptSearchService.all(searchSettings.copy(page = 1, pageSize = 2, sort = Sort.ByTitleAsc)) - val Success(page2) = - draftConceptSearchService.all(searchSettings.copy(page = 2, pageSize = 2, sort = Sort.ByTitleAsc)) + val page1 = draftConceptSearchService.all(searchSettings.copy(page = 1, pageSize = 2, sort = Sort.ByTitleAsc)).get + val page2 = draftConceptSearchService.all(searchSettings.copy(page = 2, pageSize = 2, sort = Sort.ByTitleAsc)).get val hits1 = page1.results page1.totalCount should be(11) @@ -389,59 +381,54 @@ class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with T } test("That search matches title and content ordered by relevance descending") { - val Success(results) = - draftConceptSearchService.matchingQuery("bil", searchSettings.copy(sort = Sort.ByRelevanceDesc)) - val hits = results.results + val results = draftConceptSearchService.matchingQuery("bil", searchSettings.copy(sort = Sort.ByRelevanceDesc)).get + val hits = results.results results.totalCount should be(3) hits.map(_.id) should be(Seq(1, 5, 3)) } test("That search matches title") { - val Success(results) = - draftConceptSearchService.matchingQuery("Pingvinen", searchSettings.copy(sort = Sort.ByTitleAsc)) - val hits = results.results + val results = draftConceptSearchService.matchingQuery("Pingvinen", searchSettings.copy(sort = Sort.ByTitleAsc)).get + val hits = results.results results.totalCount should be(1) hits.head.id should be(2) } test("Searching with logical AND only returns results with all terms") { - val Success(search1) = - draftConceptSearchService.matchingQuery("bilde + bil", searchSettings.copy(sort = Sort.ByTitleAsc)) + val search1 = + draftConceptSearchService.matchingQuery("bilde + bil", searchSettings.copy(sort = Sort.ByTitleAsc)).get val hits1 = search1.results hits1.map(_.id) should equal(Seq(1, 3, 5)) - val Success(search2) = - draftConceptSearchService.matchingQuery("batmen + bil", searchSettings.copy(sort = Sort.ByTitleAsc)) + val search2 = + draftConceptSearchService.matchingQuery("batmen + bil", searchSettings.copy(sort = Sort.ByTitleAsc)).get val hits2 = search2.results hits2.map(_.id) should equal(Seq(1)) - val Success(search3) = - draftConceptSearchService.matchingQuery( - "bil + bilde + -flaggermusmann", - searchSettings.copy(sort = Sort.ByTitleAsc) - ) + val search3 = draftConceptSearchService + .matchingQuery("bil + bilde + -flaggermusmann", searchSettings.copy(sort = Sort.ByTitleAsc)) + .get val hits3 = search3.results hits3.map(_.id) should equal(Seq(3, 5)) - val Success(search4) = - draftConceptSearchService.matchingQuery("bil + -hulken", searchSettings.copy(sort = Sort.ByTitleAsc)) + val search4 = + draftConceptSearchService.matchingQuery("bil + -hulken", searchSettings.copy(sort = Sort.ByTitleAsc)).get val hits4 = search4.results hits4.map(_.id) should equal(Seq(1, 3)) } test("search in content should be ranked lower than title") { - val Success(search) = - draftConceptSearchService.matchingQuery("mareritt + ragnarok", searchSettings.copy(sort = Sort.ByRelevanceDesc)) + val search = draftConceptSearchService + .matchingQuery("mareritt + ragnarok", searchSettings.copy(sort = Sort.ByRelevanceDesc)) + .get val hits = search.results hits.map(_.id) should equal(Seq(9, 8)) } test("Search should return language it is matched in") { - val Success(searchEn) = - draftConceptSearchService.matchingQuery("Unrelated", searchSettings.copy(searchLanguage = "*")) - val Success(searchNb) = - draftConceptSearchService.matchingQuery("Urelatert", searchSettings.copy(searchLanguage = "*")) + val searchEn = draftConceptSearchService.matchingQuery("Unrelated", searchSettings.copy(searchLanguage = "*")).get + val searchNb = draftConceptSearchService.matchingQuery("Urelatert", searchSettings.copy(searchLanguage = "*")).get searchEn.totalCount should be(1) searchEn.results.head.title.language should be("en") @@ -457,8 +444,8 @@ class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with T } test("Search for all languages should return all concepts in correct language") { - val Success(search) = - draftConceptSearchService.all(searchSettings.copy(searchLanguage = Language.AllLanguages, pageSize = 100)) + val search = + draftConceptSearchService.all(searchSettings.copy(searchLanguage = Language.AllLanguages, pageSize = 100)).get val hits = search.results search.totalCount should equal(12) @@ -479,10 +466,9 @@ class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with T } test("That searching with fallback parameter returns concept in language priority even if doesnt match on language") { - val Success(search) = - draftConceptSearchService.all( - searchSettings.copy(withIdIn = List(9, 10, 11), searchLanguage = "en", fallback = true) - ) + val search = draftConceptSearchService + .all(searchSettings.copy(withIdIn = List(9, 10, 11), searchLanguage = "en", fallback = true)) + .get search.totalCount should equal(3) search.results.head.id should equal(9) @@ -494,9 +480,8 @@ class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with T } test("That searching for language not in analyzers works") { - val Success(search) = - draftConceptSearchService.all(searchSettings.copy(searchLanguage = "dhm")) - val hits = search.results + val search = draftConceptSearchService.all(searchSettings.copy(searchLanguage = "dhm")).get + val hits = search.results search.totalCount should equal(1) hits.head.id should be(11) @@ -504,8 +489,7 @@ class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with T } test("That searching for not indexed language should work and not return hits") { - val Success(search) = - draftConceptSearchService.all(searchSettings.copy(searchLanguage = "bij")) + val search = draftConceptSearchService.all(searchSettings.copy(searchLanguage = "bij")).get search.totalCount should equal(0) } @@ -513,17 +497,15 @@ class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with T val pageSize = 2 val expectedIds = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13).sliding(pageSize, pageSize).toList - val Success(initialSearch) = - draftConceptSearchService.all( - searchSettings.copy(searchLanguage = "*", pageSize = pageSize, fallback = true, shouldScroll = true) - ) - - val Success(scroll1) = draftConceptSearchService.scroll(initialSearch.scrollId.get, "*") - val Success(scroll2) = draftConceptSearchService.scroll(scroll1.scrollId.get, "*") - val Success(scroll3) = draftConceptSearchService.scroll(scroll2.scrollId.get, "*") - val Success(scroll4) = draftConceptSearchService.scroll(scroll3.scrollId.get, "*") - val Success(scroll5) = draftConceptSearchService.scroll(scroll4.scrollId.get, "*") - val Success(scroll6) = draftConceptSearchService.scroll(scroll5.scrollId.get, "*") + val initialSearch = draftConceptSearchService + .all(searchSettings.copy(searchLanguage = "*", pageSize = pageSize, fallback = true, shouldScroll = true)) + .get + val scroll1 = draftConceptSearchService.scroll(initialSearch.scrollId.get, "*").get + val scroll2 = draftConceptSearchService.scroll(scroll1.scrollId.get, "*").get + val scroll3 = draftConceptSearchService.scroll(scroll2.scrollId.get, "*").get + val scroll4 = draftConceptSearchService.scroll(scroll3.scrollId.get, "*").get + val scroll5 = draftConceptSearchService.scroll(scroll4.scrollId.get, "*").get + val scroll6 = draftConceptSearchService.scroll(scroll5.scrollId.get, "*").get initialSearch.results.map(_.id) should be(expectedIds.head) scroll1.results.map(_.id) should be(expectedIds(1)) @@ -535,19 +517,18 @@ class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with T } test("that searching for tags works and respects language/fallback") { - val Success(search) = - draftConceptSearchService.matchingQuery("burugle", searchSettings.copy(searchLanguage = "*")) + val search = draftConceptSearchService.matchingQuery("burugle", searchSettings.copy(searchLanguage = "*")).get search.totalCount should be(1) search.results.head.id should be(10) - val Success(search2) = - draftConceptSearchService.matchingQuery("burugle", searchSettings.copy(searchLanguage = "en")) + val search2 = draftConceptSearchService.matchingQuery("burugle", searchSettings.copy(searchLanguage = "en")).get search2.totalCount should be(0) - val Success(search3) = - draftConceptSearchService.matchingQuery("burugle", searchSettings.copy(searchLanguage = "en", fallback = true)) + val search3 = draftConceptSearchService + .matchingQuery("burugle", searchSettings.copy(searchLanguage = "en", fallback = true)) + .get search3.totalCount should be(1) search3.results.head.id should be(10) @@ -582,172 +563,164 @@ class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with T } test("that filtering for tags works as expected") { - val Success(search) = draftConceptSearchService.all(searchSettings.copy(tagsToFilterBy = Set("burugle"))) + val search = draftConceptSearchService.all(searchSettings.copy(tagsToFilterBy = Set("burugle"))).get search.totalCount should be(1) search.results.map(_.id) should be(Seq(10)) - val Success(search1) = - draftConceptSearchService.all(searchSettings.copy(searchLanguage = "*", tagsToFilterBy = Set("burugle"))) + val search1 = + draftConceptSearchService.all(searchSettings.copy(searchLanguage = "*", tagsToFilterBy = Set("burugle"))).get search1.totalCount should be(1) search1.results.map(_.id) should be(Seq(10)) } test("Filtering by statuses works as expected with OR filtering") { - val Success(statusSearch1) = draftConceptSearchService.all(searchSettings.copy(statusFilter = Set("PUBLISHED"))) + val statusSearch1 = draftConceptSearchService.all(searchSettings.copy(statusFilter = Set("PUBLISHED"))).get statusSearch1.totalCount should be(2) statusSearch1.results.map(_.id) should be(Seq(9, 10)) - val Success(statusSearch2) = draftConceptSearchService.all(searchSettings.copy(statusFilter = Set("FOR_APPROVAL"))) + val statusSearch2 = draftConceptSearchService.all(searchSettings.copy(statusFilter = Set("FOR_APPROVAL"))).get statusSearch2.totalCount should be(1) statusSearch2.results.map(_.id) should be(Seq(10)) - val Success(statusSearch3) = - draftConceptSearchService.all(searchSettings.copy(statusFilter = Set("FOR_APPROVAL", "END_CONTROL"))) + val statusSearch3 = + draftConceptSearchService.all(searchSettings.copy(statusFilter = Set("FOR_APPROVAL", "END_CONTROL"))).get statusSearch3.totalCount should be(2) statusSearch3.results.map(_.id) should be(Seq(8, 10)) } test("ARCHIVED concepts should only be returned if filtered by ARCHIVED") { - val query = "slettet" - val Success(search1) = - draftConceptSearchService.matchingQuery( - query = query, - searchSettings.copy(withIdIn = List(12), statusFilter = Set(ConceptStatus.ARCHIVED.toString)) - ) - val Success(search2) = - draftConceptSearchService.matchingQuery( - query = query, - searchSettings.copy(withIdIn = List(12), statusFilter = Set.empty) - ) + val query = "slettet" + val search1 = + draftConceptSearchService + .matchingQuery( + query = query, + searchSettings.copy(withIdIn = List(12), statusFilter = Set(ConceptStatus.ARCHIVED.toString)) + ) + .get + val search2 = + draftConceptSearchService + .matchingQuery( + query = query, + searchSettings.copy(withIdIn = List(12), statusFilter = Set.empty) + ) + .get search1.results.map(_.id) should be(Seq(12)) search2.results.map(_.id) should be(Seq.empty) } test("Filtering by users works as expected with OR filtering") { - val Success(res1) = draftConceptSearchService.all(searchSettings.copy(userFilter = Seq("test1"))) + val res1 = draftConceptSearchService.all(searchSettings.copy(userFilter = Seq("test1"))).get res1.totalCount should be(3) res1.results.map(_.id) should be(Seq(2, 3, 7)) - val Success(res2) = draftConceptSearchService.all(searchSettings.copy(userFilter = Seq("test2"))) + val res2 = draftConceptSearchService.all(searchSettings.copy(userFilter = Seq("test2"))).get res2.totalCount should be(2) res2.results.map(_.id) should be(Seq(3, 5)) - val Success(res3) = draftConceptSearchService.all(searchSettings.copy(userFilter = Seq("Test1"))) + val res3 = draftConceptSearchService.all(searchSettings.copy(userFilter = Seq("Test1"))).get res3.totalCount should be(2) res3.results.map(_.id) should be(Seq(7, 10)) - val Success(res4) = draftConceptSearchService.all(searchSettings.copy(userFilter = Seq("test1", "test2"))) + val res4 = draftConceptSearchService.all(searchSettings.copy(userFilter = Seq("test1", "test2"))).get res4.totalCount should be(4) res4.results.map(_.id) should be(Seq(2, 3, 5, 7)) - val Success(res5) = draftConceptSearchService.all(searchSettings.copy(userFilter = Seq("test1", "Test1"))) + val res5 = draftConceptSearchService.all(searchSettings.copy(userFilter = Seq("test1", "Test1"))).get res5.totalCount should be(4) res5.results.map(_.id) should be(Seq(2, 3, 7, 10)) - val Success(res6) = draftConceptSearchService.all(searchSettings.copy(userFilter = Seq("test2", "Test1"))) + val res6 = draftConceptSearchService.all(searchSettings.copy(userFilter = Seq("test2", "Test1"))).get res6.totalCount should be(4) res6.results.map(_.id) should be(Seq(3, 5, 7, 10)) - val Success(res7) = draftConceptSearchService.all(searchSettings.copy(userFilter = Seq("test1", "Test1", "test2"))) + val res7 = draftConceptSearchService.all(searchSettings.copy(userFilter = Seq("test1", "Test1", "test2"))).get res7.totalCount should be(5) res7.results.map(_.id) should be(Seq(2, 3, 5, 7, 10)) } test("that search on embedId matches visual element") { - val Success(search) = - draftConceptSearchService.all( - searchSettings.copy(searchLanguage = Language.AllLanguages, embedId = Some("test.url")) - ) + val search = + draftConceptSearchService + .all( + searchSettings.copy(searchLanguage = Language.AllLanguages, embedId = Some("test.url")) + ) + .get search.totalCount should be(2) search.results.map(_.id) should be(List(9, 10)) } test("that search on embedResource matches visual element") { - val Success(search) = - draftConceptSearchService.all( - searchSettings.copy(searchLanguage = Language.AllLanguages, embedResource = List("brightcove")) - ) + val search = + draftConceptSearchService + .all(searchSettings.copy(searchLanguage = Language.AllLanguages, embedResource = List("brightcove"))) + .get search.totalCount should be(1) search.results.head.id should be(10) } test("that search on embedId matches visual element image") { - val Success(search) = - draftConceptSearchService.all( - searchSettings.copy(searchLanguage = Language.AllLanguages, embedId = Some("test.image")) - ) + val search = + draftConceptSearchService + .all(searchSettings.copy(searchLanguage = Language.AllLanguages, embedId = Some("test.image"))) + .get search.totalCount should be(1) search.results.head.id should be(9) } test("that search on query parameter as embedId matches visual element image") { - val Success(search) = - draftConceptSearchService.matchingQuery( - "test.image", - searchSettings.copy() - ) + val search = + draftConceptSearchService + .matchingQuery("test.image", searchSettings.copy()) + .get search.totalCount should be(1) search.results.head.id should be(9) } test("that search on query parameter as embedResource matches visual element") { - val Success(search) = - draftConceptSearchService.matchingQuery( - "brightcove", - searchSettings.copy() - ) + val search = + draftConceptSearchService + .matchingQuery("brightcove", searchSettings.copy()) + .get search.totalCount should be(1) search.results.head.id should be(10) } test("that search on query parameter as embedId matches visual element") { - val Success(search) = - draftConceptSearchService.matchingQuery( - "test.url", - searchSettings.copy() - ) + val search = draftConceptSearchService.matchingQuery("test.url", searchSettings.copy()).get search.totalCount should be(2) search.results.map(_.id) should be(List(9, 10)) } test("that search on query parameter matches on concept id") { - val Success(search) = - draftConceptSearchService.matchingQuery( - "2", - searchSettings.copy() - ) + val search = draftConceptSearchService.matchingQuery("2", searchSettings.copy()).get search.totalCount should be(1) search.results.head.id should be(2) } test("that search on embedId and embedResource only returns results with an embed matching both params") { - val Success(search) = - draftConceptSearchService.all( - searchSettings - .copy(searchLanguage = Language.AllLanguages, embedResource = List("image"), embedId = Some("test.image")) - ) + val search = + draftConceptSearchService + .all( + searchSettings + .copy(searchLanguage = Language.AllLanguages, embedResource = List("image"), embedId = Some("test.image")) + ) + .get search.totalCount should be(1) search.results.head.id should be(9) } test("That search on embed id supports embed with multiple id attributes") { - val Success(search1) = - draftConceptSearchService.all( - searchSettings.copy(embedId = Some("test.url2")) - ) - val Success(search2) = - draftConceptSearchService.all( - searchSettings.copy(embedId = Some("test.id2")) - ) + val search1 = draftConceptSearchService.all(searchSettings.copy(embedId = Some("test.url2"))).get + val search2 = draftConceptSearchService.all(searchSettings.copy(embedId = Some("test.id2"))).get search1.totalCount should be(1) search1.results.head.id should be(10) @@ -757,37 +730,36 @@ class DraftConceptSearchServiceTest extends ElasticsearchIntegrationSuite with T } test("search results should return copyright info") { - val Success(search) = - draftConceptSearchService.matchingQuery("hulk", searchSettings.copy(sort = Sort.ByRelevanceDesc)) - val hits = search.results + val search = draftConceptSearchService.matchingQuery("hulk", searchSettings.copy(sort = Sort.ByRelevanceDesc)).get + val hits = search.results hits.map(_.id) should equal(Seq(5)) hits.head.copyright.head.origin should be(Some("Gotham City")) hits.head.copyright.head.creators.length should be(1) } test("that sorting for status works") { - val Success(search) = - draftConceptSearchService.all(searchSettings.copy(withIdIn = List(1, 8, 9, 10), sort = Sort.ByStatusAsc)) + val search = + draftConceptSearchService.all(searchSettings.copy(withIdIn = List(1, 8, 9, 10), sort = Sort.ByStatusAsc)).get search.results.map(_.id) should be(Seq(8, 10, 1, 9)) - val Success(search2) = - draftConceptSearchService.all(searchSettings.copy(withIdIn = List(1, 8, 9, 10), sort = Sort.ByStatusDesc)) + val search2 = + draftConceptSearchService.all(searchSettings.copy(withIdIn = List(1, 8, 9, 10), sort = Sort.ByStatusDesc)).get search2.results.map(_.id) should be(Seq(9, 1, 10, 8)) } test("that filtering for conceptType works as expected") { { - val Success(search) = draftConceptSearchService.all(searchSettings.copy(conceptType = Some("concept"))) + val search = draftConceptSearchService.all(searchSettings.copy(conceptType = Some("concept"))).get search.totalCount should be(10) } { - val Success(search) = draftConceptSearchService.all(searchSettings.copy(conceptType = Some("gloss"))) + val search = draftConceptSearchService.all(searchSettings.copy(conceptType = Some("gloss"))).get search.totalCount should be(1) } } test("That searching for gloss data matches") { - val Success(search) = draftConceptSearchService.matchingQuery("glossorama", searchSettings) + val search = draftConceptSearchService.matchingQuery("glossorama", searchSettings).get search.totalCount should be(1) search.results.head.id should be(13) } diff --git a/concept-api/src/test/scala/no/ndla/conceptapi/service/search/PublishedConceptSearchServiceTest.scala b/concept-api/src/test/scala/no/ndla/conceptapi/service/search/PublishedConceptSearchServiceTest.scala index 5ac6b6cd90..eb27086fe6 100644 --- a/concept-api/src/test/scala/no/ndla/conceptapi/service/search/PublishedConceptSearchServiceTest.scala +++ b/concept-api/src/test/scala/no/ndla/conceptapi/service/search/PublishedConceptSearchServiceTest.scala @@ -18,22 +18,20 @@ import no.ndla.language.Language import no.ndla.scalatestsuite.ElasticsearchIntegrationSuite import java.time.LocalDateTime -import scala.util.Success import no.ndla.common.model.NDLADate import no.ndla.common.model.domain.concept.{Concept, ConceptContent, ConceptType, GlossData, VisualElement, WordClass} import no.ndla.mapping.License import no.ndla.search.model.domain.{Bucket, TermAggregation} class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite with TestEnvironment { - import props.{DefaultLanguage, DefaultPageSize} e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("http://localhost:9200")) - override val publishedConceptSearchService = new PublishedConceptSearchService - override val publishedConceptIndexService: PublishedConceptIndexService = new PublishedConceptIndexService { + override lazy val publishedConceptSearchService = new PublishedConceptSearchService + override lazy val publishedConceptIndexService: PublishedConceptIndexService = new PublishedConceptIndexService { override val indexShards = 1 } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService val byNcSa: DraftCopyright = DraftCopyright( Some(License.CC_BY_NC_SA.toString), @@ -183,7 +181,7 @@ class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite wi val searchSettings: search.SearchSettings = search.SearchSettings( withIdIn = List.empty, - searchLanguage = DefaultLanguage, + searchLanguage = props.DefaultLanguage, page = 1, pageSize = 10, sort = Sort.ByIdAsc, @@ -229,24 +227,23 @@ class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite wi "That getStartAtAndNumResults returns the correct calculated start at for page and page-size with default page-size" ) { val page = 74 - val expectedStartAt = (page - 1) * DefaultPageSize - publishedConceptSearchService.getStartAtAndNumResults(page, DefaultPageSize) should equal( - (expectedStartAt, DefaultPageSize) + val expectedStartAt = (page - 1) * props.DefaultPageSize + publishedConceptSearchService.getStartAtAndNumResults(page, props.DefaultPageSize) should equal( + (expectedStartAt, props.DefaultPageSize) ) } test("That getStartAtAndNumResults returns the correct calculated start at for page and page-size") { val page = 123 - val expectedStartAt = (page - 1) * DefaultPageSize - publishedConceptSearchService.getStartAtAndNumResults(page, DefaultPageSize) should equal( - (expectedStartAt, DefaultPageSize) + val expectedStartAt = (page - 1) * props.DefaultPageSize + publishedConceptSearchService.getStartAtAndNumResults(page, props.DefaultPageSize) should equal( + (expectedStartAt, props.DefaultPageSize) ) } test("That all returns all documents ordered by id ascending") { - val Success(results) = - publishedConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByIdAsc)) - val hits = results.results + val results = publishedConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByIdAsc)).get + val hits = results.results results.totalCount should be(11) hits.head.id should be(1) hits(1).id should be(2) @@ -262,18 +259,16 @@ class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite wi } test("That all returns all documents ordered by id descending") { - val Success(results) = - publishedConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByIdDesc)) - val hits = results.results + val results = publishedConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByIdDesc)).get + val hits = results.results results.totalCount should be(11) hits.head.id should be(12) hits.last.id should be(1) } test("That all returns all documents ordered by title ascending") { - val Success(results) = - publishedConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByTitleAsc)) - val hits = results.results + val results = publishedConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByTitleAsc)).get + val hits = results.results results.totalCount should be(11) hits.head.id should be(8) @@ -289,9 +284,8 @@ class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite wi } test("That all returns all documents ordered by title descending") { - val Success(results) = - publishedConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByTitleDesc)) - val hits = results.results + val results = publishedConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByTitleDesc)).get + val hits = results.results results.totalCount should be(11) hits.head.id should be(7) hits(1).id should be(10) @@ -308,27 +302,26 @@ class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite wi } test("That all returns all documents ordered by lastUpdated descending") { - val Success(results) = - publishedConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByLastUpdatedDesc)) + val results = + publishedConceptSearchService.all(searchSettings.copy(pageSize = 20, sort = Sort.ByLastUpdatedDesc)).get val hits = results.results results.totalCount should be(11) hits.map(_.id) should be(Seq(10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12)) } test("That all filtered by id only returns documents with the given ids") { - val Success(results) = - publishedConceptSearchService.all(searchSettings.copy(withIdIn = List(1, 3))) - val hits = results.results + val results = publishedConceptSearchService.all(searchSettings.copy(withIdIn = List(1, 3))).get + val hits = results.results results.totalCount should be(2) hits.head.id should be(1) hits.last.id should be(3) } test("That paging returns only hits on current page and not more than page-size") { - val Success(page1) = - publishedConceptSearchService.all(searchSettings.copy(page = 1, pageSize = 2, sort = Sort.ByTitleAsc)) - val Success(page2) = - publishedConceptSearchService.all(searchSettings.copy(page = 2, pageSize = 2, sort = Sort.ByTitleAsc)) + val page1 = + publishedConceptSearchService.all(searchSettings.copy(page = 1, pageSize = 2, sort = Sort.ByTitleAsc)).get + val page2 = + publishedConceptSearchService.all(searchSettings.copy(page = 2, pageSize = 2, sort = Sort.ByTitleAsc)).get val hits1 = page1.results page1.totalCount should be(11) @@ -346,8 +339,8 @@ class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite wi } test("That search matches title and content ordered by relevance descending") { - val Success(results) = - publishedConceptSearchService.matchingQuery("bil", searchSettings.copy(sort = Sort.ByRelevanceDesc)) + val results = + publishedConceptSearchService.matchingQuery("bil", searchSettings.copy(sort = Sort.ByRelevanceDesc)).get val hits = results.results results.totalCount should be(3) @@ -355,86 +348,96 @@ class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite wi } test("That search matches title") { - val Success(results) = - publishedConceptSearchService.matchingQuery("Pingvinen", searchSettings.copy(sort = Sort.ByTitleAsc)) + val results = + publishedConceptSearchService.matchingQuery("Pingvinen", searchSettings.copy(sort = Sort.ByTitleAsc)).get val hits = results.results results.totalCount should be(1) hits.head.id should be(2) } test("That search for title matches correct number of concepts") { - val Success(results) = - publishedConceptSearchService.matchingQuery("baldur har mareritt", searchSettings.copy(sort = Sort.ByTitleAsc)) + val results = publishedConceptSearchService + .matchingQuery("baldur har mareritt", searchSettings.copy(sort = Sort.ByTitleAsc)) + .get results.totalCount should be(2) } test("That search for title with exact parameter matches correct number of concepts") { - val Success(results) = - publishedConceptSearchService.matchingQuery( + val results = publishedConceptSearchService + .matchingQuery( "baldur har mareritt", searchSettings.copy(sort = Sort.ByTitleAsc, exactTitleMatch = true) ) + .get val hits = results.results results.totalCount should be(1) hits.head.id should be(8) - val Success(results2) = - publishedConceptSearchService.matchingQuery( - "Pingvinen", - searchSettings.copy(sort = Sort.ByTitleAsc, exactTitleMatch = true) - ) + val results2 = + publishedConceptSearchService + .matchingQuery( + "Pingvinen", + searchSettings.copy(sort = Sort.ByTitleAsc, exactTitleMatch = true) + ) + .get results2.totalCount should be(0) - val Success(results3) = - publishedConceptSearchService.matchingQuery( - "baldur har MARERITT", - searchSettings.copy(sort = Sort.ByTitleAsc, exactTitleMatch = true) - ) + val results3 = + publishedConceptSearchService + .matchingQuery( + "baldur har MARERITT", + searchSettings.copy(sort = Sort.ByTitleAsc, exactTitleMatch = true) + ) + .get val hits3 = results3.results results3.totalCount should be(1) hits3.head.id should be(8) } test("Searching with logical AND only returns results with all terms") { - val Success(search1) = - publishedConceptSearchService.matchingQuery("bilde + bil", searchSettings.copy(sort = Sort.ByTitleAsc)) + val search1 = + publishedConceptSearchService.matchingQuery("bilde + bil", searchSettings.copy(sort = Sort.ByTitleAsc)).get val hits1 = search1.results hits1.map(_.id) should equal(Seq(1, 3, 5)) - val Success(search2) = - publishedConceptSearchService.matchingQuery("batmen + bil", searchSettings.copy(sort = Sort.ByTitleAsc)) + val search2 = + publishedConceptSearchService.matchingQuery("batmen + bil", searchSettings.copy(sort = Sort.ByTitleAsc)).get val hits2 = search2.results hits2.map(_.id) should equal(Seq(1)) - val Success(search3) = - publishedConceptSearchService.matchingQuery( - "bil + bilde + -flaggermusmann", - searchSettings.copy(sort = Sort.ByTitleAsc) - ) + val search3 = + publishedConceptSearchService + .matchingQuery( + "bil + bilde + -flaggermusmann", + searchSettings.copy(sort = Sort.ByTitleAsc) + ) + .get val hits3 = search3.results hits3.map(_.id) should equal(Seq(3, 5)) - val Success(search4) = - publishedConceptSearchService.matchingQuery("bil + -hulken", searchSettings.copy(sort = Sort.ByTitleAsc)) + val search4 = + publishedConceptSearchService.matchingQuery("bil + -hulken", searchSettings.copy(sort = Sort.ByTitleAsc)).get val hits4 = search4.results hits4.map(_.id) should equal(Seq(1, 3)) } test("search in content should be ranked lower than title") { - val Success(search) = - publishedConceptSearchService.matchingQuery( - "mareritt + ragnarok", - searchSettings.copy(sort = Sort.ByRelevanceDesc) - ) + val search = + publishedConceptSearchService + .matchingQuery( + "mareritt + ragnarok", + searchSettings.copy(sort = Sort.ByRelevanceDesc) + ) + .get val hits = search.results hits.map(_.id) should equal(Seq(9, 8)) } test("Search should return language it is matched in") { - val Success(searchEn) = - publishedConceptSearchService.matchingQuery("Unrelated", searchSettings.copy(searchLanguage = "*")) - val Success(searchNb) = - publishedConceptSearchService.matchingQuery("Urelatert", searchSettings.copy(searchLanguage = "*")) + val searchEn = + publishedConceptSearchService.matchingQuery("Unrelated", searchSettings.copy(searchLanguage = "*")).get + val searchNb = + publishedConceptSearchService.matchingQuery("Urelatert", searchSettings.copy(searchLanguage = "*")).get searchEn.totalCount should be(1) searchEn.results.head.title.language should be("en") @@ -450,8 +453,8 @@ class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite wi } test("Search for all languages should return all concepts in correct language") { - val Success(search) = - publishedConceptSearchService.all(searchSettings.copy(searchLanguage = Language.AllLanguages, pageSize = 100)) + val search = + publishedConceptSearchService.all(searchSettings.copy(searchLanguage = Language.AllLanguages, pageSize = 100)).get val hits = search.results search.totalCount should equal(12) @@ -472,10 +475,10 @@ class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite wi } test("That searching with fallback parameter returns concept in language priority even if doesnt match on language") { - val Success(search) = - publishedConceptSearchService.all( - searchSettings.copy(withIdIn = List(9, 10, 11), searchLanguage = "en", fallback = true) - ) + val search = + publishedConceptSearchService + .all(searchSettings.copy(withIdIn = List(9, 10, 11), searchLanguage = "en", fallback = true)) + .get search.totalCount should equal(3) search.results.head.id should equal(9) @@ -490,17 +493,19 @@ class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite wi val pageSize = 2 val expectedIds = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).sliding(pageSize, pageSize).toList - val Success(initialSearch) = - publishedConceptSearchService.all( - searchSettings.copy(searchLanguage = "*", pageSize = pageSize, fallback = true, shouldScroll = true) - ) + val initialSearch = + publishedConceptSearchService + .all( + searchSettings.copy(searchLanguage = "*", pageSize = pageSize, fallback = true, shouldScroll = true) + ) + .get - val Success(scroll1) = publishedConceptSearchService.scroll(initialSearch.scrollId.get, "*") - val Success(scroll2) = publishedConceptSearchService.scroll(scroll1.scrollId.get, "*") - val Success(scroll3) = publishedConceptSearchService.scroll(scroll2.scrollId.get, "*") - val Success(scroll4) = publishedConceptSearchService.scroll(scroll3.scrollId.get, "*") - val Success(scroll5) = publishedConceptSearchService.scroll(scroll4.scrollId.get, "*") - val Success(scroll6) = publishedConceptSearchService.scroll(scroll5.scrollId.get, "*") + val scroll1 = publishedConceptSearchService.scroll(initialSearch.scrollId.get, "*").get + val scroll2 = publishedConceptSearchService.scroll(scroll1.scrollId.get, "*").get + val scroll3 = publishedConceptSearchService.scroll(scroll2.scrollId.get, "*").get + val scroll4 = publishedConceptSearchService.scroll(scroll3.scrollId.get, "*").get + val scroll5 = publishedConceptSearchService.scroll(scroll4.scrollId.get, "*").get + val scroll6 = publishedConceptSearchService.scroll(scroll5.scrollId.get, "*").get initialSearch.results.map(_.id) should be(expectedIds.head) scroll1.results.map(_.id) should be(expectedIds(1)) @@ -512,121 +517,137 @@ class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite wi } test("that filtering for tags works as expected and respects language/fallback") { - val Success(search) = publishedConceptSearchService.all(searchSettings.copy(tagsToFilterBy = Set("burugle"))) + val search = publishedConceptSearchService.all(searchSettings.copy(tagsToFilterBy = Set("burugle"))).get search.totalCount should be(1) search.results.map(_.id) should be(Seq(10)) - val Success(search1) = - publishedConceptSearchService.all(searchSettings.copy(searchLanguage = "*", tagsToFilterBy = Set("burugle"))) + val search1 = + publishedConceptSearchService.all(searchSettings.copy(searchLanguage = "*", tagsToFilterBy = Set("burugle"))).get search1.totalCount should be(1) search1.results.map(_.id) should be(Seq(10)) - val Success(search2) = - publishedConceptSearchService.all(searchSettings.copy(searchLanguage = "en", tagsToFilterBy = Set("burugle"))) + val search2 = + publishedConceptSearchService.all(searchSettings.copy(searchLanguage = "en", tagsToFilterBy = Set("burugle"))).get search2.totalCount should be(0) - val Success(search3) = - publishedConceptSearchService.all( - searchSettings.copy(searchLanguage = "en", fallback = true, tagsToFilterBy = Set("burugle")) - ) + val search3 = + publishedConceptSearchService + .all( + searchSettings.copy(searchLanguage = "en", fallback = true, tagsToFilterBy = Set("burugle")) + ) + .get search3.totalCount should be(1) search3.results.map(_.id) should be(Seq(10)) } test("that search on embedId matches visual element") { - val Success(search) = - publishedConceptSearchService.all( - searchSettings.copy(searchLanguage = Language.AllLanguages, embedId = Some("test.url")) - ) + val search = + publishedConceptSearchService + .all(searchSettings.copy(searchLanguage = Language.AllLanguages, embedId = Some("test.url"))) + .get search.totalCount should be(1) search.results.head.id should be(10) } test("that search on embedResource matches visual element") { - val Success(search) = - publishedConceptSearchService.all( - searchSettings.copy(searchLanguage = Language.AllLanguages, embedResource = List("brightcove")) - ) + val search = + publishedConceptSearchService + .all( + searchSettings.copy(searchLanguage = Language.AllLanguages, embedResource = List("brightcove")) + ) + .get search.totalCount should be(1) search.results.head.id should be(10) } test("that search on embedId matches meta image") { - val Success(search) = - publishedConceptSearchService.all( - searchSettings.copy(searchLanguage = Language.AllLanguages, embedId = Some("test.url")) - ) + val search = + publishedConceptSearchService + .all( + searchSettings.copy(searchLanguage = Language.AllLanguages, embedId = Some("test.url")) + ) + .get search.totalCount should be(1) search.results.head.id should be(10) } test("that search on query parameter as embedId matches meta image") { - val Success(search) = - publishedConceptSearchService.matchingQuery( - "test.url", - searchSettings.copy() - ) + val search = + publishedConceptSearchService + .matchingQuery( + "test.url", + searchSettings.copy() + ) + .get search.totalCount should be(1) search.results.head.id should be(10) } test("that search on query parameter as embedResource matches visual element") { - val Success(search) = - publishedConceptSearchService.matchingQuery( - "brightcove", - searchSettings.copy() - ) + val search = + publishedConceptSearchService + .matchingQuery( + "brightcove", + searchSettings.copy() + ) + .get search.totalCount should be(1) search.results.head.id should be(10) } test("that search on query parameter as embedId matches visual element") { - val Success(search) = - publishedConceptSearchService.matchingQuery( - "test.url", - searchSettings.copy() - ) + val search = + publishedConceptSearchService + .matchingQuery( + "test.url", + searchSettings.copy() + ) + .get search.totalCount should be(1) search.results.head.id should be(10) } test("that search on query parameter matches on concept id") { - val Success(search) = - publishedConceptSearchService.matchingQuery( - "2", - searchSettings.copy() - ) + val search = + publishedConceptSearchService + .matchingQuery( + "2", + searchSettings.copy() + ) + .get search.totalCount should be(1) search.results.head.id should be(2) } test("that search on embedId and embedResource only returns results with an embed matching both params") { - val Success(search) = - publishedConceptSearchService.all( - searchSettings - .copy(searchLanguage = Language.AllLanguages, embedResource = List("image"), embedId = Some("test.url")) - ) + val search = + publishedConceptSearchService + .all( + searchSettings + .copy(searchLanguage = Language.AllLanguages, embedResource = List("image"), embedId = Some("test.url")) + ) + .get search.totalCount should be(1) search.results.head.id should be(10) } test("That search on embed id supports embed with multiple id attributes") { - val Success(search1) = - publishedConceptSearchService.all( - searchSettings.copy(embedId = Some("test.url2")) - ) - val Success(search2) = - publishedConceptSearchService.all( - searchSettings.copy(embedId = Some("test.id2")) - ) + val search1 = + publishedConceptSearchService + .all(searchSettings.copy(embedId = Some("test.url2"))) + .get + val search2 = + publishedConceptSearchService + .all(searchSettings.copy(embedId = Some("test.id2"))) + .get search1.totalCount should be(1) search1.results.head.id should be(10) @@ -636,48 +657,58 @@ class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite wi } test("That search on exactTitleMatch only matches exact") { - val Success(search1) = - publishedConceptSearchService.matchingQuery( - "\"urelatert noe noe\"", - searchSettings.copy(fallback = true, exactTitleMatch = true) - ) + val search1 = + publishedConceptSearchService + .matchingQuery( + "\"urelatert noe noe\"", + searchSettings.copy(fallback = true, exactTitleMatch = true) + ) + .get search1.totalCount should be(0) - val Success(search2) = - publishedConceptSearchService.matchingQuery( - "et urelatert noe noe", - searchSettings.copy(fallback = true, exactTitleMatch = true) - ) + val search2 = + publishedConceptSearchService + .matchingQuery( + "et urelatert noe noe", + searchSettings.copy(fallback = true, exactTitleMatch = true) + ) + .get search2.totalCount should be(0) - val Success(search3) = - publishedConceptSearchService.matchingQuery( - "\"englando\"", - searchSettings.copy(fallback = true, exactTitleMatch = true) - ) + val search3 = + publishedConceptSearchService + .matchingQuery( + "\"englando\"", + searchSettings.copy(fallback = true, exactTitleMatch = true) + ) + .get search3.totalCount should be(1) search3.results.head.id should be(11) - val Success(search4) = - publishedConceptSearchService.matchingQuery( - "unrelated", - searchSettings.copy(fallback = true, exactTitleMatch = true) - ) + val search4 = + publishedConceptSearchService + .matchingQuery( + "unrelated", + searchSettings.copy(fallback = true, exactTitleMatch = true) + ) + .get search4.totalCount should be(1) search4.results.head.id should be(10) - val Success(search5) = - publishedConceptSearchService.matchingQuery( - "batmen", - searchSettings.copy(fallback = true, exactTitleMatch = true) - ) + val search5 = + publishedConceptSearchService + .matchingQuery( + "batmen", + searchSettings.copy(fallback = true, exactTitleMatch = true) + ) + .get search5.totalCount should be(0) } test("search results should return copyright info") { - val Success(search) = - publishedConceptSearchService.matchingQuery("hulk", searchSettings.copy(sort = Sort.ByRelevanceDesc)) + val search = + publishedConceptSearchService.matchingQuery("hulk", searchSettings.copy(sort = Sort.ByRelevanceDesc)).get val hits = search.results hits.map(_.id) should equal(Seq(5)) hits.head.copyright.head.origin should be(Some("Gotham City")) @@ -686,17 +717,17 @@ class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite wi test("filtering on conceptType should work as expected") { { - val Success(search) = publishedConceptSearchService.all(searchSettings.copy(conceptType = Some("concept"))) + val search = publishedConceptSearchService.all(searchSettings.copy(conceptType = Some("concept"))).get search.totalCount should be(10) } { - val Success(search) = publishedConceptSearchService.all(searchSettings.copy(conceptType = Some("gloss"))) + val search = publishedConceptSearchService.all(searchSettings.copy(conceptType = Some("gloss"))).get search.totalCount should be(1) } } test("That searching for gloss data matches") { - val Success(search) = publishedConceptSearchService.matchingQuery("glossorama", searchSettings) + val search = publishedConceptSearchService.matchingQuery("glossorama", searchSettings).get search.totalCount should be(1) search.results.head.id should be(12) } @@ -715,7 +746,7 @@ class PublishedConceptSearchServiceTest extends ElasticsearchIntegrationSuite wi ) ) - val Success(result) = publishedConceptSearchService.all(settings) + val result = publishedConceptSearchService.all(settings).get result.aggregations should be(Seq(expectedAggregations)) } diff --git a/concept-api/src/test/scala/no/ndla/conceptapi/validation/ContentValidatorTest.scala b/concept-api/src/test/scala/no/ndla/conceptapi/validation/ContentValidatorTest.scala index c51d107206..9cd2b92130 100644 --- a/concept-api/src/test/scala/no/ndla/conceptapi/validation/ContentValidatorTest.scala +++ b/concept-api/src/test/scala/no/ndla/conceptapi/validation/ContentValidatorTest.scala @@ -17,8 +17,8 @@ import no.ndla.conceptapi.{TestData, TestEnvironment, UnitSuite} import scala.util.{Failure, Success} class ContentValidatorTest extends UnitSuite with TestEnvironment { - override val converterService = new ConverterService - override val contentValidator = new ContentValidator + override lazy val converterService = new ConverterService + override lazy val contentValidator = new ContentValidator val baseConcept: Concept = TestData.domainConcept.copy(responsible = Some(Responsible("hei", TestData.today))) @@ -26,7 +26,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { val conceptToValidate = baseConcept.copy(title = Seq()) - val Failure(exception: ValidationException) = contentValidator.validateConcept(conceptToValidate) + val Failure(exception: ValidationException) = contentValidator.validateConcept(conceptToValidate): @unchecked exception.errors should be( Seq(ValidationMessage("title", "The field does not have any entries, whereas at least one is required.")) ) @@ -58,7 +58,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { val concept = baseConcept.copy(copyright = Some(DraftCopyright(Some("CC-BY-4.0"), None, Seq(), Seq(), Seq(), None, None, false)) ) - val Failure(exception: ValidationException) = contentValidator.validateConcept(concept) + val Failure(exception: ValidationException) = contentValidator.validateConcept(concept): @unchecked exception.errors should be( Seq(ValidationMessage("license.license", "At least one copyright holder is required when license is CC-BY-4.0")) ) diff --git a/database/src/main/scala/no/ndla/database/DBMigrator.scala b/database/src/main/scala/no/ndla/database/DBMigrator.scala index 48d1bc4345..6cb0fd3c74 100644 --- a/database/src/main/scala/no/ndla/database/DBMigrator.scala +++ b/database/src/main/scala/no/ndla/database/DBMigrator.scala @@ -18,7 +18,7 @@ import scala.jdk.CollectionConverters.* trait DBMigrator { this: DataSource & HasDatabaseProps => - val migrator: DBMigrator + lazy val migrator: DBMigrator case class DBMigrator(migrations: JavaMigration*) extends StrictLogging { private def logMigrationResult(result: MigrateResult): Unit = { diff --git a/database/src/main/scala/no/ndla/database/DBUtility.scala b/database/src/main/scala/no/ndla/database/DBUtility.scala index 87fbb41aef..9325d5cfdc 100644 --- a/database/src/main/scala/no/ndla/database/DBUtility.scala +++ b/database/src/main/scala/no/ndla/database/DBUtility.scala @@ -18,7 +18,7 @@ import scalikejdbc.* import scala.util.{Failure, Success, Try} trait DBUtility { - val DBUtil: DBUtility + lazy val DBUtil: DBUtility class DBUtility extends StrictLogging { def rollbackOnFailure[T](func: DBSession => Try[T]): Try[T] = { try { diff --git a/database/src/main/scala/no/ndla/database/DataSource.scala b/database/src/main/scala/no/ndla/database/DataSource.scala index f670835572..75c790af09 100644 --- a/database/src/main/scala/no/ndla/database/DataSource.scala +++ b/database/src/main/scala/no/ndla/database/DataSource.scala @@ -15,19 +15,17 @@ import scalikejdbc.{ConnectionPool, DataSourceConnectionPool} trait DataSource { this: HasDatabaseProps => - import props.* - - val dataSource: HikariDataSource + lazy val dataSource: HikariDataSource object DataSource extends StrictLogging { def getHikariDataSource: HikariDataSource = { val dataSourceConfig = new HikariConfig() - dataSourceConfig.setUsername(MetaUserName) - dataSourceConfig.setPassword(MetaPassword) - dataSourceConfig.setJdbcUrl(s"jdbc:postgresql://$MetaServer:$MetaPort/$MetaResource") + dataSourceConfig.setUsername(props.MetaUserName) + dataSourceConfig.setPassword(props.MetaPassword) + dataSourceConfig.setJdbcUrl(s"jdbc:postgresql://${props.MetaServer}:${props.MetaPort}/${props.MetaResource}") dataSourceConfig.setDriverClassName("org.postgresql.Driver") - dataSourceConfig.setSchema(MetaSchema) - dataSourceConfig.setMaximumPoolSize(MetaMaxConnections) + dataSourceConfig.setSchema(props.MetaSchema) + dataSourceConfig.setMaximumPoolSize(props.MetaMaxConnections) new HikariDataSource(dataSourceConfig) } diff --git a/database/src/main/scala/no/ndla/database/DatabaseProps.scala b/database/src/main/scala/no/ndla/database/DatabaseProps.scala index bc50aa804c..cc2b4bb5a3 100644 --- a/database/src/main/scala/no/ndla/database/DatabaseProps.scala +++ b/database/src/main/scala/no/ndla/database/DatabaseProps.scala @@ -28,5 +28,5 @@ trait DatabaseProps { } trait HasDatabaseProps { - val props: DatabaseProps + lazy val props: DatabaseProps } diff --git a/database/src/main/scala/no/ndla/database/TableMigration.scala b/database/src/main/scala/no/ndla/database/TableMigration.scala index 7483902127..1501d24291 100644 --- a/database/src/main/scala/no/ndla/database/TableMigration.scala +++ b/database/src/main/scala/no/ndla/database/TableMigration.scala @@ -13,7 +13,7 @@ import scalikejdbc.* abstract class TableMigration[ROW_DATA] extends BaseJavaMigration { val tableName: String - val whereClause: SQLSyntax + lazy val whereClause: SQLSyntax val chunkSize: Int = 1000 def extractRowData(rs: WrappedResultSet): ROW_DATA def updateRow(rowData: ROW_DATA)(implicit session: DBSession): Int @@ -33,7 +33,7 @@ abstract class TableMigration[ROW_DATA] extends BaseJavaMigration { override def migrate(context: Context): Unit = DB(context.getConnection) .autoClose(false) - .withinTx { session => migrateRows(session) } + .withinTx { session => migrateRows(using session) } private def migrateRows(implicit session: DBSession): Unit = { val count = countAllRows.get diff --git a/draft-api/src/main/scala/no/ndla/draftapi/ComponentRegistry.scala b/draft-api/src/main/scala/no/ndla/draftapi/ComponentRegistry.scala index 16bd5cb758..fad5a3e0ba 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/ComponentRegistry.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/ComponentRegistry.scala @@ -88,8 +88,8 @@ class ComponentRegistry(properties: DraftApiProperties) with SearchLanguage with V57__MigrateSavedSearch with V66__SetHideBylineForImagesNotCopyrighted { - override val props: DraftApiProperties = properties - override val migrator: DBMigrator = DBMigrator( + override lazy val props: DraftApiProperties = properties + override lazy val migrator: DBMigrator = DBMigrator( new R__RemoveEmptyStringLanguageFields(props), new R__RemoveStatusPublishedArticles(props), new R__SetArticleLanguageFromTaxonomy(props), @@ -101,50 +101,50 @@ class ComponentRegistry(properties: DraftApiProperties) new V66__SetHideBylineForImagesNotCopyrighted ) override lazy val dataSource: HikariDataSource = DataSource.getHikariDataSource - override val DBUtil: DBUtility = new DBUtility + override lazy val DBUtil: DBUtility = new DBUtility - lazy val draftRepository = new DraftRepository - lazy val userDataRepository = new UserDataRepository + override lazy val draftRepository = new DraftRepository + override lazy val userDataRepository = new UserDataRepository - lazy val articleSearchService = new ArticleSearchService - lazy val articleIndexService = new ArticleIndexService - lazy val tagSearchService = new TagSearchService - lazy val tagIndexService = new TagIndexService - lazy val grepCodesSearchService = new GrepCodesSearchService - lazy val grepCodesIndexService = new GrepCodesIndexService + override lazy val articleSearchService = new ArticleSearchService + override lazy val articleIndexService = new ArticleIndexService + override lazy val tagSearchService = new TagSearchService + override lazy val tagIndexService = new TagIndexService + override lazy val grepCodesSearchService = new GrepCodesSearchService + override lazy val grepCodesIndexService = new GrepCodesIndexService - lazy val converterService = new ConverterService - lazy val contentValidator = new ContentValidator() - lazy val importValidator = new ContentValidator() + override lazy val converterService = new ConverterService + override lazy val contentValidator = new ContentValidator() + override lazy val importValidator = new ContentValidator() - lazy val ndlaClient = new NdlaClient - lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient - lazy val searchConverterService = new SearchConverterService - lazy val readService = new ReadService - lazy val writeService = new WriteService - lazy val reindexClient = new ReindexClient + override lazy val ndlaClient = new NdlaClient + override lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient + override lazy val searchConverterService = new SearchConverterService + override lazy val readService = new ReadService + override lazy val writeService = new WriteService + override lazy val reindexClient = new ReindexClient - lazy val fileStorage = new FileStorageService + override lazy val fileStorage = new FileStorageService - lazy val s3Client = new NdlaS3Client(props.AttachmentStorageName, props.AttachmentStorageRegion) + override lazy val s3Client = new NdlaS3Client(props.AttachmentStorageName, props.AttachmentStorageRegion) var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer) - lazy val clock = new SystemClock - lazy val uuidUtil = new UUIDUtil + override lazy val clock = new SystemClock + override lazy val uuidUtil = new UUIDUtil - lazy val articleApiClient = new ArticleApiClient - lazy val searchApiClient = new SearchApiClient - lazy val taxonomyApiClient = new TaxonomyApiClient - lazy val learningpathApiClient = new LearningpathApiClient - lazy val h5pApiClient = new H5PApiClient - lazy val imageApiClient = new ImageApiClient + override lazy val articleApiClient = new ArticleApiClient + override lazy val searchApiClient = new SearchApiClient + override lazy val taxonomyApiClient = new TaxonomyApiClient + override lazy val learningpathApiClient = new LearningpathApiClient + override lazy val h5pApiClient = new H5PApiClient + override lazy val imageApiClient = new ImageApiClient - lazy val internController = new InternController - lazy val draftController = new DraftController - lazy val fileController = new FileController - lazy val userDataController = new UserDataController - lazy val healthController: TapirHealthController = new TapirHealthController + override lazy val internController = new InternController + override lazy val draftController = new DraftController + override lazy val fileController = new FileController + override lazy val userDataController = new UserDataController + override lazy val healthController: TapirHealthController = new TapirHealthController val swagger = new SwaggerController( List[TapirController]( diff --git a/draft-api/src/main/scala/no/ndla/draftapi/DraftApiProperties.scala b/draft-api/src/main/scala/no/ndla/draftapi/DraftApiProperties.scala index 4da9b7c5d2..46586d8f9f 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/DraftApiProperties.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/DraftApiProperties.scala @@ -17,7 +17,7 @@ import no.ndla.validation.ResourceType import scala.util.Properties.* trait Props extends HasBaseProps with HasDatabaseProps { - val props: DraftApiProperties + lazy val props: DraftApiProperties } class DraftApiProperties extends BaseProps with DatabaseProps with StrictLogging { diff --git a/draft-api/src/main/scala/no/ndla/draftapi/caching/Memoize1.scala b/draft-api/src/main/scala/no/ndla/draftapi/caching/Memoize1.scala index 7802002fd4..e46d92a38e 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/caching/Memoize1.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/caching/Memoize1.scala @@ -35,7 +35,7 @@ private[caching] class Memoize[R]( val task = new Runnable { def run(): Unit = renewCache() } - ex.scheduleAtFixedRate(task, 20, maxCacheAgeMs, TimeUnit.MILLISECONDS) + ex.scheduleAtFixedRate(task, 20, maxCacheAgeMs, TimeUnit.MILLISECONDS): Unit } def apply(): Option[R] = { @@ -51,17 +51,15 @@ private[caching] class Memoize[R]( } trait MemoizeHelpers { this: Props => - import props.ApiClientsCacheAgeInMs - object Memoize { def apply[R](f: () => R, shouldCacheResult: R => Boolean = (_: R) => true) = - new Memoize(ApiClientsCacheAgeInMs, f, autoRefreshCache = false, shouldCacheResult) + new Memoize(props.ApiClientsCacheAgeInMs, f, autoRefreshCache = false, shouldCacheResult) } object MemoizeAutoRenew { def apply[R](f: () => R, shouldCacheResult: R => Boolean = (_: R) => true) = - new Memoize(ApiClientsCacheAgeInMs, f, autoRefreshCache = true, shouldCacheResult) + new Memoize(props.ApiClientsCacheAgeInMs, f, autoRefreshCache = true, shouldCacheResult) } } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/controller/DraftController.scala b/draft-api/src/main/scala/no/ndla/draftapi/controller/DraftController.scala index 9635ff06e5..447e348d8a 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/controller/DraftController.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/controller/DraftController.scala @@ -9,7 +9,6 @@ package no.ndla.draftapi.controller import cats.implicits.* -import io.circe.generic.auto.* import no.ndla.common.model.api.CommaSeparatedList.* import no.ndla.common.model.api.{LanguageCode, LicenseDTO} import no.ndla.common.model.domain.ArticleType @@ -29,7 +28,6 @@ import no.ndla.network.tapir.auth.Permission.{ARTICLE_API_WRITE, DRAFT_API_WRITE import no.ndla.network.tapir.{DynamicHeaders, TapirController} import sttp.model.StatusCode import sttp.tapir.* -import sttp.tapir.generic.auto.* import sttp.tapir.server.ServerEndpoint import scala.util.{Failure, Success, Try} @@ -37,10 +35,9 @@ import scala.util.{Failure, Success, Try} trait DraftController { this: ReadService & WriteService & ArticleSearchService & SearchConverterService & ConverterService & ContentValidator & Props & ErrorHandling & TapirController => - val draftController: DraftController + lazy val draftController: DraftController class DraftController extends TapirController { - import props.{DefaultPageSize, InitialScrollContextKeywords} override val serviceName: String = "drafts" override val prefix: EndpointInput[Unit] = "draft-api" / "v1" / serviceName @@ -72,7 +69,7 @@ trait DraftController { .validate(Validator.min(1)) private val pageSize = query[Int]("page-size") .description("The number of search hits to display for each page.") - .default(DefaultPageSize) + .default(props.DefaultPageSize) .validate(Validator.min(0)) private val sort = query[Option[String]]("sort").description( """The sorting used on results. @@ -89,7 +86,7 @@ trait DraftController { .description("Fallback to existing language if language is specified.") .default(false) private val scrollId = query[Option[String]]("search-context").description( - s"""A unique string obtained from a search you want to keep scrolling in. To obtain one from a search, provide one of the following values: ${InitialScrollContextKeywords + s"""A unique string obtained from a search you want to keep scrolling in. To obtain one from a search, provide one of the following values: ${props.InitialScrollContextKeywords .mkString("[", ",", "]")}. |When scrolling, the parameters from the initial search is used, except in the case of '${this.language.name}' and '${this.fallback.name}'. |This value may change between scrolls. Always use the one in the latest scroll result (The context, if unused, dies after ${props.ElasticSearchScrollKeepAlive}). @@ -136,7 +133,7 @@ trait DraftController { orFunction: => Try[(ArticleSearchResultDTO, DynamicHeaders)] ): Try[(ArticleSearchResultDTO, DynamicHeaders)] = { scrollId match { - case Some(scroll) if !InitialScrollContextKeywords.contains(scroll) => + case Some(scroll) if !props.InitialScrollContextKeywords.contains(scroll) => articleSearchService.scroll(scroll, language.code) match { case Success(scrollResult) => val body = searchConverterService.asApiSearchResult(scrollResult) @@ -228,8 +225,8 @@ trait DraftController { .in(pageSize) .in(pageNo) .errorOut(errorOutputsFor(401, 403)) - .requirePermission(DRAFT_API_WRITE) .out(jsonBody[GrepCodesSearchResultDTO]) + .requirePermission(DRAFT_API_WRITE) .serverLogicPure { _ => { case (maybeQuery, pageSize, pageNo) => val query = maybeQuery.getOrElse("") @@ -274,7 +271,7 @@ trait DraftController { val sort = Sort.valueOf(maybeSort.getOrElse("")) val idList = articleIds.values val articleTypesFilter = articleTypes.values - val shouldScroll = scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = scrollId.exists(props.InitialScrollContextKeywords.contains) search( maybeQuery, @@ -308,13 +305,13 @@ trait DraftController { val query = searchParams.query val sort = searchParams.sort val license = searchParams.license - val pageSize = searchParams.pageSize.getOrElse(DefaultPageSize) + val pageSize = searchParams.pageSize.getOrElse(props.DefaultPageSize) val page = searchParams.page.getOrElse(1) val idList = searchParams.ids val articleTypesFilter = searchParams.articleTypes val fallback = searchParams.fallback.getOrElse(false) val grepCodes = searchParams.grepCodes - val shouldScroll = searchParams.scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = searchParams.scrollId.exists(props.InitialScrollContextKeywords.contains) search( query, @@ -357,14 +354,14 @@ trait DraftController { .in("ids") .summary("Fetch articles that matches ids parameter.") .description("Returns articles that matches ids parameter.") - .out(jsonBody[Seq[ArticleDTO]]) - .errorOut(errorOutputsFor(400, 401, 403)) - .requirePermission(DRAFT_API_WRITE) .in(articleIds) .in(fallback) .in(language) .in(pageSize) .in(pageNo) + .out(jsonBody[Seq[ArticleDTO]]) + .errorOut(errorOutputsFor(400, 401, 403)) + .requirePermission(DRAFT_API_WRITE) .serverLogicPure { _ => { case (articleIds, fallback, language, pageSize, page) => readService @@ -375,7 +372,6 @@ trait DraftController { page.toLong, pageSize.toLong ) - } } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/controller/FileController.scala b/draft-api/src/main/scala/no/ndla/draftapi/controller/FileController.scala index e4dd4c440b..21ba781a7f 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/controller/FileController.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/controller/FileController.scala @@ -19,10 +19,8 @@ import no.ndla.network.tapir.auth.Permission.DRAFT_API_WRITE import sttp.model.Part import sttp.tapir.EndpointInput import sttp.tapir.* -import io.circe.generic.auto.* import no.ndla.common.model.domain import no.ndla.network.tapir.TapirController -import sttp.tapir.generic.auto.* import sttp.tapir.server.ServerEndpoint import java.io.File @@ -30,7 +28,7 @@ import scala.util.{Failure, Success, Try} trait FileController { this: WriteService & ErrorHandling & Props & TapirController => - val fileController: FileController + lazy val fileController: FileController class FileController extends TapirController { override val serviceName: String = "files" @@ -43,8 +41,6 @@ trait FileController { deleteFile ) - case class FileForm(file: Part[File]) - def doWithStream[T](filePart: Part[File])(f: domain.UploadedFile => Try[T]): Try[T] = { val file = domain.UploadedFile.fromFilePart(filePart) if (file.fileSize > props.multipartFileSizeThresholdBytes) Failure(FileTooBigException()) diff --git a/draft-api/src/main/scala/no/ndla/draftapi/controller/InternController.scala b/draft-api/src/main/scala/no/ndla/draftapi/controller/InternController.scala index c7c81e4d15..6a1973b6f5 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/controller/InternController.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/controller/InternController.scala @@ -28,9 +28,7 @@ import no.ndla.network.tapir.auth.TokenUser import scalikejdbc.ReadOnlyAutoSession import sttp.model.StatusCode import sttp.tapir.server.ServerEndpoint -import io.circe.generic.auto.* import no.ndla.search.model.domain.ReindexResult -import sttp.tapir.generic.auto.* import java.util.concurrent.{Executors, TimeUnit} import scala.annotation.{tailrec, unused} @@ -41,11 +39,9 @@ import scala.util.{Failure, Success, Try} trait InternController { this: ReadService & WriteService & ConverterService & DraftRepository & IndexService & ArticleIndexService & TagIndexService & GrepCodesIndexService & ArticleApiClient & TapirController & Props => - val internController: InternController + lazy val internController: InternController class InternController extends TapirController with StrictLogging { - import props.{DraftSearchIndex, DraftTagSearchIndex} - override val prefix: EndpointInput[Unit] = "intern" override val enableSwagger = false private val stringInternalServerError = statusCode(StatusCode.InternalServerError).and(stringBody) @@ -116,8 +112,8 @@ trait InternController { def pluralIndex(n: Int) = if (n == 1) "1 index" else s"$n indexes" val indexes = for { - articleIndex <- Future { articleIndexService.findAllIndexes(DraftSearchIndex) } - tagIndex <- Future { tagIndexService.findAllIndexes(DraftTagSearchIndex) } + articleIndex <- Future { articleIndexService.findAllIndexes(props.DraftSearchIndex) } + tagIndex <- Future { tagIndexService.findAllIndexes(props.DraftTagSearchIndex) } } yield (articleIndex, tagIndex) val deleteResults: Seq[Try[?]] = Await.result(indexes, Duration(10, TimeUnit.MINUTES)) match { diff --git a/draft-api/src/main/scala/no/ndla/draftapi/controller/UserDataController.scala b/draft-api/src/main/scala/no/ndla/draftapi/controller/UserDataController.scala index 920f998526..3d020f849e 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/controller/UserDataController.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/controller/UserDataController.scala @@ -8,7 +8,6 @@ package no.ndla.draftapi.controller -import io.circe.generic.auto.* import no.ndla.draftapi.model.api.{ErrorHandling, UpdatedUserDataDTO, UserDataDTO} import no.ndla.draftapi.service.{ReadService, WriteService} import no.ndla.network.tapir.NoNullJsonPrinter.* @@ -16,12 +15,11 @@ import no.ndla.network.tapir.TapirController import no.ndla.network.tapir.TapirUtil.errorOutputsFor import no.ndla.network.tapir.auth.Permission.DRAFT_API_WRITE import sttp.tapir.* -import sttp.tapir.generic.auto.* import sttp.tapir.server.ServerEndpoint trait UserDataController { this: ReadService & WriteService & ErrorHandling & TapirController => - val userDataController: UserDataController + lazy val userDataController: UserDataController class UserDataController extends TapirController { override val serviceName: String = "user-data" diff --git a/draft-api/src/main/scala/no/ndla/draftapi/db/migration/V69__FixContributorTypes.scala b/draft-api/src/main/scala/no/ndla/draftapi/db/migration/V69__FixContributorTypes.scala index c3f4a2660b..2fd1e2c031 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/db/migration/V69__FixContributorTypes.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/db/migration/V69__FixContributorTypes.scala @@ -9,7 +9,6 @@ package no.ndla.draftapi.db.migration import io.circe.syntax.EncoderOps -import io.circe.generic.auto.* import io.circe.{ACursor, parser} import no.ndla.common.model.domain.{Author, ContributorType} import no.ndla.database.DocumentMigration @@ -57,3 +56,9 @@ class V69__FixContributorTypes extends DocumentMigration { } case class OldAuthor(`type`: String, name: String) +object OldAuthor { + import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} + import io.circe.{Decoder, Encoder} + implicit val encoder: Encoder[OldAuthor] = deriveEncoder + implicit val decoder: Decoder[OldAuthor] = deriveDecoder +} diff --git a/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__RemoveEmptyStringLanguageFields.scala b/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__RemoveEmptyStringLanguageFields.scala index 55b595b040..2bf9bb19d9 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__RemoveEmptyStringLanguageFields.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__RemoveEmptyStringLanguageFields.scala @@ -12,7 +12,7 @@ import no.ndla.draftapi.{DraftApiProperties, Props} import org.flywaydb.core.api.migration.{BaseJavaMigration, Context} class R__RemoveEmptyStringLanguageFields(properties: DraftApiProperties) extends BaseJavaMigration with Props { - override val props: DraftApiProperties = properties + override lazy val props: DraftApiProperties = properties override def getChecksum: Integer = 1 override def migrate(context: Context): Unit = {} } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__RemoveStatusPublishedArticles.scala b/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__RemoveStatusPublishedArticles.scala index d9124b8f43..3770e7fbea 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__RemoveStatusPublishedArticles.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__RemoveStatusPublishedArticles.scala @@ -12,7 +12,7 @@ import no.ndla.draftapi.{DraftApiProperties, Props} import org.flywaydb.core.api.migration.{BaseJavaMigration, Context} class R__RemoveStatusPublishedArticles(properties: DraftApiProperties) extends BaseJavaMigration with Props { - override val props: DraftApiProperties = properties + override lazy val props: DraftApiProperties = properties override def getChecksum: Integer = 0 override def migrate(context: Context): Unit = {} } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__SetArticleLanguageFromTaxonomy.scala b/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__SetArticleLanguageFromTaxonomy.scala index 952985dc89..6f8e646ca6 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__SetArticleLanguageFromTaxonomy.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__SetArticleLanguageFromTaxonomy.scala @@ -12,7 +12,7 @@ import no.ndla.draftapi.{DraftApiProperties, Props} import org.flywaydb.core.api.migration.{BaseJavaMigration, Context} class R__SetArticleLanguageFromTaxonomy(properties: DraftApiProperties) extends BaseJavaMigration with Props { - override val props: DraftApiProperties = properties + override lazy val props: DraftApiProperties = properties override def getChecksum: Integer = 1 override def migrate(context: Context): Unit = {} } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__SetArticleTypeFromTaxonomy.scala b/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__SetArticleTypeFromTaxonomy.scala index 90ac80ade6..c32be34303 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__SetArticleTypeFromTaxonomy.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/R__SetArticleTypeFromTaxonomy.scala @@ -12,7 +12,7 @@ import no.ndla.draftapi.{DraftApiProperties, Props} import org.flywaydb.core.api.migration.{BaseJavaMigration, Context} class R__SetArticleTypeFromTaxonomy(properties: DraftApiProperties) extends BaseJavaMigration with Props { - override val props: DraftApiProperties = properties + override lazy val props: DraftApiProperties = properties override def getChecksum: Integer = 0 override def migrate(context: Context): Unit = {} } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/V33__ConvertLanguageUnknown.scala b/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/V33__ConvertLanguageUnknown.scala index 1ffa5f4515..1637d21370 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/V33__ConvertLanguageUnknown.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/V33__ConvertLanguageUnknown.scala @@ -12,6 +12,6 @@ import no.ndla.draftapi.{DraftApiProperties, Props} import org.flywaydb.core.api.migration.{BaseJavaMigration, Context} class V33__ConvertLanguageUnknown(properties: DraftApiProperties) extends BaseJavaMigration with Props { - override val props: DraftApiProperties = properties + override lazy val props: DraftApiProperties = properties override def migrate(context: Context): Unit = {} } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/V57__MigrateSavedSearch.scala b/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/V57__MigrateSavedSearch.scala index fefb5d5b78..6bdbaeedc6 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/V57__MigrateSavedSearch.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/db/migrationwithdependencies/V57__MigrateSavedSearch.scala @@ -12,7 +12,7 @@ import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.syntax.EncoderOps import io.circe.{Decoder, Encoder, parser} import no.ndla.common.CirceUtil -import no.ndla.common.implicits.TryQuestionMark +import no.ndla.common.implicits.* import no.ndla.draftapi.integration.{Node, TaxonomyApiClient} import no.ndla.draftapi.model.api import no.ndla.draftapi.Props @@ -31,11 +31,9 @@ import scala.concurrent.duration.DurationInt trait V57__MigrateSavedSearch { this: TaxonomyApiClient with NdlaClient with Props => - import props.{TaxonomyUrl, TaxonomyVersionHeader, Environment, auth0ManagementClientId, auth0ManagementClientSecret} - class V57__MigrateSavedSearch extends BaseJavaMigration { - val auth0Domain = AuthUser.getAuth0HostForEnv(Environment) + val auth0Domain = AuthUser.getAuth0HostForEnv(props.Environment) val managementUri = uri"https://$auth0Domain/oauth/token" val auth0Audience = s"https://$auth0Domain/api/v2/" @@ -63,8 +61,8 @@ trait V57__MigrateSavedSearch { def getManagementToken(): Try[String] = { val inputBody = GetTokenBody( - client_id = auth0ManagementClientId, - client_secret = auth0ManagementClientSecret, + client_id = props.auth0ManagementClientId, + client_secret = props.auth0ManagementClientSecret, audience = auth0Audience, grant_type = "client_credentials" ) @@ -94,7 +92,7 @@ trait V57__MigrateSavedSearch { } } - def getEditors(managementToken: String): Try[Map[String, Auth0UserObject]] = { + def getEditors(managementToken: String): Try[Map[String, Auth0UserObject]] = permitTry { implicit val ec: ExecutionContextExecutorService = ExecutionContext.fromExecutorService(Executors.newWorkStealingPool(10)) val firstPage = fetchAuth0UsersByQuery(managementToken, 0).? @@ -117,7 +115,7 @@ trait V57__MigrateSavedSearch { lazy val managementToken = getManagementToken().get lazy val auth0Editors = getEditors(managementToken).get - private val TaxonomyApiEndpoint = s"$TaxonomyUrl/v1" + private val TaxonomyApiEndpoint = s"${props.TaxonomyUrl}/v1" private val taxonomyTimeout = 20.seconds def countAllRows(implicit session: DBSession): Option[Long] = { @@ -165,7 +163,7 @@ trait V57__MigrateSavedSearch { quickRequest .get(uri"$url".withParams(params: _*)) .readTimeout(taxonomyTimeout) - .header(TaxonomyVersionHeader, TaxonomyData.get), + .header(props.TaxonomyVersionHeader, TaxonomyData.get), None ) } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/integration/ArticleApiClient.scala b/draft-api/src/main/scala/no/ndla/draftapi/integration/ArticleApiClient.scala index fe535e2a64..fb9a091f1a 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/integration/ArticleApiClient.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/integration/ArticleApiClient.scala @@ -39,7 +39,7 @@ import scala.util.{Failure, Try} trait ArticleApiClient { this: NdlaClient & ConverterService & Props => - val articleApiClient: ArticleApiClient + lazy val articleApiClient: ArticleApiClient class ArticleApiClient(ArticleBaseUrl: String = s"http://${props.ArticleApiHost}") { private val InternalEndpoint = s"$ArticleBaseUrl/intern" diff --git a/draft-api/src/main/scala/no/ndla/draftapi/integration/H5PApiClient.scala b/draft-api/src/main/scala/no/ndla/draftapi/integration/H5PApiClient.scala index 7c04a06c08..0810970f25 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/integration/H5PApiClient.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/integration/H5PApiClient.scala @@ -25,7 +25,7 @@ import scala.util.{Failure, Success, Try} trait H5PApiClient { this: NdlaClient with Props => - val h5pApiClient: H5PApiClient + lazy val h5pApiClient: H5PApiClient class H5PApiClient extends StrictLogging { private val H5PApi = s"${props.H5PAddress}/v1" diff --git a/draft-api/src/main/scala/no/ndla/draftapi/integration/ImageApiClient.scala b/draft-api/src/main/scala/no/ndla/draftapi/integration/ImageApiClient.scala index e364f4f36e..bdd2fefa3f 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/integration/ImageApiClient.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/integration/ImageApiClient.scala @@ -20,7 +20,7 @@ import scala.util.Try trait ImageApiClient { this: NdlaClient & ConverterService & Props => - val imageApiClient: ImageApiClient + lazy val imageApiClient: ImageApiClient class ImageApiClient { private val Endpoint = s"http://${props.ImageApiHost}/image-api/v3/images" diff --git a/draft-api/src/main/scala/no/ndla/draftapi/integration/LearningpathApiClient.scala b/draft-api/src/main/scala/no/ndla/draftapi/integration/LearningpathApiClient.scala index 37d8d29256..f325d89875 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/integration/LearningpathApiClient.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/integration/LearningpathApiClient.scala @@ -21,7 +21,7 @@ import scala.util.Try trait LearningpathApiClient { this: NdlaClient with ConverterService with Props => - val learningpathApiClient: LearningpathApiClient + lazy val learningpathApiClient: LearningpathApiClient class LearningpathApiClient { private val Endpoint = s"http://${props.LearningpathApiHost}/learningpath-api/v2/learningpaths" diff --git a/draft-api/src/main/scala/no/ndla/draftapi/integration/ReindexClient.scala b/draft-api/src/main/scala/no/ndla/draftapi/integration/ReindexClient.scala index da407462b3..b63c3fc814 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/integration/ReindexClient.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/integration/ReindexClient.scala @@ -17,28 +17,26 @@ import sttp.client3.quick._ trait ReindexClient { this: Props => - val reindexClient: ReindexClient + lazy val reindexClient: ReindexClient class ReindexClient extends StrictLogging { - import props.internalApiUrls - private def reindexArticles() = { - val req = quickRequest.post(uri"${internalApiUrls("article-api")}/index") + val req = quickRequest.post(uri"${props.internalApiUrls("article-api")}/index") simpleHttpClient.send(req) } private def reindexAudios() = { - val req = quickRequest.post(uri"${internalApiUrls("audio-api")}/index") + val req = quickRequest.post(uri"${props.internalApiUrls("audio-api")}/index") simpleHttpClient.send(req) } private def reindexDrafts() = { - val req = quickRequest.post(uri"${internalApiUrls("draft-api")}/index") + val req = quickRequest.post(uri"${props.internalApiUrls("draft-api")}/index") simpleHttpClient.send(req) } private def reindexImages() = { - val req = quickRequest.post(uri"${internalApiUrls("image-api")}/index") + val req = quickRequest.post(uri"${props.internalApiUrls("image-api")}/index") simpleHttpClient.send(req) } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/integration/TaxonomyApiClient.scala b/draft-api/src/main/scala/no/ndla/draftapi/integration/TaxonomyApiClient.scala index 9a4421cc0a..b4b8f2f702 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/integration/TaxonomyApiClient.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/integration/TaxonomyApiClient.scala @@ -28,11 +28,10 @@ import scala.util.{Failure, Success, Try} trait TaxonomyApiClient { this: NdlaClient with Props => - val taxonomyApiClient: TaxonomyApiClient - import props.{DefaultLanguage, TaxonomyUrl, TaxonomyVersionHeader} + lazy val taxonomyApiClient: TaxonomyApiClient class TaxonomyApiClient extends StrictLogging { - private val TaxonomyApiEndpoint = s"$TaxonomyUrl/v1" + private val TaxonomyApiEndpoint = s"${props.TaxonomyUrl}/v1" private val taxonomyTimeout = 20.seconds def updateTaxonomyIfExists(articleId: Long, article: Draft, user: TokenUser): Try[Long] = { @@ -54,7 +53,7 @@ trait TaxonomyApiClient { * List of Resources or Topics that were updated if none failed. */ private def updateTaxonomy(nodes: Seq[Node], titles: Seq[Title], user: TokenUser): Try[List[Node]] = { - Language.findByLanguageOrBestEffort(titles, DefaultLanguage) match { + Language.findByLanguageOrBestEffort(titles, props.DefaultLanguage) match { case Some(title) => val updated = nodes.map(updateTitleAndTranslations(_, title, titles, user)) updated @@ -147,7 +146,7 @@ trait TaxonomyApiClient { quickRequest .get(uri"$url".withParams(params: _*)) .readTimeout(taxonomyTimeout) - .header(TaxonomyVersionHeader, TaxonomyData.get), + .header(props.TaxonomyVersionHeader, TaxonomyData.get), None ) } @@ -183,7 +182,7 @@ trait TaxonomyApiClient { .put(uri) .body(CirceUtil.toJsonString(data)) .readTimeout(taxonomyTimeout) - .header(TaxonomyVersionHeader, TaxonomyData.get) + .header(props.TaxonomyVersionHeader, TaxonomyData.get) .header("Content-Type", "application/json", replaceExisting = true) ndlaClient.fetchRawWithForwardedAuth(request, Some(user)) match { case Success(_) => Success(data) diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/AddMultipleNotesDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/AddMultipleNotesDTO.scala index 191028b145..49d3798bbf 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/AddMultipleNotesDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/AddMultipleNotesDTO.scala @@ -15,3 +15,13 @@ case class AddMultipleNotesDTO( @description("Objects for which notes should be added to which drafts") data: List[AddNoteDTO] ) + +object AddMultipleNotesDTO { + import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} + import io.circe.{Decoder, Encoder} + import sttp.tapir.generic.auto.* + + implicit val encoder: Encoder[AddMultipleNotesDTO] = deriveEncoder + implicit val decoder: Decoder[AddMultipleNotesDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[AddMultipleNotesDTO] = sttp.tapir.Schema.derivedSchema +} diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/AddNoteDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/AddNoteDTO.scala index aa328f3d67..3919a15ffa 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/AddNoteDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/AddNoteDTO.scala @@ -17,3 +17,13 @@ case class AddNoteDTO( @description("Notes to add to the draft") notes: List[String] ) + +object AddNoteDTO { + import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} + import io.circe.{Decoder, Encoder} + import sttp.tapir.generic.auto.* + + implicit val encoder: Encoder[AddNoteDTO] = deriveEncoder + implicit val decoder: Decoder[AddNoteDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[AddNoteDTO] = sttp.tapir.Schema.derivedSchema +} diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleContentDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleContentDTO.scala index 50596f06d8..ccf45ce00b 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleContentDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleContentDTO.scala @@ -11,6 +11,7 @@ package no.ndla.draftapi.model.api import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} import sttp.tapir.Schema.annotations.description +import sttp.tapir.Schema @description("The content of the article in the specified language") case class ArticleContentDTO( @@ -21,4 +22,5 @@ case class ArticleContentDTO( object ArticleContentDTO { implicit def encoder: Encoder[ArticleContentDTO] = deriveEncoder implicit def decoder: Decoder[ArticleContentDTO] = deriveDecoder + implicit def schema: Schema[ArticleContentDTO] = Schema.derived[ArticleContentDTO] } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleDTO.scala index f631f294a6..29328260af 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleDTO.scala @@ -22,50 +22,86 @@ import no.ndla.common.model.api.{ RevisionMetaDTO } import sttp.tapir.Schema.annotations.description +import sttp.tapir.Schema import no.ndla.common.model.domain.Priority -// format: off @description("Information about the article") case class ArticleDTO( - @description("The unique id of the article") id: Long, - @description("Link to article on old platform") oldNdlaUrl: Option[String], - @description("The revision number for the article") revision: Int, - @description("The status of this article") status: StatusDTO, - @description("Available titles for the article") title: Option[ArticleTitleDTO], - @description("The content of the article in available languages") content: Option[ArticleContentDTO], - @description("Describes the copyright information for the article") copyright: Option[DraftCopyrightDTO], - @description("Searchable tags for the article") tags: Option[ArticleTagDTO], - @description("Required libraries in order to render the article") requiredLibraries: Seq[RequiredLibraryDTO], - @description("A visual element article") visualElement: Option[VisualElementDTO], - @description("An introduction for the article") introduction: Option[ArticleIntroductionDTO], - @description("Meta description for the article") metaDescription: Option[ArticleMetaDescriptionDTO], - @description("Meta image for the article") metaImage: Option[ArticleMetaImageDTO], - @description("When the article was created") created: NDLADate, - @description("When the article was last updated") updated: NDLADate, - @description("By whom the article was last updated") updatedBy: String, - @description("When the article was last published") published: NDLADate, - @description("The type of article this is. Possible values are frontpage-article, standard, topic-article") articleType: String, - @description("The languages this article supports") supportedLanguages: Seq[String], - @description("The notes for this article draft") notes: Seq[EditorNoteDTO], - @description("The labels attached to this article; meant for editors.") editorLabels: Seq[String], - @description("A list of codes from GREP API connected to the article") grepCodes: Seq[String], - @description("A list of conceptIds connected to the article") conceptIds: Seq[Long], - @description("Value that dictates who gets to see the article. Possible values are: everyone/teacher") availability: String, - @description("A list of content related to the article") relatedContent: Seq[RelatedContent], - @description("A list of revisions planned for the article") revisions: Seq[RevisionMetaDTO], - @description("Object with data representing the editor responsible for this article") responsible: Option[ResponsibleDTO], - @description("The path to the frontpage article") slug: Option[String], - @description("Information about comments attached to the article") comments: Seq[CommentDTO], - @description("If the article should be prioritized. Possible values are prioritized, on-hold, unspecified") priority: Priority, - @description("If the article has been edited after last status or responsible change") started: Boolean, - @description("The quality evaluation of the article. Consist of a score from 1 to 5 and a comment.") qualityEvaluation : Option[QualityEvaluationDTO], - @description("The disclaimer of the article") disclaimer: Option[DisclaimerDTO] + @description("The unique id of the article") + id: Long, + @description("Link to article on old platform") + oldNdlaUrl: Option[String], + @description("The revision number for the article") + revision: Int, + @description("The status of this article") + status: StatusDTO, + @description("Available titles for the article") + title: Option[ArticleTitleDTO], + @description("The content of the article in available languages") + content: Option[ArticleContentDTO], + @description("Describes the copyright information for the article") + copyright: Option[DraftCopyrightDTO], + @description("Searchable tags for the article") + tags: Option[ArticleTagDTO], + @description("Required libraries in order to render the article") + requiredLibraries: Seq[RequiredLibraryDTO], + @description("A visual element article") + visualElement: Option[VisualElementDTO], + @description("An introduction for the article") + introduction: Option[ArticleIntroductionDTO], + @description("Meta description for the article") + metaDescription: Option[ArticleMetaDescriptionDTO], + @description("Meta image for the article") + metaImage: Option[ArticleMetaImageDTO], + @description("When the article was created") + created: NDLADate, + @description("When the article was last updated") + updated: NDLADate, + @description("By whom the article was last updated") + updatedBy: String, + @description("When the article was last published") + published: NDLADate, + @description("The type of article this is. Possible values are frontpage-article, standard, topic-article") + articleType: String, + @description("The languages this article supports") + supportedLanguages: Seq[String], + @description("The notes for this article draft") + notes: Seq[EditorNoteDTO], + @description("The labels attached to this article; meant for editors.") + editorLabels: Seq[String], + @description("A list of codes from GREP API connected to the article") + grepCodes: Seq[String], + @description("A list of conceptIds connected to the article") + conceptIds: Seq[Long], + @description("Value that dictates who gets to see the article. Possible values are: everyone/teacher") + availability: String, + @description("A list of content related to the article") + relatedContent: Seq[RelatedContent], + @description("A list of revisions planned for the article") + revisions: Seq[RevisionMetaDTO], + @description("Object with data representing the editor responsible for this article") + responsible: Option[ResponsibleDTO], + @description("The path to the frontpage article") + slug: Option[String], + @description("Information about comments attached to the article") + comments: Seq[CommentDTO], + @description("If the article should be prioritized. Possible values are prioritized, on-hold, unspecified") + priority: Priority, + @description("If the article has been edited after last status or responsible change") + started: Boolean, + @description("The quality evaluation of the article. Consist of a score from 1 to 5 and a comment.") + qualityEvaluation: Option[QualityEvaluationDTO], + @description("The disclaimer of the article") + disclaimer: Option[DisclaimerDTO] ) object ArticleDTO { - implicit def relatedContentEnc: Encoder[Either[RelatedContentLinkDTO, Long]] = eitherEncoder[RelatedContentLinkDTO, Long] - implicit def relatedContentDec: Decoder[Either[RelatedContentLinkDTO, Long]] = eitherDecoder[RelatedContentLinkDTO, Long] + implicit def relatedContentEnc: Encoder[Either[RelatedContentLinkDTO, Long]] = + eitherEncoder[RelatedContentLinkDTO, Long] + implicit def relatedContentDec: Decoder[Either[RelatedContentLinkDTO, Long]] = + eitherDecoder[RelatedContentLinkDTO, Long] implicit def encoder: Encoder[ArticleDTO] = deriveEncoder implicit def decoder: Decoder[ArticleDTO] = deriveDecoder + implicit def schema: Schema[ArticleDTO] = Schema.derived } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleDomainDumpDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleDomainDumpDTO.scala index 13ccd671c3..9158af1552 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleDomainDumpDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleDomainDumpDTO.scala @@ -18,3 +18,14 @@ case class ArticleDomainDumpDTO( @description("The number of results per page") pageSize: Int, @description("The search results") results: Seq[draft.Draft] ) + +object ArticleDomainDumpDTO { + import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} + import io.circe.{Decoder, Encoder} + import sttp.tapir.generic.auto.* + import sttp.tapir.Schema + + implicit val encoder: Encoder[ArticleDomainDumpDTO] = deriveEncoder + implicit val decoder: Decoder[ArticleDomainDumpDTO] = deriveDecoder + implicit def schema: Schema[ArticleDomainDumpDTO] = Schema.derivedSchema +} diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleIntroductionDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleIntroductionDTO.scala index 2ed1e83366..af11215cad 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleIntroductionDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleIntroductionDTO.scala @@ -14,14 +14,16 @@ import sttp.tapir.Schema.annotations.description @description("Description of the article introduction") case class ArticleIntroductionDTO( - @description("The introduction content") introduction: String, - @description("The html introduction content") htmlIntroduction: String, - @description( - "The ISO 639-1 language code describing which article translation this introduction belongs to" - ) language: String + @description("The introduction content") + introduction: String, + @description("The html introduction content") + htmlIntroduction: String, + @description("The ISO 639-1 language code describing which article translation this introduction belongs to") + language: String ) object ArticleIntroductionDTO { - implicit def encoder: Encoder[ArticleIntroductionDTO] = deriveEncoder - implicit def decoder: Decoder[ArticleIntroductionDTO] = deriveDecoder + implicit def encoder: Encoder[ArticleIntroductionDTO] = deriveEncoder + implicit def decoder: Decoder[ArticleIntroductionDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[ArticleIntroductionDTO] = sttp.tapir.Schema.derived[ArticleIntroductionDTO] } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleMetaDescriptionDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleMetaDescriptionDTO.scala index c32f926f30..1600a8fa96 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleMetaDescriptionDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleMetaDescriptionDTO.scala @@ -11,6 +11,7 @@ package no.ndla.draftapi.model.api import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} import sttp.tapir.Schema.annotations.description +import sttp.tapir.Schema @description("Meta description of the article") case class ArticleMetaDescriptionDTO( @@ -23,4 +24,5 @@ case class ArticleMetaDescriptionDTO( object ArticleMetaDescriptionDTO { implicit def encoder: Encoder[ArticleMetaDescriptionDTO] = deriveEncoder implicit def decoder: Decoder[ArticleMetaDescriptionDTO] = deriveDecoder + implicit def schema: Schema[ArticleMetaDescriptionDTO] = Schema.derived[ArticleMetaDescriptionDTO] } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleMetaImageDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleMetaImageDTO.scala index bea4a49fc5..c2b9dffa18 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleMetaImageDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleMetaImageDTO.scala @@ -14,14 +14,16 @@ import sttp.tapir.Schema.annotations.description @description("Meta description of the article") case class ArticleMetaImageDTO( - @description("The meta image") url: String, - @description("The meta image alt text") alt: String, - @description( - "The ISO 639-1 language code describing which article translation this meta image belongs to" - ) language: String + @description("The meta image") + url: String, + @description("The meta image alt text") + alt: String, + @description("The ISO 639-1 language code describing which article translation this meta image belongs to") + language: String ) object ArticleMetaImageDTO { - implicit def encoder: Encoder[ArticleMetaImageDTO] = deriveEncoder - implicit def decoder: Decoder[ArticleMetaImageDTO] = deriveDecoder + implicit def encoder: Encoder[ArticleMetaImageDTO] = deriveEncoder + implicit def decoder: Decoder[ArticleMetaImageDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[ArticleMetaImageDTO] = sttp.tapir.Schema.derived[ArticleMetaImageDTO] } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleRevisionHistoryDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleRevisionHistoryDTO.scala index bce15137a8..a004ff05b5 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleRevisionHistoryDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleRevisionHistoryDTO.scala @@ -12,8 +12,17 @@ import sttp.tapir.Schema.annotations.description @description("Information about article revision history") case class ArticleRevisionHistoryDTO( - @description("The revisions of an article, with the latest revision being the first in the list") revisions: Seq[ - ArticleDTO - ], - @description("Whether or not the current revision is safe to delete") canDeleteCurrentRevision: Boolean + @description("The revisions of an article, with the latest revision being the first in the list") + revisions: Seq[ArticleDTO], + @description("Whether or not the current revision is safe to delete") + canDeleteCurrentRevision: Boolean ) + +object ArticleRevisionHistoryDTO { + import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} + import io.circe.{Decoder, Encoder} + + implicit val encoder: Encoder[ArticleRevisionHistoryDTO] = deriveEncoder + implicit val decoder: Decoder[ArticleRevisionHistoryDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[ArticleRevisionHistoryDTO] = sttp.tapir.Schema.derived +} diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleSearchParamsDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleSearchParamsDTO.scala index ed54e7e5bd..1dd7873229 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleSearchParamsDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleSearchParamsDTO.scala @@ -43,4 +43,6 @@ case class ArticleSearchParamsDTO( object ArticleSearchParamsDTO { implicit def encoder: Encoder[ArticleSearchParamsDTO] = deriveEncoder implicit def decoder: Decoder[ArticleSearchParamsDTO] = deriveDecoder + import sttp.tapir.generic.auto.* + implicit def schema: sttp.tapir.Schema[ArticleSearchParamsDTO] = sttp.tapir.Schema.derivedSchema } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleSearchResultDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleSearchResultDTO.scala index 1a0bf683b6..28c6aca62b 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleSearchResultDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleSearchResultDTO.scala @@ -17,3 +17,13 @@ case class ArticleSearchResultDTO( @description("The number of results per page") pageSize: Int, @description("The search results") results: Seq[ArticleSummaryDTO] ) + +object ArticleSearchResultDTO { + import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} + import io.circe.{Decoder, Encoder} + import sttp.tapir.generic.auto.* + + implicit val encoder: Encoder[ArticleSearchResultDTO] = deriveEncoder + implicit val decoder: Decoder[ArticleSearchResultDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[ArticleSearchResultDTO] = sttp.tapir.Schema.derivedSchema +} diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleSummaryDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleSummaryDTO.scala index 5efcc2268c..32e5cfb1cf 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleSummaryDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleSummaryDTO.scala @@ -29,3 +29,14 @@ case class ArticleSummaryDTO( @description("The status of this article") status: StatusDTO, @description("When the article was last updated") updated: NDLADate ) + +object ArticleSummaryDTO { + import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} + import io.circe.{Decoder, Encoder} + + implicit val encoder: Encoder[ArticleSummaryDTO] = deriveEncoder + implicit val decoder: Decoder[ArticleSummaryDTO] = deriveDecoder + + import sttp.tapir.generic.auto.* + implicit def schema: sttp.tapir.Schema[ArticleSummaryDTO] = sttp.tapir.Schema.derivedSchema +} diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleTagDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleTagDTO.scala index 6d65aa4b08..510f3d1c0d 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleTagDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleTagDTO.scala @@ -19,6 +19,7 @@ case class ArticleTagDTO( ) object ArticleTagDTO { - implicit def encoder: Encoder[ArticleTagDTO] = deriveEncoder - implicit def decoder: Decoder[ArticleTagDTO] = deriveDecoder + implicit def encoder: Encoder[ArticleTagDTO] = deriveEncoder + implicit def decoder: Decoder[ArticleTagDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[ArticleTagDTO] = sttp.tapir.Schema.derived[ArticleTagDTO] } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleTitleDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleTitleDTO.scala index 6a173bb5b7..f202aabe88 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleTitleDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ArticleTitleDTO.scala @@ -20,6 +20,7 @@ case class ArticleTitleDTO( ) object ArticleTitleDTO { - implicit def encoder: Encoder[ArticleTitleDTO] = deriveEncoder - implicit def decoder: Decoder[ArticleTitleDTO] = deriveDecoder + implicit def encoder: Encoder[ArticleTitleDTO] = deriveEncoder + implicit def decoder: Decoder[ArticleTitleDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[ArticleTitleDTO] = sttp.tapir.Schema.derived[ArticleTitleDTO] } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ContentIdDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ContentIdDTO.scala index 90e5b9e696..4d47650a06 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/ContentIdDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/ContentIdDTO.scala @@ -11,6 +11,7 @@ package no.ndla.draftapi.model.api import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} import sttp.tapir.Schema.annotations.description +import sttp.tapir.Schema @description("Id for a single Article") case class ContentIdDTO(@description("The unique id of the article") id: Long) @@ -18,4 +19,5 @@ case class ContentIdDTO(@description("The unique id of the article") id: Long) object ContentIdDTO { implicit val encoder: Encoder[ContentIdDTO] = deriveEncoder implicit val decoder: Decoder[ContentIdDTO] = deriveDecoder + implicit def schema: Schema[ContentIdDTO] = Schema.derived } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/EditorNoteDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/EditorNoteDTO.scala index c6db1f7759..9460b5b369 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/EditorNoteDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/EditorNoteDTO.scala @@ -21,6 +21,7 @@ case class EditorNoteDTO( ) object EditorNoteDTO { - implicit def encoder: Encoder[EditorNoteDTO] = deriveEncoder - implicit def decoder: Decoder[EditorNoteDTO] = deriveDecoder + implicit def encoder: Encoder[EditorNoteDTO] = deriveEncoder + implicit def decoder: Decoder[EditorNoteDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[EditorNoteDTO] = sttp.tapir.Schema.derived[EditorNoteDTO] } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/FileForm.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/FileForm.scala new file mode 100644 index 0000000000..2a883d1a6d --- /dev/null +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/FileForm.scala @@ -0,0 +1,19 @@ +/* + * Part of NDLA draft-api + * Copyright (C) 2025 NDLA + * + * See LICENSE + * + */ + +package no.ndla.draftapi.model.api + +import java.io.File +import sttp.model.Part + +case class FileForm(file: Part[File]) + +object FileForm { + import sttp.tapir.Schema + implicit def schema: Schema[FileForm] = Schema.derived[FileForm] +} diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/MultiPartialPublishResultDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/MultiPartialPublishResultDTO.scala index ad2a07a13c..590b9e61d3 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/MultiPartialPublishResultDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/MultiPartialPublishResultDTO.scala @@ -8,6 +8,7 @@ package no.ndla.draftapi.model.api +import sttp.tapir.Schema import sttp.tapir.Schema.annotations.description @description("Single failed result") @@ -16,8 +17,26 @@ case class PartialPublishFailureDTO( @description("Error message") message: String ) +object PartialPublishFailureDTO { + import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} + import io.circe.{Decoder, Encoder} + + implicit def encoder: Encoder[PartialPublishFailureDTO] = deriveEncoder + implicit def decoder: Decoder[PartialPublishFailureDTO] = deriveDecoder + implicit def schema: Schema[PartialPublishFailureDTO] = Schema.derived[PartialPublishFailureDTO] +} + @description("A list of articles that were partial published to article-api") case class MultiPartialPublishResultDTO( @description("Successful ids") successes: Seq[Long], @description("Failed ids with error messages") failures: Seq[PartialPublishFailureDTO] ) + +object MultiPartialPublishResultDTO { + import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} + import io.circe.{Decoder, Encoder} + + implicit def encoder: Encoder[MultiPartialPublishResultDTO] = deriveEncoder + implicit def decoder: Decoder[MultiPartialPublishResultDTO] = deriveDecoder + implicit def schema: Schema[MultiPartialPublishResultDTO] = Schema.derived[MultiPartialPublishResultDTO] +} diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/NewArticleDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/NewArticleDTO.scala index b636532d14..d466665d64 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/NewArticleDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/NewArticleDTO.scala @@ -15,6 +15,7 @@ import no.ndla.common.model.NDLADate import no.ndla.common.model.api.{DraftCopyrightDTO, NewCommentDTO, RelatedContentLinkDTO, RevisionMetaDTO} import sttp.tapir.Schema.annotations.description import no.ndla.common.model.domain.Priority +import sttp.tapir.Schema // format: off @description("Information about the article") @@ -52,4 +53,5 @@ object NewArticleDTO { implicit def eitherDec: Decoder[Either[RelatedContentLinkDTO, Long]] = eitherDecoder[RelatedContentLinkDTO, Long] implicit def encoder: Encoder[NewArticleDTO] = deriveEncoder implicit def decoder: Decoder[NewArticleDTO] = deriveDecoder + implicit def schema: Schema[NewArticleDTO] = Schema.derived } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/PartialPublishArticle.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/PartialPublishArticle.scala index cc3694bdc8..d8a913566f 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/PartialPublishArticle.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/PartialPublishArticle.scala @@ -8,8 +8,11 @@ package no.ndla.draftapi.model.api -import enumeratum._ +import enumeratum.* +import sttp.tapir.Codec.PlainCodec +import sttp.tapir.Schema import sttp.tapir.Schema.annotations.description +import sttp.tapir.codec.enumeratum.* sealed trait PartialArticleFieldsDTO extends EnumEntry @@ -24,6 +27,10 @@ object PartialArticleFieldsDTO extends Enum[PartialArticleFieldsDTO] with CirceE case object tags extends PartialArticleFieldsDTO case object revisionDate extends PartialArticleFieldsDTO case object published extends PartialArticleFieldsDTO + + implicit def schema: Schema[PartialArticleFieldsDTO] = schemaForEnumEntry[PartialArticleFieldsDTO] + implicit def seqSchema: Schema[Seq[PartialArticleFieldsDTO]] = schema.asIterable + implicit def codec: PlainCodec[PartialArticleFieldsDTO] = plainCodecEnumEntry[PartialArticleFieldsDTO] } @description("Partial data about articles to publish in bulk") @@ -31,3 +38,12 @@ case class PartialBulkArticlesDTO( @description("A list of article ids to partially publish") articleIds: Seq[Long], @description("A list of fields that should be partially published") fields: Seq[PartialArticleFieldsDTO] ) + +object PartialBulkArticlesDTO { + import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} + import io.circe.{Decoder, Encoder} + + implicit def encoder: Encoder[PartialBulkArticlesDTO] = deriveEncoder + implicit def decoder: Decoder[PartialBulkArticlesDTO] = deriveDecoder + implicit def schema: Schema[PartialBulkArticlesDTO] = Schema.derived +} diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/QualityEvaluationDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/QualityEvaluationDTO.scala index a52efdce69..33f7f2395f 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/QualityEvaluationDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/QualityEvaluationDTO.scala @@ -11,6 +11,7 @@ package no.ndla.draftapi.model.api import io.circe.{Decoder, Encoder} import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import no.ndla.common.model.domain.draft.Grade +import sttp.tapir.Schema import sttp.tapir.Schema.annotations.description @description("Quality evaluation of the article") @@ -22,4 +23,5 @@ case class QualityEvaluationDTO( object QualityEvaluationDTO { implicit def encoder: Encoder[QualityEvaluationDTO] = deriveEncoder implicit def decoder: Decoder[QualityEvaluationDTO] = deriveDecoder + implicit def schema: Schema[QualityEvaluationDTO] = Schema.derived } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/RequiredLibraryDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/RequiredLibraryDTO.scala index 227748c0da..9bad9aea13 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/RequiredLibraryDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/RequiredLibraryDTO.scala @@ -10,6 +10,7 @@ package no.ndla.draftapi.model.api import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} +import sttp.tapir.Schema import sttp.tapir.Schema.annotations.description @description("Information about a library required to render the article") @@ -22,4 +23,5 @@ case class RequiredLibraryDTO( object RequiredLibraryDTO { implicit def encoder: Encoder[RequiredLibraryDTO] = deriveEncoder[RequiredLibraryDTO] implicit def decoder: Decoder[RequiredLibraryDTO] = deriveDecoder[RequiredLibraryDTO] + implicit def schema: Schema[RequiredLibraryDTO] = Schema.derived[RequiredLibraryDTO] } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/SearchResultDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/SearchResultDTO.scala index dbd44ddad8..828e6b2a68 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/SearchResultDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/SearchResultDTO.scala @@ -11,6 +11,7 @@ package no.ndla.draftapi.model.api import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} import sttp.tapir.Schema.annotations.description +import sttp.tapir.Schema @description("Information about search-results") case class SearchResultDTO( @@ -21,6 +22,13 @@ case class SearchResultDTO( @description("The search results") results: Seq[ArticleSummaryDTO] ) +object SearchResultDTO { + implicit def encoder: Encoder[SearchResultDTO] = deriveEncoder + implicit def decoder: Decoder[SearchResultDTO] = deriveDecoder + import sttp.tapir.generic.auto.* + implicit def schema: Schema[SearchResultDTO] = Schema.derivedSchema +} + @description("Information and metadata about codes from GREP API") case class GrepCodesSearchResultDTO( @description("The total number of codes from GREP API matching this query") totalCount: Long, @@ -29,6 +37,13 @@ case class GrepCodesSearchResultDTO( @description("The search results") results: Seq[String] ) +object GrepCodesSearchResultDTO { + implicit def encoder: Encoder[GrepCodesSearchResultDTO] = deriveEncoder + implicit def decoder: Decoder[GrepCodesSearchResultDTO] = deriveDecoder + import sttp.tapir.generic.auto.* + implicit def schema: Schema[GrepCodesSearchResultDTO] = Schema.derivedSchema +} + @description("Information about tags-search-results") case class TagsSearchResultDTO( @description("The total number of tags matching this query") totalCount: Long, @@ -38,6 +53,13 @@ case class TagsSearchResultDTO( @description("The search results") results: Seq[String] ) +object TagsSearchResultDTO { + implicit def encoder: Encoder[TagsSearchResultDTO] = deriveEncoder + implicit def decoder: Decoder[TagsSearchResultDTO] = deriveDecoder + import sttp.tapir.generic.auto.* + implicit def schema: Schema[TagsSearchResultDTO] = Schema.derivedSchema +} + @description("Information about articles") case class ArticleDumpDTO( @description("The total number of articles in the database") totalCount: Long, @@ -50,4 +72,6 @@ case class ArticleDumpDTO( object ArticleDumpDTO { implicit def encoder: Encoder[ArticleDumpDTO] = deriveEncoder implicit def decoder: Decoder[ArticleDumpDTO] = deriveDecoder + import sttp.tapir.generic.auto.* + implicit def schema: Schema[ArticleDumpDTO] = Schema.derivedSchema } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/StatusDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/StatusDTO.scala index 693fe0ab7a..61b2885c66 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/StatusDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/StatusDTO.scala @@ -18,6 +18,7 @@ case class StatusDTO( ) object StatusDTO { - implicit def encoder: Encoder[StatusDTO] = deriveEncoder - implicit def decoder: Decoder[StatusDTO] = deriveDecoder + implicit def encoder: Encoder[StatusDTO] = deriveEncoder + implicit def decoder: Decoder[StatusDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[StatusDTO] = sttp.tapir.Schema.derived[StatusDTO] } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/UpdatedArticleDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/UpdatedArticleDTO.scala index 198967a936..d4ac18001b 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/UpdatedArticleDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/UpdatedArticleDTO.scala @@ -16,46 +16,75 @@ import no.ndla.common.model.api.{ DraftCopyrightDTO, RelatedContent, RelatedContentLinkDTO, + RevisionMetaDTO, UpdateOrDelete, - UpdatedCommentDTO, - RevisionMetaDTO + UpdatedCommentDTO } import sttp.tapir.Schema.annotations.description import no.ndla.common.model.domain.Priority +import sttp.tapir.Schema -// format: off @description("Information about the article") case class UpdatedArticleDTO( - @description("The revision number for the article") revision: Int, - @description("The chosen language") language: Option[String], - @description("The title of the article") title: Option[String], - @description("The status of the article") status: Option[String], - @description("The date the article is published") published: Option[NDLADate], - @description("The content of the article") content: Option[String], - @description("Searchable tags") tags: Option[Seq[String]], - @description("An introduction") introduction: Option[String], - @description("A meta description") metaDescription: Option[String], - @description("An image-api ID for the article meta image") metaImage: UpdateOrDelete[NewArticleMetaImageDTO], - @description("A visual element for the article. May be anything from an image to a video or H5P") visualElement: Option[String], - @description("Describes the copyright information for the article") copyright: Option[DraftCopyrightDTO], - @description("Required libraries in order to render the article") requiredLibraries: Option[Seq[RequiredLibraryDTO]], - @description("The type of article this is. Possible values are frontpage-article, standard, topic-article") articleType: Option[String], - @description("The notes for this article draft") notes: Option[Seq[String]], - @description("The labels attached to this article; meant for editors.") editorLabels: Option[Seq[String]], - @description("A list of codes from GREP API connected to the article") grepCodes: Option[Seq[String]], - @description("A list of conceptIds connected to the article") conceptIds: Option[Seq[Long]], - @description("Stores the new article as a separate version. Useful when making big changes that should be revertable.") createNewVersion: Option[Boolean], - @description("Value that dictates who gets to see the article. Possible values are: everyone/teacher") availability: Option[String], - @description("A list of content related to the article") relatedContent: Option[Seq[Either[RelatedContentLinkDTO, Long]]], - @description("A list of all revisions of the article") revisionMeta: Option[Seq[RevisionMetaDTO]], - @description("NDLA ID representing the editor responsible for this article") responsibleId: UpdateOrDelete[String], - @description("The path to the frontpage article") slug: Option[String], - @description("Information about a comment attached to an article") comments: Option[List[UpdatedCommentDTO]], - @description("If the article should be prioritized. Possible values are prioritized, on-hold, unspecified") priority: Option[Priority], - @description("The quality evaluation of the article. Consist of a score from 1 to 5 and a comment.") qualityEvaluation : Option[QualityEvaluationDTO], - @description("The disclaimer of the article") disclaimer: Option[String] + @description("The revision number for the article") + revision: Int, + @description("The chosen language") + language: Option[String], + @description("The title of the article") + title: Option[String], + @description("The status of the article") + status: Option[String], + @description("The date the article is published") + published: Option[NDLADate], + @description("The content of the article") + content: Option[String], + @description("Searchable tags") + tags: Option[Seq[String]], + @description("An introduction") + introduction: Option[String], + @description("A meta description") + metaDescription: Option[String], + @description("An image-api ID for the article meta image") + metaImage: UpdateOrDelete[NewArticleMetaImageDTO], + @description("A visual element for the article. May be anything from an image to a video or H5P") + visualElement: Option[String], + @description("Describes the copyright information for the article") + copyright: Option[DraftCopyrightDTO], + @description("Required libraries in order to render the article") + requiredLibraries: Option[Seq[RequiredLibraryDTO]], + @description("The type of article this is. Possible values are frontpage-article, standard, topic-article") + articleType: Option[String], + @description("The notes for this article draft") + notes: Option[Seq[String]], + @description("The labels attached to this article; meant for editors.") + editorLabels: Option[Seq[String]], + @description("A list of codes from GREP API connected to the article") + grepCodes: Option[Seq[String]], + @description("A list of conceptIds connected to the article") + conceptIds: Option[Seq[Long]], + @description( + "Stores the new article as a separate version. Useful when making big changes that should be revertable." + ) + createNewVersion: Option[Boolean], + @description("Value that dictates who gets to see the article. Possible values are: everyone/teacher") + availability: Option[String], + @description("A list of content related to the article") + relatedContent: Option[Seq[Either[RelatedContentLinkDTO, Long]]], + @description("A list of all revisions of the article") + revisionMeta: Option[Seq[RevisionMetaDTO]], + @description("NDLA ID representing the editor responsible for this article") + responsibleId: UpdateOrDelete[String], + @description("The path to the frontpage article") + slug: Option[String], + @description("Information about a comment attached to an article") + comments: Option[List[UpdatedCommentDTO]], + @description("If the article should be prioritized. Possible values are prioritized, on-hold, unspecified") + priority: Option[Priority], + @description("The quality evaluation of the article. Consist of a score from 1 to 5 and a comment.") + qualityEvaluation: Option[QualityEvaluationDTO], + @description("The disclaimer of the article") + disclaimer: Option[String] ) -// format: on object UpdatedArticleDTO { implicit def relatedContentEncoder: Encoder[RelatedContent] = eitherEncoder[RelatedContentLinkDTO, Long] @@ -63,4 +92,5 @@ object UpdatedArticleDTO { implicit def encoder: Encoder[UpdatedArticleDTO] = deriveEncoder[UpdatedArticleDTO] implicit def decoder: Decoder[UpdatedArticleDTO] = deriveDecoder[UpdatedArticleDTO] + implicit def schema: Schema[UpdatedArticleDTO] = Schema.derived } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/UpdatedUserDataDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/UpdatedUserDataDTO.scala index c941749321..6a054e7605 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/UpdatedUserDataDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/UpdatedUserDataDTO.scala @@ -17,3 +17,13 @@ case class UpdatedUserDataDTO( @description("User's last edited concepts") latestEditedConcepts: Option[Seq[String]], @description("User's favorite subjects") favoriteSubjects: Option[Seq[String]] ) + +object UpdatedUserDataDTO { + import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} + import io.circe.{Decoder, Encoder} + import sttp.tapir.generic.auto.* + + implicit val encoder: Encoder[UpdatedUserDataDTO] = deriveEncoder + implicit val decoder: Decoder[UpdatedUserDataDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[UpdatedUserDataDTO] = sttp.tapir.Schema.derivedSchema +} diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/UploadedFileDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/UploadedFileDTO.scala index b9b0d5d388..96476ce055 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/UploadedFileDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/UploadedFileDTO.scala @@ -10,6 +10,7 @@ package no.ndla.draftapi.model.api import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} import sttp.tapir.Schema.annotations.description +import sttp.tapir.Schema @description("Information about the uploaded file") case class UploadedFileDTO( @@ -22,4 +23,6 @@ case class UploadedFileDTO( object UploadedFileDTO { implicit val encoder: Encoder[UploadedFileDTO] = deriveEncoder implicit val decoder: Decoder[UploadedFileDTO] = deriveDecoder + import sttp.tapir.generic.auto.* + implicit def schema: Schema[UploadedFileDTO] = Schema.derivedSchema } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/UserDataDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/UserDataDTO.scala index 28e1b4bc7f..b72d3cd263 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/UserDataDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/UserDataDTO.scala @@ -18,3 +18,13 @@ case class UserDataDTO( @description("User's last edited concepts") latestEditedConcepts: Option[Seq[String]], @description("User's favorite subjects") favoriteSubjects: Option[Seq[String]] ) + +object UserDataDTO { + import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} + import io.circe.{Decoder, Encoder} + import sttp.tapir.generic.auto.* + + implicit val encoder: Encoder[UserDataDTO] = deriveEncoder + implicit val decoder: Decoder[UserDataDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[UserDataDTO] = sttp.tapir.Schema.derivedSchema +} diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/api/VisualElementDTO.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/api/VisualElementDTO.scala index 6778eadc62..3d71e2923a 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/api/VisualElementDTO.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/api/VisualElementDTO.scala @@ -14,15 +14,14 @@ import sttp.tapir.Schema.annotations.description @description("Description of a visual element") case class VisualElementDTO( - @description( - "Html containing the visual element. May contain any legal html element, including the embed-tag" - ) visualElement: String, - @description( - "The ISO 639-1 language code describing which article translation this visual element belongs to" - ) language: String + @description("Html containing the visual element. May contain any legal html element, including the embed-tag") + visualElement: String, + @description("The ISO 639-1 language code describing which article translation this visual element belongs to") + language: String ) object VisualElementDTO { - implicit def encoder: Encoder[VisualElementDTO] = deriveEncoder - implicit def decoder: Decoder[VisualElementDTO] = deriveDecoder + implicit def encoder: Encoder[VisualElementDTO] = deriveEncoder + implicit def decoder: Decoder[VisualElementDTO] = deriveDecoder + implicit def schema: sttp.tapir.Schema[VisualElementDTO] = sttp.tapir.Schema.derived[VisualElementDTO] } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/domain/ArticleIds.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/domain/ArticleIds.scala index 4f602bd8bc..993f2625cb 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/domain/ArticleIds.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/domain/ArticleIds.scala @@ -9,3 +9,14 @@ package no.ndla.draftapi.model.domain case class ArticleIds(articleId: Long, externalId: List[String], importId: Option[String] = None) + +object ArticleIds { + import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} + import io.circe.{Decoder, Encoder} + import sttp.tapir.generic.auto.* + import sttp.tapir.Schema + + implicit val encoder: Encoder[ArticleIds] = deriveEncoder + implicit val decoder: Decoder[ArticleIds] = deriveDecoder + implicit def schema: Schema[ArticleIds] = Schema.derivedSchema +} diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/domain/ImportId.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/domain/ImportId.scala index b525325aac..39be4946f9 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/domain/ImportId.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/domain/ImportId.scala @@ -9,3 +9,14 @@ package no.ndla.draftapi.model.domain case class ImportId(importId: Option[String]) + +object ImportId { + import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} + import io.circe.{Decoder, Encoder} + import sttp.tapir.generic.auto.* + import sttp.tapir.Schema + + implicit val encoder: Encoder[ImportId] = deriveEncoder + implicit val decoder: Decoder[ImportId] = deriveDecoder + implicit def schema: Schema[ImportId] = Schema.derivedSchema +} diff --git a/draft-api/src/main/scala/no/ndla/draftapi/model/domain/Sort.scala b/draft-api/src/main/scala/no/ndla/draftapi/model/domain/Sort.scala index 59b9ff562a..efdea51a01 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/model/domain/Sort.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/model/domain/Sort.scala @@ -37,6 +37,6 @@ object Sort extends Enum[Sort] with CirceEnum[Sort] { } } - implicit val schema: Schema[Sort] = schemaForEnumEntry[Sort] - implicit val codec: PlainCodec[Sort] = plainCodecEnumEntry[Sort] + implicit def schema: Schema[Sort] = schemaForEnumEntry[Sort] + implicit def codec: PlainCodec[Sort] = plainCodecEnumEntry[Sort] } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/repository/DraftRepository.scala b/draft-api/src/main/scala/no/ndla/draftapi/repository/DraftRepository.scala index 06415ff1ea..205f54f600 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/repository/DraftRepository.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/repository/DraftRepository.scala @@ -24,7 +24,7 @@ import scala.util.{Failure, Success, Try} trait DraftRepository { this: DataSource & ErrorHandling & Clock => - val draftRepository: DraftRepository + lazy val draftRepository: DraftRepository class DraftRepository extends StrictLogging with Repository[Draft] { def insert(article: Draft)(implicit session: DBSession): Draft = { @@ -227,7 +227,7 @@ trait DraftRepository { private def externalIdsFromResultSet(wrappedResultSet: WrappedResultSet): List[String] = { Option(wrappedResultSet.array("external_id")) .map(_.getArray.asInstanceOf[Array[String]]) - .getOrElse(Array.empty) + .getOrElse(Array.empty[String]) .toList .flatMap(Option(_)) } @@ -242,7 +242,7 @@ trait DraftRepository { private def externalSubjectIdsFromResultSet(wrappedResultSet: WrappedResultSet): List[String] = { Option(wrappedResultSet.array("external_subject_id")) .map(_.getArray.asInstanceOf[Array[String]]) - .getOrElse(Array.empty) + .getOrElse(Array.empty[String]) .toList .flatMap(Option(_)) } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/repository/UserDataRepository.scala b/draft-api/src/main/scala/no/ndla/draftapi/repository/UserDataRepository.scala index f62533ceb3..1375596b78 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/repository/UserDataRepository.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/repository/UserDataRepository.scala @@ -20,7 +20,7 @@ import scala.util.{Success, Try} trait UserDataRepository { this: DataSource => - val userDataRepository: UserDataRepository + lazy val userDataRepository: UserDataRepository class UserDataRepository extends StrictLogging { def insert(userData: UserData)(implicit session: DBSession = AutoSession): Try[UserData] = { @@ -44,11 +44,11 @@ trait UserDataRepository { dataObject.setType("jsonb") dataObject.setValue(CirceUtil.toJsonString(userData)) - sql""" + val _ = sql""" update ${UserData.table} set document=$dataObject where user_id=${userData.userId} - """.update(): Unit + """.update() logger.info(s"Updated user data ${userData.userId}") Success(userData) diff --git a/draft-api/src/main/scala/no/ndla/draftapi/service/ConverterService.scala b/draft-api/src/main/scala/no/ndla/draftapi/service/ConverterService.scala index bd02d31271..2f5a25f9b7 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/service/ConverterService.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/service/ConverterService.scala @@ -13,7 +13,7 @@ import com.typesafe.scalalogging.StrictLogging import no.ndla.common.configuration.Constants.EmbedTagName import no.ndla.common.converter.CommonConverter import no.ndla.common.errors.ValidationException -import no.ndla.common.implicits.TryQuestionMark +import no.ndla.common.implicits.* import no.ndla.common.model.api.{Delete, DisclaimerDTO, DraftCopyrightDTO, Missing, ResponsibleDTO, UpdateWith} import no.ndla.common.model.domain.{ArticleContent, Priority, Responsible} import no.ndla.common.model.domain.draft.DraftStatus.PLANNED @@ -42,11 +42,9 @@ import scala.util.{Failure, Success, Try} trait ConverterService { this: Clock & DraftRepository & ArticleApiClient & StateTransitionRules & WriteService & UUIDUtil & CommonConverter & Props => - val converterService: ConverterService + lazy val converterService: ConverterService class ConverterService extends StrictLogging { - import props.externalApiUrls - def toDomainArticle(newArticleId: Long, newArticle: api.NewArticleDTO, user: TokenUser): Try[Draft] = { val domainTitles = Seq(common.Title(newArticle.title, newArticle.language)) val domainContent = newArticle.content @@ -356,7 +354,7 @@ trait ConverterService { private def toApiArticleMetaImage(metaImage: common.ArticleMetaImage): api.ArticleMetaImageDTO = { api.ArticleMetaImageDTO( - s"${externalApiUrls("raw-image")}/${metaImage.imageId}", + s"${props.externalApiUrls("raw-image")}/${metaImage.imageId}", metaImage.altText, metaImage.language ) @@ -623,9 +621,9 @@ trait ConverterService { ) .getOrElse(toMergeInto.metaImage) - def toDomainArticle(toMergeInto: Draft, article: api.UpdatedArticleDTO, user: TokenUser): Try[Draft] = { + def toDomainArticle(toMergeInto: Draft, article: api.UpdatedArticleDTO, user: TokenUser): Try[Draft] = permitTry { if (article.language.isEmpty && languageFieldIsDefined(article)) - return Failure(ValidationException("language", "This field must be specified when updating language fields")) + Failure(ValidationException("language", "This field must be specified when updating language fields")).? val isNewLanguage = article.language.exists(l => !toMergeInto.supportedLanguages.contains(l)) val createdDate = toMergeInto.created diff --git a/draft-api/src/main/scala/no/ndla/draftapi/service/FileStorageService.scala b/draft-api/src/main/scala/no/ndla/draftapi/service/FileStorageService.scala index 8dcbd19dca..24c5be827a 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/service/FileStorageService.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/service/FileStorageService.scala @@ -18,7 +18,7 @@ import scala.util.Try trait FileStorageService { this: NdlaS3Client with Props => - val fileStorage: FileStorageService + lazy val fileStorage: FileStorageService class FileStorageService extends StrictLogging { val resourceDirectory = "resources" diff --git a/draft-api/src/main/scala/no/ndla/draftapi/service/ReadService.scala b/draft-api/src/main/scala/no/ndla/draftapi/service/ReadService.scala index 8a853abe5f..5fb8949313 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/service/ReadService.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/service/ReadService.scala @@ -34,14 +34,12 @@ import scalikejdbc.ReadOnlyAutoSession import scala.jdk.CollectionConverters.* import scala.math.max -import scala.util.{Failure, Success, Try} +import scala.util.{Failure, Success, Try, boundary} trait ReadService { this: DraftRepository & ConverterService & ArticleSearchService & TagSearchService & GrepCodesSearchService & SearchConverterService & UserDataRepository & WriteService & Props & MemoizeHelpers & DBUtility => - val readService: ReadService - - import props.* + lazy val readService: ReadService class ReadService { @@ -141,7 +139,7 @@ trait ReadService { typeAndPathOption match { case Some((resourceType, path)) => - val baseUrl = Url.parse(externalApiUrls(resourceType)) + val baseUrl = Url.parse(props.externalApiUrls(resourceType)) val pathParts = Path.parse(path).parts embedTag.attr( @@ -190,13 +188,15 @@ trait ReadService { articleId: Long, language: String, fallback: Boolean - ): Try[ArticleRevisionHistoryDTO] = { + ): Try[ArticleRevisionHistoryDTO] = boundary { val drafts = draftRepository .articlesWithId(articleId) .map(addUrlsOnEmbedResources) .sortBy( _.revision.getOrElse( - return Failure(api.NotFoundException(s"Revision was missing for draft of article with id $articleId")) + boundary.break( + Failure(api.NotFoundException(s"Revision was missing for draft of article with id $articleId")) + ) ) ) .reverse diff --git a/draft-api/src/main/scala/no/ndla/draftapi/service/WriteService.scala b/draft-api/src/main/scala/no/ndla/draftapi/service/WriteService.scala index e6a635015b..0e6bf475de 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/service/WriteService.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/service/WriteService.scala @@ -16,8 +16,8 @@ import no.ndla.common.Clock import no.ndla.common.ContentURIUtil.parseArticleIdAndRevision import no.ndla.common.TryUtil.failureIf import no.ndla.common.configuration.Constants.EmbedTagName -import no.ndla.common.errors.{MissingIdException, NotFoundException, ValidationException, OperationNotAllowedException} -import no.ndla.common.implicits.{OptionImplicit, TryQuestionMark} +import no.ndla.common.errors.{MissingIdException, NotFoundException, OperationNotAllowedException, ValidationException} +import no.ndla.common.implicits.* import no.ndla.common.logging.logTaskTime import no.ndla.common.model.api.UpdateWith import no.ndla.common.model.domain.article.PartialPublishArticleDTO @@ -48,13 +48,13 @@ import scala.concurrent.duration.* import scala.concurrent.{Await, ExecutionContext, ExecutionContextExecutorService, Future} import scala.jdk.CollectionConverters.* import scala.math.max -import scala.util.{Failure, Random, Success, Try} +import scala.util.{Failure, Random, Success, Try, boundary} trait WriteService { this: DraftRepository & UserDataRepository & ConverterService & ContentValidator & ArticleIndexService & TagIndexService & GrepCodesIndexService & Clock & ReadService & ArticleApiClient & SearchApiClient & FileStorageService & TaxonomyApiClient & Props & DBUtility => - val writeService: WriteService + lazy val writeService: WriteService class WriteService extends StrictLogging { @@ -75,9 +75,9 @@ trait WriteService { article.id match { case None => Failure(new IllegalStateException("No id found for article when indexing. This is a bug.")) case Some(articleId) => - searchApiClient.indexDocument("draft", article, Some(user)): Unit - articleIndexService.indexAsync(articleId, article): Unit - tagIndexService.indexAsync(articleId, article): Unit + val _ = searchApiClient.indexDocument("draft", article, Some(user)) + val _ = articleIndexService.indexAsync(articleId, article) + val _ = tagIndexService.indexAsync(articleId, article) Success(()) } @@ -221,23 +221,25 @@ trait WriteService { private def migrateOutdatedGrepForDraft( draft: Draft, user: TokenUser - )(session: DBSession): Try[Option[(Long, PartialPublishArticleDTO)]] = { - val articleId = draft.id.getOrElse(-1L) - logger.info(s"Migrating grep codes for article $articleId") - if (draft.grepCodes.isEmpty) { return Success(None) } - val newGrepCodeMapping = searchApiClient.convertGrepCodes(draft.grepCodes, user).? - val updatedGrepCodes = newGrepCodeMapping.values.toSeq - if (draft.grepCodes.sorted == updatedGrepCodes.sorted) { return Success(None) } - val grepCodeNote = getGrepCodeNote(newGrepCodeMapping, draft, user) - val newDraft = draft.copy(grepCodes = updatedGrepCodes, notes = draft.notes :+ grepCodeNote) - val updated = draftRepository.updateArticle(newDraft)(session).? - val partialPart = partialArticleFieldsUpdate(updated, grepFieldsToPublish, Language.AllLanguages) - logger.info( - s"Migrated grep codes for article $articleId from [${draft.grepCodes.mkString(",")}] to [${updatedGrepCodes.mkString(",")}]" - ) - lazy val idException = MissingIdException("Article id was missing after updating grep codes. This is a bug.") - val id = updated.id.toTry(idException).? - Success(Some((id, partialPart))) + )(session: DBSession): Try[Option[(Long, PartialPublishArticleDTO)]] = permitTry { + boundary { + val articleId = draft.id.getOrElse(-1L) + logger.info(s"Migrating grep codes for article $articleId") + if (draft.grepCodes.isEmpty) { boundary.break(Success(None)) } + val newGrepCodeMapping = searchApiClient.convertGrepCodes(draft.grepCodes, user).? + val updatedGrepCodes = newGrepCodeMapping.values.toSeq + if (draft.grepCodes.sorted == updatedGrepCodes.sorted) { boundary.break(Success(None)) } + val grepCodeNote = getGrepCodeNote(newGrepCodeMapping, draft, user) + val newDraft = draft.copy(grepCodes = updatedGrepCodes, notes = draft.notes :+ grepCodeNote) + val updated = draftRepository.updateArticle(newDraft)(session).? + val partialPart = partialArticleFieldsUpdate(updated, grepFieldsToPublish, Language.AllLanguages) + logger.info( + s"Migrated grep codes for article $articleId from [${draft.grepCodes.mkString(",")}] to [${updatedGrepCodes.mkString(",")}]" + ) + lazy val idException = MissingIdException("Article id was missing after updating grep codes. This is a bug.") + val id = updated.id.toTry(idException).? + Success(Some((id, partialPart))) + } } def migrateOutdatedGreps(user: TokenUser): Try[Unit] = logTaskTime("Migrate outdated grep codes") { @@ -497,16 +499,17 @@ trait WriteService { updatedApiArticle: api.UpdatedArticleDTO, user: TokenUser, shouldNotAutoUpdateStatus: Boolean - ): Try[Draft] = { + ): Try[Draft] = permitTry { val newManualStatus = updatedApiArticle.status.traverse(DraftStatus.valueOfOrError).? - if (shouldNotAutoUpdateStatus && newManualStatus.isEmpty) - return Success(convertedArticle) - - val oldStatus = existingArticle.status.current - val newStatusIfUndefined = if (oldStatus == PUBLISHED) IN_PROGRESS else oldStatus - val newStatus = newManualStatus.getOrElse(newStatusIfUndefined) + if (shouldNotAutoUpdateStatus && newManualStatus.isEmpty) { + Success(convertedArticle) + } else { + val oldStatus = existingArticle.status.current + val newStatusIfUndefined = if (oldStatus == PUBLISHED) IN_PROGRESS else oldStatus + val newStatus = newManualStatus.getOrElse(newStatusIfUndefined) - converterService.updateStatus(newStatus, convertedArticle, user) + converterService.updateStatus(newStatus, convertedArticle, user) + } } def updateArticle(articleId: Long, updatedApiArticle: api.UpdatedArticleDTO, user: TokenUser): Try[api.ArticleDTO] = @@ -884,7 +887,7 @@ trait WriteService { taxonomyApiClient .getChildResources(id) .flatMap(resources => resources.traverse(setRevisions(_, revisions))) - case _ => Success(()) + case null => Success(()) } }) } diff --git a/draft-api/src/main/scala/no/ndla/draftapi/service/search/ArticleIndexService.scala b/draft-api/src/main/scala/no/ndla/draftapi/service/search/ArticleIndexService.scala index 377baa4293..e9659871a4 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/service/search/ArticleIndexService.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/service/search/ArticleIndexService.scala @@ -21,9 +21,9 @@ import no.ndla.draftapi.repository.{DraftRepository, Repository} trait ArticleIndexService { this: SearchConverterService with IndexService with DraftRepository with Props => - val articleIndexService: ArticleIndexService + lazy val articleIndexService: ArticleIndexService - class ArticleIndexService extends StrictLogging with IndexService[Draft, SearchableArticle] { + class ArticleIndexService extends IndexService[Draft, SearchableArticle] with StrictLogging { override val documentType: String = props.DraftSearchDocument override val searchIndex: String = props.DraftSearchIndex override val repository: Repository[Draft] = draftRepository diff --git a/draft-api/src/main/scala/no/ndla/draftapi/service/search/ArticleSearchService.scala b/draft-api/src/main/scala/no/ndla/draftapi/service/search/ArticleSearchService.scala index 104de858ec..44dd5be4e3 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/service/search/ArticleSearchService.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/service/search/ArticleSearchService.scala @@ -31,11 +31,9 @@ trait ArticleSearchService { with SearchConverterService with Props with ErrorHandling => - val articleSearchService: ArticleSearchService + lazy val articleSearchService: ArticleSearchService class ArticleSearchService extends StrictLogging with SearchService[api.ArticleSummaryDTO] { - import props.{ElasticSearchIndexMaxResultWindow, ElasticSearchScrollKeepAlive} - private val noCopyright = boolQuery().not(termQuery("license", License.Copyrighted.toString)) override val searchIndex: String = props.DraftSearchIndex @@ -106,9 +104,9 @@ trait ArticleSearchService { val (startAt, numResults) = getStartAtAndNumResults(settings.page, settings.pageSize) val requestedResultWindow = settings.pageSize * settings.page - if (requestedResultWindow > ElasticSearchIndexMaxResultWindow) { + if (requestedResultWindow > props.ElasticSearchIndexMaxResultWindow) { logger.info( - s"Max supported results are $ElasticSearchIndexMaxResultWindow, user requested $requestedResultWindow" + s"Max supported results are ${props.ElasticSearchIndexMaxResultWindow}, user requested $requestedResultWindow" ) Failure(new ResultWindowTooLargeException()) } else { @@ -122,7 +120,7 @@ trait ArticleSearchService { val searchWithScroll = if (startAt == 0 && settings.shouldScroll) { - searchToExecute.scroll(ElasticSearchScrollKeepAlive) + searchToExecute.scroll(props.ElasticSearchScrollKeepAlive) } else { searchToExecute } e4sClient.execute(searchWithScroll) match { diff --git a/draft-api/src/main/scala/no/ndla/draftapi/service/search/GrepCodesIndexService.scala b/draft-api/src/main/scala/no/ndla/draftapi/service/search/GrepCodesIndexService.scala index 430f4ab981..317d94278e 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/service/search/GrepCodesIndexService.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/service/search/GrepCodesIndexService.scala @@ -20,9 +20,9 @@ import no.ndla.draftapi.repository.{DraftRepository, Repository} trait GrepCodesIndexService { this: SearchConverterService with IndexService with DraftRepository with Props => - val grepCodesIndexService: GrepCodesIndexService + lazy val grepCodesIndexService: GrepCodesIndexService - class GrepCodesIndexService extends StrictLogging with IndexService[Draft, SearchableGrepCode] { + class GrepCodesIndexService extends IndexService[Draft, SearchableGrepCode] with StrictLogging { override val documentType: String = props.DraftGrepCodesSearchDocument override val searchIndex: String = props.DraftGrepCodesSearchIndex override val repository: Repository[Draft] = draftRepository diff --git a/draft-api/src/main/scala/no/ndla/draftapi/service/search/GrepCodesSearchService.scala b/draft-api/src/main/scala/no/ndla/draftapi/service/search/GrepCodesSearchService.scala index 6324d3cf5e..5d37d45574 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/service/search/GrepCodesSearchService.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/service/search/GrepCodesSearchService.scala @@ -32,11 +32,10 @@ trait GrepCodesSearchService { with SearchConverterService with Props with ErrorHandling => - val grepCodesSearchService: GrepCodesSearchService + lazy val grepCodesSearchService: GrepCodesSearchService class GrepCodesSearchService extends StrictLogging with BasicSearchService[String] { - import props._ - override val searchIndex: String = DraftGrepCodesSearchIndex + override val searchIndex: String = props.DraftGrepCodesSearchIndex def getHits(response: SearchResponse): Seq[String] = { response.hits.hits.toList.map(hit => CirceUtil.unsafeParseAs[SearchableGrepCode](hit.sourceAsString).grepCode) @@ -62,9 +61,9 @@ trait GrepCodesSearchService { ): Try[LanguagelessSearchResult[String]] = { val (startAt, numResults) = getStartAtAndNumResults(page, pageSize) val requestedResultWindow = pageSize * page - if (requestedResultWindow > ElasticSearchIndexMaxResultWindow) { + if (requestedResultWindow > props.ElasticSearchIndexMaxResultWindow) { logger.info( - s"Max supported results are $ElasticSearchIndexMaxResultWindow, user requested $requestedResultWindow" + s"Max supported results are ${props.ElasticSearchIndexMaxResultWindow}, user requested $requestedResultWindow" ) Failure(new ResultWindowTooLargeException()) } else { @@ -77,7 +76,7 @@ trait GrepCodesSearchService { val searchWithScroll = if (startAt != 0) { searchToExecute } - else { searchToExecute.scroll(ElasticSearchScrollKeepAlive) } + else { searchToExecute.scroll(props.ElasticSearchScrollKeepAlive) } e4sClient.execute(searchWithScroll) match { case Success(response) => diff --git a/draft-api/src/main/scala/no/ndla/draftapi/service/search/IndexService.scala b/draft-api/src/main/scala/no/ndla/draftapi/service/search/IndexService.scala index 5283ab4c5f..e0e6cae174 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/service/search/IndexService.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/service/search/IndexService.scala @@ -25,7 +25,7 @@ import scala.concurrent.{ExecutionContext, Future} trait IndexService { this: Elastic4sClient & BaseIndexService & Props & SearchLanguage => - trait IndexService[D, T <: AnyRef] extends BaseIndexService with StrictLogging { + abstract class IndexService[D, T <: AnyRef] extends BaseIndexService with StrictLogging { override val MaxResultWindowOption: Int = props.ElasticSearchIndexMaxResultWindow val repository: Repository[D] diff --git a/draft-api/src/main/scala/no/ndla/draftapi/service/search/SearchConverterService.scala b/draft-api/src/main/scala/no/ndla/draftapi/service/search/SearchConverterService.scala index eb26c8d2d3..eec3f72b81 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/service/search/SearchConverterService.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/service/search/SearchConverterService.scala @@ -27,7 +27,7 @@ import org.jsoup.Jsoup trait SearchConverterService { this: ConverterService & SearchLanguage => - val searchConverterService: SearchConverterService + lazy val searchConverterService: SearchConverterService class SearchConverterService extends StrictLogging { def asSearchableArticle(ai: Draft): SearchableArticle = { diff --git a/draft-api/src/main/scala/no/ndla/draftapi/service/search/SearchService.scala b/draft-api/src/main/scala/no/ndla/draftapi/service/search/SearchService.scala index 53f79b9437..287849eed0 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/service/search/SearchService.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/service/search/SearchService.scala @@ -24,8 +24,6 @@ import scala.util.{Failure, Success, Try} trait SearchService { this: Elastic4sClient with SearchConverterService with StrictLogging with Props => - import props._ - trait SearchService[T] extends BasicSearchService[T] { def hitToApiModel(hit: String, language: String): T @@ -50,7 +48,7 @@ trait SearchService { def scroll(scrollId: String, language: String): Try[SearchResult[T]] = e4sClient .execute { - searchScroll(scrollId, ElasticSearchScrollKeepAlive) + searchScroll(scrollId, props.ElasticSearchScrollKeepAlive) } .map(response => { val hits = getHits(response.result, language) @@ -67,7 +65,7 @@ trait SearchService { def getSortDefinition(sort: Sort, language: String): FieldSort = { val sortLanguage = language match { - case Language.NoLanguage => DefaultLanguage + case Language.NoLanguage => props.DefaultLanguage case _ => language } @@ -121,7 +119,7 @@ trait SearchService { } def getStartAtAndNumResults(page: Int, pageSize: Int): (Int, Int) = { - val numResults = max(pageSize.min(MaxPageSize), 0) + val numResults = max(pageSize.min(props.MaxPageSize), 0) val startAt = (page - 1).max(0) * numResults (startAt, numResults) diff --git a/draft-api/src/main/scala/no/ndla/draftapi/service/search/TagIndexService.scala b/draft-api/src/main/scala/no/ndla/draftapi/service/search/TagIndexService.scala index 76f5fbf449..f7f8d46437 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/service/search/TagIndexService.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/service/search/TagIndexService.scala @@ -20,9 +20,9 @@ import no.ndla.draftapi.repository.{DraftRepository, Repository} trait TagIndexService { this: SearchConverterService with IndexService with DraftRepository with Props => - val tagIndexService: TagIndexService + lazy val tagIndexService: TagIndexService - class TagIndexService extends StrictLogging with IndexService[Draft, SearchableTag] { + class TagIndexService extends IndexService[Draft, SearchableTag] with StrictLogging { override val documentType: String = props.DraftTagSearchDocument override val searchIndex: String = props.DraftTagSearchIndex override val repository: Repository[Draft] = draftRepository diff --git a/draft-api/src/main/scala/no/ndla/draftapi/service/search/TagSearchService.scala b/draft-api/src/main/scala/no/ndla/draftapi/service/search/TagSearchService.scala index 1152bc8399..62519805a3 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/service/search/TagSearchService.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/service/search/TagSearchService.scala @@ -32,11 +32,10 @@ trait TagSearchService { with SearchConverterService with Props with ErrorHandling => - val tagSearchService: TagSearchService + lazy val tagSearchService: TagSearchService class TagSearchService extends StrictLogging with SearchService[String] { - import props._ - override val searchIndex: String = DraftTagSearchIndex + override val searchIndex: String = props.DraftTagSearchIndex override def hitToApiModel(hit: String, language: String): String = { val searchableTag = CirceUtil.unsafeParseAs[SearchableTag](hit) @@ -83,9 +82,9 @@ trait TagSearchService { val (startAt, numResults) = getStartAtAndNumResults(page, pageSize) val requestedResultWindow = pageSize * page - if (requestedResultWindow > ElasticSearchIndexMaxResultWindow) { + if (requestedResultWindow > props.ElasticSearchIndexMaxResultWindow) { logger.info( - s"Max supported results are $ElasticSearchIndexMaxResultWindow, user requested $requestedResultWindow" + s"Max supported results are ${props.ElasticSearchIndexMaxResultWindow}, user requested $requestedResultWindow" ) Failure(new ResultWindowTooLargeException()) } else { @@ -98,7 +97,7 @@ trait TagSearchService { val searchWithScroll = if (startAt != 0) { searchToExecute } - else { searchToExecute.scroll(ElasticSearchScrollKeepAlive) } + else { searchToExecute.scroll(props.ElasticSearchScrollKeepAlive) } e4sClient.execute(searchWithScroll) match { case Success(response) => diff --git a/draft-api/src/main/scala/no/ndla/draftapi/validation/ContentValidator.scala b/draft-api/src/main/scala/no/ndla/draftapi/validation/ContentValidator.scala index 7a737b5e6a..615d5100a6 100644 --- a/draft-api/src/main/scala/no/ndla/draftapi/validation/ContentValidator.scala +++ b/draft-api/src/main/scala/no/ndla/draftapi/validation/ContentValidator.scala @@ -32,11 +32,10 @@ import scala.util.{Failure, Success, Try} trait ContentValidator { this: DraftRepository & ConverterService & ArticleApiClient & Props => - val contentValidator: ContentValidator - val importValidator: ContentValidator + lazy val contentValidator: ContentValidator + lazy val importValidator: ContentValidator class ContentValidator { - import props.{BrightcoveVideoScriptUrl, H5PResizerScriptUrl, NRKVideoScriptUrl} private val inlineHtmlTags = props.InlineHtmlTags private val introductionHtmlTags = props.IntroductionHtmlTags @@ -294,7 +293,7 @@ trait ContentValidator { } private def validateRequiredLibrary(requiredLibrary: RequiredLibrary): Option[ValidationMessage] = { - val permittedLibraries = Seq(BrightcoveVideoScriptUrl, H5PResizerScriptUrl) ++ NRKVideoScriptUrl + val permittedLibraries = Seq(props.BrightcoveVideoScriptUrl, props.H5PResizerScriptUrl) ++ props.NRKVideoScriptUrl if (permittedLibraries.contains(requiredLibrary.url)) { None } else { diff --git a/draft-api/src/test/scala/no/ndla/draftapi/TestEnvironment.scala b/draft-api/src/test/scala/no/ndla/draftapi/TestEnvironment.scala index 374f0b2f9c..2015a415da 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/TestEnvironment.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/TestEnvironment.scala @@ -74,56 +74,57 @@ trait TestEnvironment with MemoizeHelpers with DBMigrator with Props - with V57__MigrateSavedSearch { + with V57__MigrateSavedSearch + with ImageApiClient { lazy val props: DraftApiProperties = new DraftApiProperties { override def InlineHtmlTags: Set[String] = Set("code", "em", "span", "strong", "sub", "sup") override def IntroductionHtmlTags: Set[String] = InlineHtmlTags ++ Set("br", "p") } - val migrator: DBMigrator = mock[DBMigrator] - val DBUtil: DBUtility = mock[DBUtility] + override lazy val migrator: DBMigrator = mock[DBMigrator] + override lazy val DBUtil: DBUtility = mock[DBUtility] - val articleSearchService: ArticleSearchService = mock[ArticleSearchService] - val articleIndexService: ArticleIndexService = mock[ArticleIndexService] - val tagSearchService: TagSearchService = mock[TagSearchService] - val tagIndexService: TagIndexService = mock[TagIndexService] - val grepCodesSearchService: GrepCodesSearchService = mock[GrepCodesSearchService] - val grepCodesIndexService: GrepCodesIndexService = mock[GrepCodesIndexService] + override lazy val articleSearchService: ArticleSearchService = mock[ArticleSearchService] + override lazy val articleIndexService: ArticleIndexService = mock[ArticleIndexService] + override lazy val tagSearchService: TagSearchService = mock[TagSearchService] + override lazy val tagIndexService: TagIndexService = mock[TagIndexService] + override lazy val grepCodesSearchService: GrepCodesSearchService = mock[GrepCodesSearchService] + override lazy val grepCodesIndexService: GrepCodesIndexService = mock[GrepCodesIndexService] - val internController: InternController = mock[InternController] - val draftController: DraftController = mock[DraftController] - val fileController: FileController = mock[FileController] - val userDataController: UserDataController = mock[UserDataController] - val healthController: TapirHealthController = mock[TapirHealthController] + override lazy val internController: InternController = mock[InternController] + override lazy val draftController: DraftController = mock[DraftController] + override lazy val fileController: FileController = mock[FileController] + override lazy val userDataController: UserDataController = mock[UserDataController] + override lazy val healthController: TapirHealthController = mock[TapirHealthController] - val dataSource: HikariDataSource = mock[HikariDataSource] - val draftRepository: DraftRepository = mock[DraftRepository] - val userDataRepository: UserDataRepository = mock[UserDataRepository] + override lazy val dataSource: HikariDataSource = mock[HikariDataSource] + override lazy val draftRepository: DraftRepository = mock[DraftRepository] + override lazy val userDataRepository: UserDataRepository = mock[UserDataRepository] - val converterService: ConverterService = mock[ConverterService] + override lazy val converterService: ConverterService = mock[ConverterService] - val readService: ReadService = mock[ReadService] - val writeService: WriteService = mock[WriteService] - val contentValidator: ContentValidator = mock[ContentValidator] - val importValidator: ContentValidator = mock[ContentValidator] - val reindexClient: ReindexClient = mock[ReindexClient] + override lazy val readService: ReadService = mock[ReadService] + override lazy val writeService: WriteService = mock[WriteService] + override lazy val contentValidator: ContentValidator = mock[ContentValidator] + override lazy val importValidator: ContentValidator = mock[ContentValidator] + override lazy val reindexClient: ReindexClient = mock[ReindexClient] - lazy val fileStorage: FileStorageService = mock[FileStorageService] - val s3Client: NdlaS3Client = mock[NdlaS3Client] + override lazy val fileStorage: FileStorageService = mock[FileStorageService] + override lazy val s3Client: NdlaS3Client = mock[NdlaS3Client] - val ndlaClient: NdlaClient = mock[NdlaClient] - val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] - val searchConverterService: SearchConverterService = mock[SearchConverterService] - var e4sClient: NdlaE4sClient = mock[NdlaE4sClient] - override val learningpathApiClient: LearningpathApiClient = mock[LearningpathApiClient] + override lazy val ndlaClient: NdlaClient = mock[NdlaClient] + override lazy val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] + override lazy val searchConverterService: SearchConverterService = mock[SearchConverterService] + var e4sClient: NdlaE4sClient = mock[NdlaE4sClient] + override lazy val learningpathApiClient: LearningpathApiClient = mock[LearningpathApiClient] - val clock: SystemClock = mock[SystemClock] - val uuidUtil: UUIDUtil = mock[UUIDUtil] + override lazy val clock: SystemClock = mock[SystemClock] + override lazy val uuidUtil: UUIDUtil = mock[UUIDUtil] - val articleApiClient: ArticleApiClient = mock[ArticleApiClient] - val searchApiClient: SearchApiClient = mock[SearchApiClient] - val taxonomyApiClient: TaxonomyApiClient = mock[TaxonomyApiClient] - val h5pApiClient: H5PApiClient = mock[H5PApiClient] - val imageApiClient: ImageApiClient = mock[ImageApiClient] + override lazy val articleApiClient: ArticleApiClient = mock[ArticleApiClient] + override lazy val searchApiClient: SearchApiClient = mock[SearchApiClient] + override lazy val taxonomyApiClient: TaxonomyApiClient = mock[TaxonomyApiClient] + override lazy val h5pApiClient: H5PApiClient = mock[H5PApiClient] + override lazy val imageApiClient: ImageApiClient = mock[ImageApiClient] def services: List[TapirController] = List.empty val swagger: SwaggerController = mock[SwaggerController] diff --git a/draft-api/src/test/scala/no/ndla/draftapi/integration/TaxonomyApiClientTest.scala b/draft-api/src/test/scala/no/ndla/draftapi/integration/TaxonomyApiClientTest.scala index 524e249aa0..1783dae8e0 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/integration/TaxonomyApiClientTest.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/integration/TaxonomyApiClientTest.scala @@ -21,7 +21,7 @@ import scala.util.{Failure, Success} class TaxonomyApiClientTest extends UnitSuite with TestEnvironment { - override val taxonomyApiClient: TaxonomyApiClient = spy(new TaxonomyApiClient) + override lazy val taxonomyApiClient: TaxonomyApiClient = spy(new TaxonomyApiClient) override def beforeEach(): Unit = { // Since we use spy, we reset the mock before each test allowing verify to be accurate diff --git a/draft-api/src/test/scala/no/ndla/draftapi/repository/DraftRepositoryTest.scala b/draft-api/src/test/scala/no/ndla/draftapi/repository/DraftRepositoryTest.scala index 362e8ac73c..e85d9bbd7b 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/repository/DraftRepositoryTest.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/repository/DraftRepositoryTest.scala @@ -24,10 +24,10 @@ import java.util.UUID import scala.util.{Success, Try} class DraftRepositoryTest extends DatabaseIntegrationSuite with TestEnvironment { - override val dataSource: HikariDataSource = testDataSource.get - override val migrator: DBMigrator = new DBMigrator - var repository: DraftRepository = _ - val sampleArticle: Draft = TestData.sampleArticleWithByNcSa + override lazy val dataSource: HikariDataSource = testDataSource.get + override lazy val migrator: DBMigrator = new DBMigrator + var repository: DraftRepository = _ + val sampleArticle: Draft = TestData.sampleArticleWithByNcSa def emptyTestDatabase(): Unit = DB autoCommit (implicit session => { sql"delete from articledata;".execute()(session) @@ -312,7 +312,7 @@ class DraftRepositoryTest extends DatabaseIntegrationSuite with TestEnvironment test("withId parse relatedContent correctly") { repository.insert(sampleArticle.copy(id = Some(1), relatedContent = Seq(Right(2))))(AutoSession) - val Right(relatedId) = repository.withId(1)(ReadOnlyAutoSession).get.relatedContent.head + val Right(relatedId) = repository.withId(1)(ReadOnlyAutoSession).get.relatedContent.head: @unchecked relatedId should be(2L) } diff --git a/draft-api/src/test/scala/no/ndla/draftapi/repository/UserDataRepositoryTest.scala b/draft-api/src/test/scala/no/ndla/draftapi/repository/UserDataRepositoryTest.scala index 33af7bdd0d..a774a78ed6 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/repository/UserDataRepositoryTest.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/repository/UserDataRepositoryTest.scala @@ -20,9 +20,9 @@ import scalikejdbc.* import scala.util.{Failure, Success, Try} class UserDataRepositoryTest extends DatabaseIntegrationSuite with TestEnvironment { - override val dataSource: HikariDataSource = testDataSource.get - override val migrator: DBMigrator = new DBMigrator - var repository: UserDataRepository = _ + override lazy val dataSource: HikariDataSource = testDataSource.get + override lazy val migrator: DBMigrator = new DBMigrator + var repository: UserDataRepository = _ def emptyTestDatabase: Boolean = { DB autoCommit (implicit session => { diff --git a/draft-api/src/test/scala/no/ndla/draftapi/service/ConverterServiceTest.scala b/draft-api/src/test/scala/no/ndla/draftapi/service/ConverterServiceTest.scala index db6338fe96..8dcdf568ff 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/service/ConverterServiceTest.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/service/ConverterServiceTest.scala @@ -110,7 +110,7 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { when(clock.now()).thenReturn(expectedTime) - val Success(result) = service.toDomainArticle(1, apiArticle, TestData.userWithWriteAccess) + val Success(result) = service.toDomainArticle(1, apiArticle, TestData.userWithWriteAccess): @unchecked result.content.head.content should equal(expectedContent) result.visualElement.head.resource should equal(expectedVisualElement) result.created should equal(expectedTime) @@ -139,20 +139,20 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { TestData.sampleDomainArticle.copy(status = Status(PLANNED, Set())), updatedArticle, TestData.userWithWriteAccess - ) + ): @unchecked res.title.find(_.language == "nb").get.title should equal("kakemonster") } test("updateStatus should return an IO[Failure] if the status change is illegal") { val Failure(res: IllegalStatusStateTransition) = - service.updateStatus(PUBLISHED, TestData.sampleArticleWithByNcSa, TestData.userWithWriteAccess) + service.updateStatus(PUBLISHED, TestData.sampleArticleWithByNcSa, TestData.userWithWriteAccess): @unchecked res.getMessage should equal( s"Cannot go to PUBLISHED when article is ${TestData.sampleArticleWithByNcSa.status.current}" ) } test("stateTransitionsToApi should return only disabled entries if user has no roles") { - val Success(res) = service.stateTransitionsToApi(TestData.userWithNoRoles, None) + val Success(res) = service.stateTransitionsToApi(TestData.userWithNoRoles, None): @unchecked res.forall { case (_, to) => to.isEmpty } should be(true) } @@ -161,7 +161,7 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { val article: Draft = TestData.sampleArticleWithPublicDomain.copy(id = Some(articleId), status = Status(DraftStatus.PLANNED, Set())) when(draftRepository.withId(eqTo(articleId))(any)).thenReturn(Some(article)) - val Success(noTrans) = service.stateTransitionsToApi(TestData.userWithWriteAccess, Some(articleId)) + val Success(noTrans) = service.stateTransitionsToApi(TestData.userWithWriteAccess, Some(articleId)): @unchecked noTrans(PLANNED.toString) should contain(DraftStatus.ARCHIVED.toString) noTrans(IN_PROGRESS.toString) should contain(DraftStatus.ARCHIVED.toString) noTrans(EXTERNAL_REVIEW.toString) should contain(DraftStatus.ARCHIVED.toString) @@ -179,7 +179,7 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { val article: Draft = TestData.sampleArticleWithPublicDomain.copy(id = Some(articleId), status = Status(DraftStatus.PUBLISHED, Set())) when(draftRepository.withId(eqTo(articleId))(any)).thenReturn(Some(article)) - val Success(noTrans) = service.stateTransitionsToApi(TestData.userWithWriteAccess, Some(articleId)) + val Success(noTrans) = service.stateTransitionsToApi(TestData.userWithWriteAccess, Some(articleId)): @unchecked noTrans(PLANNED.toString) should not contain (DraftStatus.ARCHIVED.toString) noTrans(IN_PROGRESS.toString) should not contain (DraftStatus.ARCHIVED.toString) @@ -197,7 +197,7 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { val unpublished: Draft = TestData.sampleArticleWithPublicDomain.copy(id = Some(articleId), status = Status(DraftStatus.IN_PROGRESS, Set())) when(draftRepository.withId(eqTo(articleId))(any)).thenReturn(Some(unpublished)) - val Success(transOne) = service.stateTransitionsToApi(TestData.userWithWriteAccess, Some(articleId)) + val Success(transOne) = service.stateTransitionsToApi(TestData.userWithWriteAccess, Some(articleId)): @unchecked transOne(IN_PROGRESS.toString) should not contain (DraftStatus.LANGUAGE.toString) val published: Draft = @@ -206,7 +206,7 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { status = Status(DraftStatus.IN_PROGRESS, Set(DraftStatus.PUBLISHED)) ) when(draftRepository.withId(eqTo(articleId))(any)).thenReturn(Some(published)) - val Success(transTwo) = service.stateTransitionsToApi(TestData.userWithWriteAccess, Some(articleId)) + val Success(transTwo) = service.stateTransitionsToApi(TestData.userWithWriteAccess, Some(articleId)): @unchecked transTwo(IN_PROGRESS.toString) should contain(DraftStatus.LANGUAGE.toString) } @@ -219,7 +219,7 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { status = Status(DraftStatus.PLANNED, Set(DraftStatus.PUBLISHED)) ) when(draftRepository.withId(eqTo(articleId))(any)).thenReturn(Some(article)) - val Success(noTrans) = service.stateTransitionsToApi(TestData.userWithWriteAccess, None) + val Success(noTrans) = service.stateTransitionsToApi(TestData.userWithWriteAccess, None): @unchecked noTrans(PLANNED.toString) should not contain (DraftStatus.ARCHIVED) noTrans(IN_PROGRESS.toString) should not contain (DraftStatus.ARCHIVED) @@ -233,8 +233,8 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { } test("stateTransitionsToApi should return different number of transitions based on access") { - val Success(adminTrans) = service.stateTransitionsToApi(TestData.userWithAdminAccess, None) - val Success(writeTrans) = service.stateTransitionsToApi(TestData.userWithWriteAccess, None) + val Success(adminTrans) = service.stateTransitionsToApi(TestData.userWithAdminAccess, None): @unchecked + val Success(writeTrans) = service.stateTransitionsToApi(TestData.userWithWriteAccess, None): @unchecked // format: off writeTrans(PLANNED.toString).length should be(adminTrans(PLANNED.toString).length) @@ -250,12 +250,12 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { } test("stateTransitionsToApi should have transitions from all statuses if admin") { - val Success(adminTrans) = service.stateTransitionsToApi(TestData.userWithAdminAccess, None) + val Success(adminTrans) = service.stateTransitionsToApi(TestData.userWithAdminAccess, None): @unchecked adminTrans.size should be(DraftStatus.values.size - 1) } test("stateTransitionsToApi should have transitions in inserted order") { - val Success(adminTrans) = service.stateTransitionsToApi(TestData.userWithAdminAccess, None) + val Success(adminTrans) = service.stateTransitionsToApi(TestData.userWithAdminAccess, None): @unchecked adminTrans(LANGUAGE.toString) should be( Seq( IN_PROGRESS.toString, @@ -555,25 +555,25 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { TestData.sampleDomainArticle.copy(status = Status(PLANNED, Set()), notes = existingNotes), updatedArticleWithoutNotes, TestData.userWithWriteAccess - ) + ): @unchecked val Success(res2) = service.toDomainArticle( TestData.sampleDomainArticle.copy(status = Status(PLANNED, Set()), notes = Seq.empty), updatedArticleWithoutNotes, TestData.userWithWriteAccess - ) + ): @unchecked val Success(res3) = service.toDomainArticle( TestData.sampleDomainArticle.copy(status = Status(PLANNED, Set()), notes = existingNotes), updatedArticleWithNotes, TestData.userWithWriteAccess - ) + ): @unchecked val Success(res4) = service.toDomainArticle( TestData.sampleDomainArticle.copy(status = Status(PLANNED, Set()), notes = Seq.empty), updatedArticleWithNotes, TestData.userWithWriteAccess - ) + ): @unchecked res1.notes should be(existingNotes) res2.notes should be(Seq.empty) @@ -587,7 +587,7 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { val article = TestData.sampleDomainArticle.copy(status = status, responsible = Some(Responsible("hei", clock.now()))) val Failure(res: IllegalStatusStateTransition) = - service.updateStatus(ARCHIVED, article, TestData.userWithPublishAccess) + service.updateStatus(ARCHIVED, article, TestData.userWithPublishAccess): @unchecked res.getMessage should equal(s"Cannot go to ARCHIVED when article contains ${status.other}") } @@ -603,19 +603,19 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { TestData.sampleDomainArticle.copy(status = Status(PLANNED, Set()), notes = existingNotes), updatedArticleWithNotes.copy(language = Some("sna")), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res2) = service.toDomainArticle( TestData.sampleDomainArticle.copy(status = Status(PLANNED, Set()), notes = existingNotes), updatedArticleWithNotes.copy(language = Some("nb")), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res3) = service.toDomainArticle( TestData.sampleDomainArticle.copy(status = Status(PLANNED, Set()), notes = existingNotes), updatedArticleWithoutNotes.copy(language = Some("sna")), TestData.userWithWriteAccess - ) + ): @unchecked res1.notes.map(_.note) should be(Seq("swoop", "fleibede", s"Ny språkvariant 'sna' ble lagt til.")) res2.notes.map(_.note) should be(Seq("swoop", "fleibede")) @@ -628,13 +628,13 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { 1, TestData.newArticle.copy(grepCodes = Some(Seq("a", "b"))), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res2) = service.toDomainArticle( 1, TestData.newArticle.copy(grepCodes = None), TestData.userWithWriteAccess - ) + ): @unchecked res1.grepCodes should be(Seq("a", "b")) res2.grepCodes should be(Seq.empty) @@ -645,19 +645,19 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { TestData.sampleDomainArticle.copy(grepCodes = Seq("a", "b", "c")), TestData.sampleApiUpdateArticle.copy(grepCodes = Some(Seq("x", "y"))), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res2) = service.toDomainArticle( TestData.sampleDomainArticle.copy(grepCodes = Seq("a", "b", "c")), TestData.sampleApiUpdateArticle.copy(grepCodes = Some(Seq.empty)), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res3) = service.toDomainArticle( TestData.sampleDomainArticle.copy(grepCodes = Seq("a", "b", "c")), TestData.sampleApiUpdateArticle.copy(grepCodes = None), TestData.userWithWriteAccess - ) + ): @unchecked res1.grepCodes should be(Seq("x", "y")) res2.grepCodes should be(Seq.empty) @@ -674,20 +674,20 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { beforeUpdate, TestData.sampleApiUpdateArticle.copy(language = Some("nb"), metaImage = Delete), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res2) = service.toDomainArticle( beforeUpdate, TestData.sampleApiUpdateArticle .copy(language = Some("nb"), metaImage = UpdateWith(api.NewArticleMetaImageDTO("1", "Hola"))), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res3) = service.toDomainArticle( beforeUpdate, TestData.sampleApiUpdateArticle.copy(language = Some("nb"), metaImage = Missing), TestData.userWithWriteAccess - ) + ): @unchecked res1.metaImage should be(Seq(ArticleMetaImage("2", "Hej", "nn"))) res2.metaImage should be(Seq(ArticleMetaImage("2", "Hej", "nn"), ArticleMetaImage("1", "Hola", "nb"))) @@ -717,7 +717,7 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { existingArticle, apiArticle, TestData.userWithWriteAccess - ) + ): @unchecked } @@ -759,19 +759,19 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { 1, TestData.newArticle.copy(availability = Some(Availability.teacher.toString)), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res2) = service.toDomainArticle( 1, TestData.newArticle.copy(availability = None), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res3) = service.toDomainArticle( 1, TestData.newArticle.copy(availability = Some("Krutte go")), TestData.userWithWriteAccess - ) + ): @unchecked res1.availability should be(Availability.teacher) res1.availability should not be (Availability.everyone) @@ -785,19 +785,19 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { TestData.sampleDomainArticle.copy(availability = Availability.everyone), TestData.sampleApiUpdateArticle.copy(availability = Some(Availability.teacher.toString)), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res2) = service.toDomainArticle( TestData.sampleDomainArticle.copy(availability = Availability.everyone), TestData.sampleApiUpdateArticle.copy(availability = Some("Krutte go")), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res3) = service.toDomainArticle( TestData.sampleDomainArticle.copy(availability = Availability.teacher), TestData.sampleApiUpdateArticle.copy(availability = None), TestData.userWithWriteAccess - ) + ): @unchecked res1.availability should be(Availability.teacher) res2.availability should be(Availability.everyone) @@ -819,7 +819,7 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { .copy(status = Status(PLANNED, Set()), notes = existingNotes, responsible = Some(existingRepsonsible)), updatedArticleWithNotes.copy(language = Some("nb"), responsibleId = UpdateWith("nyid")), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res2) = service.toDomainArticle( @@ -827,14 +827,14 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { .copy(status = Status(PLANNED, Set()), notes = existingNotes, responsible = None), updatedArticleWithNotes.copy(language = Some("nb"), responsibleId = UpdateWith("nyid")), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res3) = service.toDomainArticle( TestData.sampleDomainArticle .copy(status = Status(PLANNED, Set()), notes = existingNotes, responsible = Some(existingRepsonsible)), updatedArticleWithNotes.copy(language = Some("nb")), TestData.userWithWriteAccess - ) + ): @unchecked res1.notes.map(_.note) should be(Seq("swoop", "fleibede", "Ansvarlig endret.")) res2.notes.map(_.note) should be(Seq("swoop", "fleibede", "Ansvarlig endret.")) @@ -854,21 +854,21 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { .copy(status = Status(PLANNED, Set()), responsible = Some(existingRepsonsible)), updatedArticle.copy(language = Some("nb"), responsibleId = UpdateWith("nyid")), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res2) = service.toDomainArticle( TestData.sampleDomainArticle .copy(status = Status(PLANNED, Set()), responsible = None), updatedArticle.copy(language = Some("nb"), responsibleId = UpdateWith("nyid")), TestData.userWithWriteAccess - ) + ): @unchecked val Success(res3) = service.toDomainArticle( TestData.sampleDomainArticle .copy(status = Status(PLANNED, Set()), responsible = Some(existingRepsonsible)), updatedArticle.copy(language = Some("nb"), responsibleId = UpdateWith("oldId")), TestData.userWithWriteAccess - ) + ): @unchecked res1.responsible.get.responsibleId should be("nyid") res1.responsible.get.lastUpdated should not be (yesterday) @@ -948,7 +948,7 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { test("that toArticleApiArticle fails if copyright is not present") { val draft = TestData.sampleDomainArticle.copy(copyright = None) - val Failure(result1: ValidationException) = service.toArticleApiArticle(draft) + val Failure(result1: ValidationException) = service.toArticleApiArticle(draft): @unchecked result1.errors.head.message should be("Copyright must be present when publishing an article") } diff --git a/draft-api/src/test/scala/no/ndla/draftapi/service/ReadServiceTest.scala b/draft-api/src/test/scala/no/ndla/draftapi/service/ReadServiceTest.scala index e5b0b95f29..b9b3844bea 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/service/ReadServiceTest.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/service/ReadServiceTest.scala @@ -20,9 +20,7 @@ import scalikejdbc.DBSession import scala.util.{Failure, Success} class ReadServiceTest extends UnitSuite with TestEnvironment { - import props.externalApiUrls - - val externalImageApiUrl: String = externalApiUrls("image") + val externalImageApiUrl: String = props.externalApiUrls("image") val resourceIdAttr: String = s"${TagAttribute.DataResource_Id}" val resourceAttr: String = s"${TagAttribute.DataResource}" val imageType: String = s"${ResourceType.Image}" @@ -42,8 +40,8 @@ class ReadServiceTest extends UnitSuite with TestEnvironment { val articleContent2: ArticleContent = ArticleContent(content2, "und") - override val readService = new ReadService - override val converterService = new ConverterService + override lazy val readService = new ReadService + override lazy val converterService = new ConverterService test("withId adds urls and ids on embed resources") { val visualElementBefore = @@ -128,7 +126,7 @@ class ReadServiceTest extends UnitSuite with TestEnvironment { fallback = true, page = 1, pageSize = 10 - ) + ): @unchecked result.length should be(3) verify(draftRepository, times(1)).withIds(any, any, any)(any) @@ -143,7 +141,7 @@ class ReadServiceTest extends UnitSuite with TestEnvironment { fallback = true, page = 1, pageSize = 10 - ) + ): @unchecked result.errors.head.message should be("Query parameter 'ids' is missing") verify(draftRepository, times(0)).withIds(any, any, any)(any) diff --git a/draft-api/src/test/scala/no/ndla/draftapi/service/StateTransitionRulesTest.scala b/draft-api/src/test/scala/no/ndla/draftapi/service/StateTransitionRulesTest.scala index 5c469e215b..ef8e402560 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/service/StateTransitionRulesTest.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/service/StateTransitionRulesTest.scala @@ -47,7 +47,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { test("doTransition should succeed when performing a legal transition") { val expected = common.Status(PUBLISHED, Set.empty) val (Success(res), _) = - doTransitionWithoutSideEffect(InProcessArticle, PUBLISHED, TestData.userWithAdminAccess) + doTransitionWithoutSideEffect(InProcessArticle, PUBLISHED, TestData.userWithAdminAccess): @unchecked res.status should equal(expected) } @@ -59,7 +59,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { InProcessArticle.copy(status = ExternalReviewStatus), EXTERNAL_REVIEW, TestData.userWithPublishAccess - ) + ): @unchecked res.status should equal(expected) val expected2 = common.Status(IN_PROGRESS, Set(PUBLISHED)) @@ -68,7 +68,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { InProcessArticle.copy(status = PublishedStatus), IN_PROGRESS, TestData.userWithPublishAccess - ) + ): @unchecked res2.status should equal(expected2) } @@ -80,7 +80,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { InProcessArticle.copy(status = PublishedStatus), ARCHIVED, TestData.userWithPublishAccess - ) + ): @unchecked res1.status should equal(expected1) val expected2 = common.Status(ARCHIVED, Set.empty) @@ -89,7 +89,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { InProcessArticle.copy(status = UnpublishedStatus), ARCHIVED, TestData.userWithPublishAccess - ) + ): @unchecked res2.status should equal(expected2) val expected3 = common.Status(ARCHIVED, Set.empty) @@ -98,7 +98,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { InProcessArticle.copy(status = InProcessStatus), ARCHIVED, TestData.userWithPublishAccess - ) + ): @unchecked res3.status should equal(expected3) val expected4 = common.Status(ARCHIVED, Set.empty) @@ -107,7 +107,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { InProcessArticle.copy(status = ExternalReviewStatus), ARCHIVED, TestData.userWithPublishAccess - ) + ): @unchecked res4.status should equal(expected4) val expected5 = common.Status(ARCHIVED, Set.empty) @@ -116,7 +116,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { InProcessArticle.copy(status = PlannedStatus), ARCHIVED, TestData.userWithPublishAccess - ) + ): @unchecked res5.status should equal(expected5) val expected6 = common.Status(ARCHIVED, Set.empty) @@ -125,7 +125,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { InProcessArticle.copy(status = PublishedStatus), ARCHIVED, TestData.userWithPublishAccess - ) + ): @unchecked res6.status should equal(expected6) } @@ -157,7 +157,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { .thenReturn(Success(expectedArticle)) val (Success(res), sideEffect) = - doTransitionWithoutSideEffect(InProcessArticle, PUBLISHED, TestData.userWithAdminAccess) + doTransitionWithoutSideEffect(InProcessArticle, PUBLISHED, TestData.userWithAdminAccess): @unchecked sideEffect.map(sf => sf.run(res, TestData.userWithAdminAccess).get.status should equal(expectedStatus)) val captor = ArgumentCaptor.forClass(classOf[Draft]) @@ -180,7 +180,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { when(articleApiClient.unpublishArticle(any[Draft], any)).thenReturn(Success(expectedArticle)) val (Success(res), sideEffect) = - doTransitionWithoutSideEffect(PublishedArticle, UNPUBLISHED, TestData.userWithAdminAccess) + doTransitionWithoutSideEffect(PublishedArticle, UNPUBLISHED, TestData.userWithAdminAccess): @unchecked sideEffect.map(sf => sf.run(res, TestData.userWithAdminAccess).get.status should equal(expectedStatus)) val captor = ArgumentCaptor.forClass(classOf[Draft]) @@ -199,7 +199,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { when(articleIndexService.deleteDocument(UnpublishedArticle.id.get)).thenReturn(Success(UnpublishedArticle.id.get)) val (Success(res), sideEffect) = - doTransitionWithoutSideEffect(UnpublishedArticle, ARCHIVED, TestData.userWithPublishAccess) + doTransitionWithoutSideEffect(UnpublishedArticle, ARCHIVED, TestData.userWithPublishAccess): @unchecked sideEffect.map(sf => sf.run(res, TestData.userWithAdminAccess).get.status should equal(expectedStatus)) verify(articleIndexService, times(0)) @@ -209,7 +209,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { test("user without required roles should not be permitted to perform the status transition") { val proposalArticle = TestData.sampleArticleWithByNcSa.copy(status = InProcessStatus) val (Failure(ex: IllegalStatusStateTransition), _) = - doTransitionWithoutSideEffect(proposalArticle, PUBLISHED, TestData.userWithWriteAccess) + doTransitionWithoutSideEffect(proposalArticle, PUBLISHED, TestData.userWithWriteAccess): @unchecked ex.getMessage should equal("Cannot go to PUBLISHED when article is IN_PROGRESS") } @@ -217,7 +217,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { val expected = common.Status(UNPUBLISHED, Set()) val publishedArticle = InProcessArticle.copy(status = common.Status(current = PUBLISHED, other = Set())) val (Success(res), _) = - doTransitionWithoutSideEffect(publishedArticle, UNPUBLISHED, TestData.userWithAdminAccess) + doTransitionWithoutSideEffect(publishedArticle, UNPUBLISHED, TestData.userWithAdminAccess): @unchecked res.status should equal(expected) } @@ -243,7 +243,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { when(searchApiClient.publishedWhereUsed(any[Long], any)).thenReturn(Seq(result)) val Failure(res: ValidationException) = - StateTransitionRules.checkIfArticleIsInUse.run(article, TestData.userWithAdminAccess) + StateTransitionRules.checkIfArticleIsInUse.run(article, TestData.userWithAdminAccess): @unchecked res.errors should equal( Seq( ValidationMessage("status.current", "Article is in use in these published article(s) 1 (Title)") @@ -286,7 +286,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { when(taxonomyApiClient.queryNodes(articleId)).thenReturn(Success(List.empty)) val Failure(res: ValidationException) = - StateTransitionRules.checkIfArticleIsInUse.run(article, TestData.userWithAdminAccess) + StateTransitionRules.checkIfArticleIsInUse.run(article, TestData.userWithAdminAccess): @unchecked res.errors.head.message should equal("Learningpath(s) 1 (Title) contains a learning step that uses this article") } @@ -298,7 +298,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { when(draftRepository.getIdFromExternalId(any[String])(any[DBSession])).thenReturn(Some(articleId)) val Failure(res: ValidationException) = - StateTransitionRules.checkIfArticleIsInUse.run(article, TestData.userWithAdminAccess) + StateTransitionRules.checkIfArticleIsInUse.run(article, TestData.userWithAdminAccess): @unchecked res.errors.head.message should equal("Learningpath(s) 1 (Title) contains a learning step that uses this article") } @@ -422,7 +422,7 @@ class StateTransitionRulesTest extends UnitSuite with TestEnvironment { .thenReturn(Success(expectedArticle)) val (Success(res), sideEffect) = - doTransitionWithoutSideEffect(InProcessArticle, PUBLISHED, TestData.userWithAdminAccess) + doTransitionWithoutSideEffect(InProcessArticle, PUBLISHED, TestData.userWithAdminAccess): @unchecked sideEffect.map(sf => sf.run(res, TestData.userWithAdminAccess).get.status should equal(expectedStatus)) val captor = ArgumentCaptor.forClass(classOf[Draft]) diff --git a/draft-api/src/test/scala/no/ndla/draftapi/service/WriteServiceTest.scala b/draft-api/src/test/scala/no/ndla/draftapi/service/WriteServiceTest.scala index c936914061..0c80726404 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/service/WriteServiceTest.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/service/WriteServiceTest.scala @@ -34,7 +34,7 @@ import scala.concurrent.Future import scala.util.{Failure, Success, Try} class WriteServiceTest extends UnitSuite with TestEnvironment { - override val converterService = new ConverterService + override lazy val converterService = new ConverterService val today: NDLADate = NDLADate.now() val yesterday: NDLADate = NDLADate.now().minusDays(1) @@ -65,7 +65,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { doAnswer((i: InvocationOnMock) => { val x = i.getArgument[DBSession => Try[?]](0) x(mock[DBSession]) - }).when(DBUtil).rollbackOnFailure(any) + }).when(DBUtil).rollbackOnFailure(any()) when(draftRepository.withId(eqTo(articleId))(any)).thenReturn(Option(article)) when(articleIndexService.indexDocument(any[Draft])).thenAnswer((invocation: InvocationOnMock) => @@ -261,7 +261,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { existing.id.get, updatedArticle, TestData.userWithWriteAccess - ) + ): @unchecked result.status should equal(api.StatusDTO("IN_PROGRESS", Seq.empty)) } @@ -279,7 +279,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { existing.id.get, updatedArticle, TestData.userWithWriteAccess - ) + ): @unchecked result.status should equal(api.StatusDTO("IN_PROGRESS", Seq("PUBLISHED"))) } @@ -296,7 +296,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { existing.id.get, updatedArticle, TestData.userWithWriteAccess - ) + ): @unchecked result.status should equal(api.StatusDTO("IN_PROGRESS", Seq.empty)) } @@ -310,7 +310,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { existing.id.get, updatedArticle, TestData.userWithWriteAccess - ) + ): @unchecked result.status should equal(api.StatusDTO("EXTERNAL_REVIEW", Seq.empty)) } @@ -324,7 +324,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { existing.id.get, updatedArticle, TestData.userWithWriteAccess - ) + ): @unchecked result.status should equal(api.StatusDTO("INTERNAL_REVIEW", Seq.empty)) } @@ -344,13 +344,13 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { existing.id.get, updatedArticle, TestData.userWithWriteAccess - ) + ): @unchecked result.status should equal(api.StatusDTO("END_CONTROL", Seq.empty)) } } test("That delete article should fail when only one language") { - val Failure(result) = service.deleteLanguage(article.id.get, "nb", TokenUser("asdf", Set(), None)) + val Failure(result) = service.deleteLanguage(article.id.get, "nb", TokenUser("asdf", Set(), None)): @unchecked result.getMessage should equal("Only one language left") } @@ -571,7 +571,8 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { val existing = TestData.sampleDomainArticle.copy(status = TestData.statusWithPublished) when(draftRepository.withId(eqTo(existing.id.get))(any)).thenReturn(Some(existing)) - val Success(result1) = service.updateArticle(existing.id.get, updatedArticle, TestData.userWithWriteAccess) + val Success(result1) = + service.updateArticle(existing.id.get, updatedArticle, TestData.userWithWriteAccess): @unchecked result1.status.current should be(existing.status.current.toString) result1.status.other.sorted should be(existing.status.other.map(_.toString).toSeq.sorted) } @@ -591,7 +592,8 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { status = TestData.statusWithPublished ) when(draftRepository.withId(eqTo(existing.id.get))(any)).thenReturn(Some(existing)) - val Success(result1) = service.updateArticle(existing.id.get, updatedArticle, TestData.userWithWriteAccess) + val Success(result1) = + service.updateArticle(existing.id.get, updatedArticle, TestData.userWithWriteAccess): @unchecked result1.status.current should be(existing.status.current.toString) result1.status.other.sorted should be(existing.status.other.map(_.toString).toSeq.sorted) } @@ -636,7 +638,8 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { when(writeService.partialPublish(any, any, any, any)).thenReturn((existing.id.get, Success(existing))) when(articleApiClient.partialPublishArticle(any, any, any)).thenReturn(Success(existing.id.get)) - val Success(result1) = service.updateArticle(existing.id.get, updatedArticle, TestData.userWithWriteAccess) + val Success(result1) = + service.updateArticle(existing.id.get, updatedArticle, TestData.userWithWriteAccess): @unchecked result1.status.current should be(existing.status.current.toString) result1.status.other.sorted should be(existing.status.other.map(_.toString).toSeq.sorted) @@ -687,7 +690,8 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { when(writeService.partialPublish(any, any, any, any)).thenReturn((existing.id.get, Success(existing))) when(articleApiClient.partialPublishArticle(any, any, any)).thenReturn(Success(existing.id.get)) - val Success(result1) = service.updateArticle(existing.id.get, updatedArticle, TestData.userWithWriteAccess) + val Success(result1) = + service.updateArticle(existing.id.get, updatedArticle, TestData.userWithWriteAccess): @unchecked result1.status.current should not be existing.status.current.toString result1.status.current should be(DraftStatus.IN_PROGRESS.toString) @@ -738,7 +742,8 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { when(writeService.partialPublish(any, any, any, any)).thenReturn((existing.id.get, Success(existing))) when(articleApiClient.partialPublishArticle(any, any, any)).thenReturn(Success(existing.id.get)) - val Success(result1) = service.updateArticle(existing.id.get, updatedArticle, TestData.userWithWriteAccess) + val Success(result1) = + service.updateArticle(existing.id.get, updatedArticle, TestData.userWithWriteAccess): @unchecked result1.status.current should not be existing.status.current.toString result1.status.current should be(DraftStatus.IN_PROGRESS.toString) @@ -1061,7 +1066,8 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { when(writeService.partialPublish(any, any, any, any)).thenReturn((existing.id.get, Success(existing))) when(articleApiClient.partialPublishArticle(any, any, any)).thenReturn(Success(existing.id.get)) - val Success(result1) = service.updateArticle(existing.id.get, updatedArticle, TestData.userWithWriteAccess) + val Success(result1) = + service.updateArticle(existing.id.get, updatedArticle, TestData.userWithWriteAccess): @unchecked result1.status.current should be(existing.status.current.toString) result1.status.other.sorted should be(existing.status.other.map(_.toString).toSeq.sorted) @@ -1094,7 +1100,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { when(draftRepository.newEmptyArticleId()(any[DBSession])).thenReturn(Success(10L)) - val Success(_) = service.newArticle(newArt, TestData.userWithWriteAccess) + val Success(_) = service.newArticle(newArt, TestData.userWithWriteAccess): @unchecked val captor: ArgumentCaptor[Draft] = ArgumentCaptor.forClass(classOf[Draft]) Mockito.verify(draftRepository).insert(captor.capture())(any) diff --git a/draft-api/src/test/scala/no/ndla/draftapi/service/search/ArticleSearchServiceTest.scala b/draft-api/src/test/scala/no/ndla/draftapi/service/search/ArticleSearchServiceTest.scala index b59b35375e..b6e6bfa150 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/service/search/ArticleSearchServiceTest.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/service/search/ArticleSearchServiceTest.scala @@ -21,16 +21,14 @@ import no.ndla.scalatestsuite.ElasticsearchIntegrationSuite import scala.util.Success class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEnvironment { - import props.{DefaultLanguage, DefaultPageSize, MaxPageSize} - e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("http://localhost:9200")) - override val articleSearchService = new ArticleSearchService - override val articleIndexService: ArticleIndexService = new ArticleIndexService { + override lazy val articleSearchService = new ArticleSearchService + override lazy val articleIndexService: ArticleIndexService = new ArticleIndexService { override val indexShards = 1 } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService val byNcSa: DraftCopyright = DraftCopyright( Some(License.CC_BY_NC_SA.toString), @@ -232,21 +230,25 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn } test("That getStartAtAndNumResults returns SEARCH_MAX_PAGE_SIZE for value greater than SEARCH_MAX_PAGE_SIZE") { - articleSearchService.getStartAtAndNumResults(0, 10001) should equal((0, MaxPageSize)) + articleSearchService.getStartAtAndNumResults(0, 10001) should equal((0, props.MaxPageSize)) } test( "That getStartAtAndNumResults returns the correct calculated start at for page and page-size with default page-size" ) { val page = 74 - val expectedStartAt = (page - 1) * DefaultPageSize - articleSearchService.getStartAtAndNumResults(page, DefaultPageSize) should equal((expectedStartAt, DefaultPageSize)) + val expectedStartAt = (page - 1) * props.DefaultPageSize + articleSearchService.getStartAtAndNumResults(page, props.DefaultPageSize) should equal( + (expectedStartAt, props.DefaultPageSize) + ) } test("That getStartAtAndNumResults returns the correct calculated start at for page and page-size") { val page = 123 - val expectedStartAt = (page - 1) * DefaultPageSize - articleSearchService.getStartAtAndNumResults(page, DefaultPageSize) should equal((expectedStartAt, DefaultPageSize)) + val expectedStartAt = (page - 1) * props.DefaultPageSize + articleSearchService.getStartAtAndNumResults(page, props.DefaultPageSize) should equal( + (expectedStartAt, props.DefaultPageSize) + ) } test("all should return only articles of a given type if a type filter is specified") { @@ -254,16 +256,16 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn searchSettings.copy( articleTypes = Seq(ArticleType.TopicArticle.entryName) ) - ) + ): @unchecked results.totalCount should be(3) results.results.map(_.id) should be(Seq(8, 9, 11)) val Success(results2) = articleSearchService.matchingQuery( searchSettings.copy( - searchLanguage = DefaultLanguage, + searchLanguage = props.DefaultLanguage, articleTypes = ArticleType.all ) - ) + ): @unchecked results2.totalCount should be(9) } @@ -272,7 +274,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn searchSettings.copy( sort = Sort.ByIdAsc ) - ) + ): @unchecked val hits = results.results results.totalCount should be(9) hits.head.id should be(1) @@ -291,7 +293,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn searchSettings.copy( sort = Sort.ByIdDesc ) - ) + ): @unchecked val hits = results.results results.totalCount should be(9) hits.head.id should be(11) @@ -303,7 +305,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn searchSettings.copy( sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits = results.results results.totalCount should be(9) hits.head.id should be(8) @@ -322,7 +324,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn searchSettings.copy( sort = Sort.ByTitleDesc ) - ) + ): @unchecked val hits = results.results results.totalCount should be(9) hits.head.id should be(7) @@ -341,7 +343,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn searchSettings.copy( sort = Sort.ByLastUpdatedDesc ) - ) + ): @unchecked val hits = results.results results.totalCount should be(9) hits.head.id should be(3) @@ -353,7 +355,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn searchSettings.copy( sort = Sort.ByLastUpdatedAsc ) - ) + ): @unchecked val hits = results.results results.totalCount should be(9) hits.map(_.id) should be(Seq(5, 6, 7, 8, 9, 11, 1, 2, 3)) @@ -365,7 +367,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn license = Some(License.PublicDomain.toString), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits = results.results results.totalCount should be(8) hits.map(_.id) should be(Seq(8, 9, 3, 5, 11, 6, 2, 7)) @@ -376,7 +378,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn searchSettings.copy( withIdIn = List(1, 3) ) - ) + ): @unchecked val hits = results.results results.totalCount should be(2) hits.head.id should be(1) @@ -390,7 +392,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn pageSize = 2, sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits1 = page1.results page1.totalCount should be(9) page1.page.get should be(1) @@ -404,7 +406,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn pageSize = 2, sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits2 = page2.results page2.totalCount should be(9) @@ -421,7 +423,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn sort = Sort.ByRelevanceDesc, articleTypes = Seq(ArticleType.TopicArticle.entryName) ) - ) + ): @unchecked results.totalCount should be(0) val Success(results2) = articleSearchService.matchingQuery( @@ -430,7 +432,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn sort = Sort.ByRelevanceDesc, articleTypes = Seq(ArticleType.Standard.entryName) ) - ) + ): @unchecked results2.totalCount should be(3) } @@ -441,7 +443,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn query = Some("bil"), sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked val hits = results.results results.totalCount should be(3) @@ -455,7 +457,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn withIdIn = List(3), sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked val hits = results.results results.totalCount should be(1) hits.head.id should be(3) @@ -467,7 +469,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn query = Some("Pingvinen"), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits = results.results results.totalCount should be(1) hits.head.id should be(2) @@ -479,7 +481,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn query = Some("and"), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits = results.results results.totalCount should be(1) hits.head.id should be(3) @@ -491,7 +493,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn query = Some("supermann"), sort = Sort.ByTitleAsc ) - ) + ): @unchecked results.totalCount should be(0) } @@ -502,7 +504,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn license = Some(License.Copyrighted.toString), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits = results.results results.totalCount should be(1) hits.head.id should be(4) @@ -514,7 +516,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn query = Some("bilde + bil"), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits1 = search1.results hits1.map(_.id) should equal(Seq(1, 3, 5)) @@ -523,7 +525,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn query = Some("batmen + bil"), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits2 = search2.results hits2.map(_.id) should equal(Seq(1)) @@ -532,7 +534,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn query = Some("bil + bilde - flaggermusmann"), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits3 = search3.results hits3.map(_.id) should equal(Seq(1, 3, 5)) @@ -541,7 +543,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn query = Some("bil - hulken"), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits4 = search4.results hits4.map(_.id) should equal(Seq(1, 3, 5)) } @@ -552,7 +554,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn query = Some("mareritt + ragnarok"), sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked val hits = search.results hits.map(_.id) should equal(Seq(9, 8)) } @@ -563,7 +565,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn query = Some("kakemonster"), sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked search.totalCount should be(1) search.results.head.id should be(5) } @@ -575,7 +577,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn sort = Sort.ByIdAsc, pageSize = 100 ) - ) + ): @unchecked val hits = search.results search.totalCount should equal(10) @@ -601,7 +603,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn sort = Sort.ByTitleAsc, pageSize = 100 ) - ) + ): @unchecked val hits = search.results search.totalCount should equal(1) @@ -615,14 +617,14 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn searchLanguage = Language.AllLanguages, sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked val Success(searchNb) = articleSearchService.matchingQuery( searchSettings.copy( query = Some("Store"), searchLanguage = Language.AllLanguages, sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked searchEn.totalCount should equal(1) searchEn.results.head.id should equal(11) @@ -642,7 +644,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn searchLanguage = "en", fallback = true ) - ) + ): @unchecked search.totalCount should equal(3) search.results.head.id should equal(9) @@ -654,7 +656,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn } test("That searching for language not in analyzers works as expected") { - val Success(search) = articleSearchService.matchingQuery(searchSettings.copy(searchLanguage = "biz")) + val Success(search) = articleSearchService.matchingQuery(searchSettings.copy(searchLanguage = "biz")): @unchecked search.totalCount should equal(1) search.results.head.id should equal(11) @@ -662,13 +664,13 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn } test("That searching for language not in index works as expected") { - val Success(search) = articleSearchService.matchingQuery(searchSettings.copy(searchLanguage = "mix")) + val Success(search) = articleSearchService.matchingQuery(searchSettings.copy(searchLanguage = "mix")): @unchecked search.totalCount should equal(0) } test("That searching for unsupported language code works as expected") { - val Success(search) = articleSearchService.matchingQuery(searchSettings.copy(searchLanguage = "asdf")) + val Success(search) = articleSearchService.matchingQuery(searchSettings.copy(searchLanguage = "asdf")): @unchecked search.totalCount should equal(0) } @@ -684,13 +686,13 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn pageSize = pageSize, shouldScroll = true ) - ) + ): @unchecked - val Success(scroll1) = articleSearchService.scroll(initialSearch.scrollId.get, "*") - val Success(scroll2) = articleSearchService.scroll(scroll1.scrollId.get, "*") - val Success(scroll3) = articleSearchService.scroll(scroll2.scrollId.get, "*") - val Success(scroll4) = articleSearchService.scroll(scroll3.scrollId.get, "*") - val Success(scroll5) = articleSearchService.scroll(scroll4.scrollId.get, "*") + val Success(scroll1) = articleSearchService.scroll(initialSearch.scrollId.get, "*"): @unchecked + val Success(scroll2) = articleSearchService.scroll(scroll1.scrollId.get, "*"): @unchecked + val Success(scroll3) = articleSearchService.scroll(scroll2.scrollId.get, "*"): @unchecked + val Success(scroll4) = articleSearchService.scroll(scroll3.scrollId.get, "*"): @unchecked + val Success(scroll5) = articleSearchService.scroll(scroll4.scrollId.get, "*"): @unchecked initialSearch.results.map(_.id) should be(expectedIds.head) scroll1.results.map(_.id) should be(expectedIds(1)) @@ -709,8 +711,8 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn pageSize = 1, shouldScroll = true ) - ) - val Success(scroll) = articleSearchService.scroll(initialSearch.scrollId.get, "*") + ): @unchecked + val Success(scroll) = articleSearchService.scroll(initialSearch.scrollId.get, "*"): @unchecked initialSearch.results.size should be(1) initialSearch.results.head.id should be(10) @@ -727,7 +729,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn query = Some("kyllingkanon"), sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked search.totalCount should be(1) search.results.head.id should be(5) @@ -741,7 +743,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn sort = Sort.ByRelevanceDesc, fallback = true ) - ) + ): @unchecked search.results.map(_.id) should be(Seq(10)) } @@ -754,7 +756,7 @@ class ArticleSearchServiceTest extends ElasticsearchIntegrationSuite with TestEn fallback = true, grepCodes = Seq("KM1234") ) - ) + ): @unchecked search.totalCount should be(1) search.results.head.id should be(5) } diff --git a/draft-api/src/test/scala/no/ndla/draftapi/service/search/DraftSearchConverterServiceTest.scala b/draft-api/src/test/scala/no/ndla/draftapi/service/search/DraftSearchConverterServiceTest.scala index 82ad14ad40..f12738bfa4 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/service/search/DraftSearchConverterServiceTest.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/service/search/DraftSearchConverterServiceTest.scala @@ -16,9 +16,8 @@ import no.ndla.search.model.{SearchableLanguageList, SearchableLanguageValues} import no.ndla.common.model.domain.draft.Draft class DraftSearchConverterServiceTest extends UnitSuite with TestEnvironment { - - override val searchConverterService = new SearchConverterService - val sampleArticle: Draft = TestData.sampleArticleWithPublicDomain.copy() + override lazy val searchConverterService = new SearchConverterService + val sampleArticle: Draft = TestData.sampleArticleWithPublicDomain.copy() val titles: List[Title] = List( Title("Bokmål tittel", "nb"), diff --git a/draft-api/src/test/scala/no/ndla/draftapi/service/search/GrepCodesIndexServiceTest.scala b/draft-api/src/test/scala/no/ndla/draftapi/service/search/GrepCodesIndexServiceTest.scala index e5f65ff20a..d68b92b421 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/service/search/GrepCodesIndexServiceTest.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/service/search/GrepCodesIndexServiceTest.scala @@ -15,11 +15,11 @@ class GrepCodesIndexServiceTest extends ElasticsearchIntegrationSuite with TestE e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("http://localhost:9200")) - override val grepCodesIndexService: GrepCodesIndexService = new GrepCodesIndexService { + override lazy val grepCodesIndexService: GrepCodesIndexService = new GrepCodesIndexService { override val indexShards = 1 } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService test("That indexing does not fail if no grepCodes are present") { tagIndexService.createIndexWithName(props.DraftGrepCodesSearchIndex) diff --git a/draft-api/src/test/scala/no/ndla/draftapi/service/search/GrepCodesSearchServiceTest.scala b/draft-api/src/test/scala/no/ndla/draftapi/service/search/GrepCodesSearchServiceTest.scala index d452824008..171fe985ac 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/service/search/GrepCodesSearchServiceTest.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/service/search/GrepCodesSearchServiceTest.scala @@ -18,12 +18,12 @@ class GrepCodesSearchServiceTest extends ElasticsearchIntegrationSuite with Test e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("http://localhost:9200")) - override val grepCodesSearchService = new GrepCodesSearchService - override val grepCodesIndexService: GrepCodesIndexService = new GrepCodesIndexService { + override lazy val grepCodesSearchService = new GrepCodesSearchService + override lazy val grepCodesIndexService: GrepCodesIndexService = new GrepCodesIndexService { override val indexShards: Int = 1 } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService val article1: Draft = TestData.sampleDomainArticle.copy( grepCodes = Seq("KE101", "KE115", "TT555") @@ -57,31 +57,31 @@ class GrepCodesSearchServiceTest extends ElasticsearchIntegrationSuite with Test } test("That searching for grepcodes returns sensible results") { - val Success(result) = grepCodesSearchService.matchingQuery("KE", 1, 100) + val Success(result) = grepCodesSearchService.matchingQuery("KE", 1, 100): @unchecked result.totalCount should be(3) result.results should be(Seq("KE101", "KE115", "KE105")) - val Success(result2) = grepCodesSearchService.matchingQuery("KE115", 1, 100) + val Success(result2) = grepCodesSearchService.matchingQuery("KE115", 1, 100): @unchecked result2.totalCount should be(1) result2.results should be(Seq("KE115")) } test("That searching for grepcodes returns sensible results even if lowercase") { - val Success(result) = grepCodesSearchService.matchingQuery("ke", 1, 100) + val Success(result) = grepCodesSearchService.matchingQuery("ke", 1, 100): @unchecked result.totalCount should be(3) result.results should be(Seq("KE101", "KE115", "KE105")) - val Success(result2) = grepCodesSearchService.matchingQuery("ke115", 1, 100) + val Success(result2) = grepCodesSearchService.matchingQuery("ke115", 1, 100): @unchecked result2.totalCount should be(1) result2.results should be(Seq("KE115")) } test("That only prefixes are matched with grepcodes") { - val Success(result) = grepCodesSearchService.matchingQuery("TT", 1, 100) + val Success(result) = grepCodesSearchService.matchingQuery("TT", 1, 100): @unchecked result.totalCount should be(1) result.results should be(Seq("TT555")) diff --git a/draft-api/src/test/scala/no/ndla/draftapi/service/search/TagIndexServiceTest.scala b/draft-api/src/test/scala/no/ndla/draftapi/service/search/TagIndexServiceTest.scala index 5a98ac2302..eb1d2ea287 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/service/search/TagIndexServiceTest.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/service/search/TagIndexServiceTest.scala @@ -15,11 +15,11 @@ class TagIndexServiceTest extends ElasticsearchIntegrationSuite with TestEnviron e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("http://localhost:9200")) - override val tagIndexService: TagIndexService = new TagIndexService { + override lazy val tagIndexService: TagIndexService = new TagIndexService { override val indexShards = 1 } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService test("That indexing does not fail if no tags are present") { tagIndexService.createIndexAndAlias() diff --git a/draft-api/src/test/scala/no/ndla/draftapi/service/search/TagSearchServiceTest.scala b/draft-api/src/test/scala/no/ndla/draftapi/service/search/TagSearchServiceTest.scala index 47b839a2aa..436b3fd23d 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/service/search/TagSearchServiceTest.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/service/search/TagSearchServiceTest.scala @@ -19,12 +19,12 @@ class TagSearchServiceTest extends ElasticsearchIntegrationSuite with TestEnviro e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("http://localhost:9200")) - override val tagSearchService = new TagSearchService - override val tagIndexService: TagIndexService = new TagIndexService { + override lazy val tagSearchService = new TagSearchService + override lazy val tagIndexService: TagIndexService = new TagIndexService { override val indexShards = 1 } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService val article1: Draft = TestData.sampleDomainArticle.copy( tags = Seq( @@ -84,14 +84,14 @@ class TagSearchServiceTest extends ElasticsearchIntegrationSuite with TestEnviro } test("That searching for tags returns sensible results") { - val Success(result) = tagSearchService.matchingQuery("test", "nb", 1, 100) + val Success(result) = tagSearchService.matchingQuery("test", "nb", 1, 100): @unchecked result.totalCount should be(3) result.results should be(Seq("test", "testemer", "testing")) } test("That only prefixes are matched") { - val Success(result) = tagSearchService.matchingQuery("kylling", "nb", 1, 100) + val Success(result) = tagSearchService.matchingQuery("kylling", "nb", 1, 100): @unchecked result.totalCount should be(1) result.results should be(Seq("kyllingfilet")) diff --git a/draft-api/src/test/scala/no/ndla/draftapi/validation/ContentValidatorTest.scala b/draft-api/src/test/scala/no/ndla/draftapi/validation/ContentValidatorTest.scala index 9b14f12d38..b39f5879f1 100644 --- a/draft-api/src/test/scala/no/ndla/draftapi/validation/ContentValidatorTest.scala +++ b/draft-api/src/test/scala/no/ndla/draftapi/validation/ContentValidatorTest.scala @@ -20,11 +20,11 @@ import java.util.UUID import scala.util.Failure class ContentValidatorTest extends UnitSuite with TestEnvironment { - override val contentValidator = new ContentValidator() - override val converterService: ConverterService = new ConverterService - val validDocument = """

heisann

heia

""" - val invalidDocument = """
""" - val validDisclaimer = + override lazy val contentValidator = new ContentValidator() + override lazy val converterService: ConverterService = new ConverterService + val validDocument = """

heisann

heia

""" + val invalidDocument = """
""" + val validDisclaimer = """

hallo!test

""" val articleToValidate: Draft = @@ -68,7 +68,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { content = Seq(ArticleContent(validDocument, "nb")), disclaimer = OptLanguageFields.withValue("

hei

", "nb") ) - val Failure(error: ValidationException) = contentValidator.validateArticle(article) + val Failure(error: ValidationException) = contentValidator.validateArticle(article): @unchecked val expected = ValidationException( "disclaimer.nb", "The content contains illegal tags and/or attributes. Allowed HTML tags are: h3, msgroup, a, article, sub, sup, mtext, msrow, tbody, mtd, pre, thead, figcaption, mover, msup, semantics, ol, span, mroot, munder, h4, mscarries, dt, nav, mtr, ndlaembed, li, br, mrow, merror, mphantom, u, audio, ul, maligngroup, mfenced, annotation, div, strong, section, i, mspace, malignmark, mfrac, code, h2, td, aside, em, mstack, button, dl, th, tfoot, math, tr, b, blockquote, msline, col, annotation-xml, mstyle, caption, mpadded, mo, mlongdiv, msubsup, p, munderover, maction, menclose, h1, details, mmultiscripts, msqrt, mscarry, mstac, mi, mglyph, mlabeledtr, mtable, mprescripts, summary, mn, msub, ms, table, colgroup, dd" @@ -127,7 +127,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { test("validateArticle should fail if the title exceeds 256 bytes") { val article = articleToValidate.copy(title = Seq(Title("A" * 257, "nb"))) - val Failure(ex: ValidationException) = contentValidator.validateArticle(article) + val Failure(ex: ValidationException) = contentValidator.validateArticle(article): @unchecked ex.errors.length should be(1) ex.errors.head.message should be("This field exceeds the maximum permitted length of 256 characters") @@ -135,7 +135,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { test("validateArticle should fail if the title is empty") { val article = articleToValidate.copy(title = Seq(Title("", "nb"))) - val Failure(ex: ValidationException) = contentValidator.validateArticle(article) + val Failure(ex: ValidationException) = contentValidator.validateArticle(article): @unchecked ex.errors.length should be(1) ex.errors.head.message should be("This field does not meet the minimum length requirement of 1 characters") @@ -143,7 +143,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { test("validateArticle should fail if the title is whitespace") { val article = articleToValidate.copy(title = Seq(Title(" ", "nb"))) - val Failure(ex: ValidationException) = contentValidator.validateArticle(article) + val Failure(ex: ValidationException) = contentValidator.validateArticle(article): @unchecked ex.errors.length should be(1) ex.errors.head.message should be("This field does not meet the minimum length requirement of 1 characters") @@ -317,7 +317,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { test("validation should fail if metaImage altText contains html") { val article = articleToValidate.copy(metaImage = Seq(ArticleMetaImage("1234", "Ikke krutte god", "nb"))) - val Failure(res1: ValidationException) = contentValidator.validateArticle(article) + val Failure(res1: ValidationException) = contentValidator.validateArticle(article): @unchecked res1.errors should be( Seq(ValidationMessage("metaImage.alt", "The content contains illegal html-characters. No HTML is allowed")) ) @@ -332,7 +332,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { articleToValidate.copy( metaImage = Seq(ArticleMetaImage("", "alt-text", "nb")) ) - ) + ): @unchecked res.errors.length should be(1) res.errors.head.field should be("metaImageId") @@ -345,7 +345,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { articleToValidate.copy( revisionMeta = Seq.empty ) - ) + ): @unchecked res.errors.length should be(1) res.errors.head.field should be("revisionMeta") @@ -359,7 +359,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { articleType = ArticleType.TopicArticle, slug = Some("pepe") ) - ) + ): @unchecked res.errors.length should be(1) res.errors.head.field should be("articleType") @@ -375,7 +375,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { articleType = ArticleType.FrontpageArticle, slug = None ) - ) + ): @unchecked res.errors.length should be(1) res.errors.head.field should be("slug") @@ -391,7 +391,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { articleType = ArticleType.FrontpageArticle, slug = Some("ugyldig slug") ) - ) + ): @unchecked res.errors.length should be(1) res.errors.head.field should be("slug") @@ -409,7 +409,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { ) contentValidator.validateArticleOnLanguage(article, Some("nb")).failIfFailure - val Failure(error: ValidationException) = contentValidator.validateArticle(article) + val Failure(error: ValidationException) = contentValidator.validateArticle(article): @unchecked val Seq(err1, err2) = error.errors err1.message.contains("The content contains illegal tags and/or attributes.") should be(true) err2.message should be("An article must consist of one or more
blocks. Illegal tag(s) are div ") @@ -469,7 +469,7 @@ class ContentValidatorTest extends UnitSuite with TestEnvironment { val result = contentValidator.validateArticleOnLanguage(Some(oldArticle), article, Some("nb")) result.isFailure should be(true) - val Failure(validationError: ValidationException) = result + val Failure(validationError: ValidationException) = result: @unchecked validationError.errors.length should be(1) validationError.errors.head.message should be("An article must contain at least one planned revision date") } diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/ComponentRegistry.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/ComponentRegistry.scala index 57ab4ae8ca..0053d59eda 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/ComponentRegistry.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/ComponentRegistry.scala @@ -39,27 +39,27 @@ class ComponentRegistry(properties: FrontpageApiProperties) with DBMigrator with ConverterService with SwaggerDocControllerConfig { - override val props: FrontpageApiProperties = properties - override val migrator: DBMigrator = DBMigrator() - override lazy val dataSource: HikariDataSource = DataSource.getHikariDataSource + override lazy val props: FrontpageApiProperties = properties + override lazy val migrator: DBMigrator = DBMigrator() + override lazy val dataSource: HikariDataSource = DataSource.getHikariDataSource - override val clock = new SystemClock + override lazy val clock = new SystemClock - override val subjectPageRepository = new SubjectPageRepository - override val frontPageRepository = new FrontPageRepository - override val filmFrontPageRepository = new FilmFrontPageRepository + override lazy val subjectPageRepository = new SubjectPageRepository + override lazy val frontPageRepository = new FrontPageRepository + override lazy val filmFrontPageRepository = new FilmFrontPageRepository - override val readService = new ReadService - override val writeService = new WriteService + override lazy val readService = new ReadService + override lazy val writeService = new WriteService - override val subjectPageController = new SubjectPageController - override val frontPageController = new FrontPageController - override val filmPageController = new FilmPageController - override val internController = new InternController - val healthController = new TapirHealthController + override lazy val subjectPageController = new SubjectPageController + override lazy val frontPageController = new FrontPageController + override lazy val filmPageController = new FilmPageController + override lazy val internController = new InternController + override lazy val healthController = new TapirHealthController - override val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient - override val ndlaClient: NdlaClient = new NdlaClient + override lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient + override lazy val ndlaClient: NdlaClient = new NdlaClient val swagger = new SwaggerController( List( diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/FrontpageApiProperties.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/FrontpageApiProperties.scala index 31a130a746..70bab4d869 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/FrontpageApiProperties.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/FrontpageApiProperties.scala @@ -15,7 +15,7 @@ import no.ndla.database.{DatabaseProps, HasDatabaseProps} import scala.util.Properties.* trait Props extends HasBaseProps with HasDatabaseProps { - val props: FrontpageApiProperties + lazy val props: FrontpageApiProperties } class FrontpageApiProperties extends BaseProps with DatabaseProps { diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/FilmPageController.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/FilmPageController.scala index 0769ca30aa..cb069058e1 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/FilmPageController.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/FilmPageController.scala @@ -23,7 +23,7 @@ import sttp.tapir.server.ServerEndpoint trait FilmPageController { this: ReadService & WriteService & ErrorHandling & TapirController => - val filmPageController: FilmPageController + lazy val filmPageController: FilmPageController class FilmPageController extends TapirController { override val serviceName: String = "filmfrontpage" diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/FrontPageController.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/FrontPageController.scala index 70baa53daa..272fff4cb6 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/FrontPageController.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/FrontPageController.scala @@ -8,7 +8,6 @@ package no.ndla.frontpageapi.controller -import io.circe.generic.auto.* import no.ndla.common.model.api.FrontPageDTO import no.ndla.frontpageapi.model.api.* import no.ndla.frontpageapi.service.{ReadService, WriteService} @@ -22,7 +21,7 @@ import sttp.tapir.server.ServerEndpoint trait FrontPageController { this: ReadService & WriteService & ErrorHandling & TapirController => - val frontPageController: FrontPageController + lazy val frontPageController: FrontPageController class FrontPageController() extends TapirController { override val serviceName: String = "frontpage" diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/InternController.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/InternController.scala index b66a684513..f0861f45f1 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/InternController.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/InternController.scala @@ -25,7 +25,7 @@ import scala.util.{Failure, Success} trait InternController { this: ReadService & WriteService & Props & ErrorHandling & TapirController => - val internController: InternController + lazy val internController: InternController class InternController extends TapirController { override val prefix: EndpointInput[Unit] = "intern" diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/SubjectPageController.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/SubjectPageController.scala index 45334b9fa5..1befba6162 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/SubjectPageController.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/controller/SubjectPageController.scala @@ -8,7 +8,6 @@ package no.ndla.frontpageapi.controller -import io.circe.generic.auto.* import no.ndla.common.errors.ValidationException import no.ndla.common.model.api.CommaSeparatedList.* import no.ndla.common.model.api.LanguageCode @@ -26,7 +25,7 @@ import sttp.tapir.server.ServerEndpoint trait SubjectPageController { this: ReadService & WriteService & Props & ErrorHandling & TapirController => - val subjectPageController: SubjectPageController + lazy val subjectPageController: SubjectPageController class SubjectPageController extends TapirController { override val serviceName: String = "subjectpage" diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V2__convert_subjects_to_object.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V2__convert_subjects_to_object.scala index 1f70f0c173..8cd1efb1ed 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V2__convert_subjects_to_object.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V2__convert_subjects_to_object.scala @@ -8,7 +8,6 @@ package no.ndla.frontpageapi.db.migration -import io.circe.generic.auto.* import io.circe.generic.semiauto.* import io.circe.parser.* import io.circe.syntax.* @@ -21,10 +20,6 @@ import scalikejdbc.* import scala.util.{Failure, Success} class V2__convert_subjects_to_object extends BaseJavaMigration { - - implicit val decoder: Decoder[V1_DBFrontPageData] = deriveDecoder - implicit val encoder: Encoder[V1_DBFrontPageData] = deriveEncoder - override def migrate(context: Context): Unit = DB(context.getConnection) .autoClose(false) .withinTx { implicit session => @@ -60,9 +55,33 @@ class V2__convert_subjects_to_object extends BaseJavaMigration { } case class V2_FrontPageData(topical: List[String], categories: List[V2_SubjectCollection]) +object V2_FrontPageData { + implicit val decoder: Decoder[V2_FrontPageData] = deriveDecoder + implicit val encoder: Encoder[V2_FrontPageData] = deriveEncoder +} case class V2_SubjectCollection(name: String, subjects: List[V2_SubjectFilters]) +object V2_SubjectCollection { + implicit val decoder: Decoder[V2_SubjectCollection] = deriveDecoder + implicit val encoder: Encoder[V2_SubjectCollection] = deriveEncoder +} case class V2_SubjectFilters(id: String, filters: List[String]) +object V2_SubjectFilters { + implicit val decoder: Decoder[V2_SubjectFilters] = deriveDecoder + implicit val encoder: Encoder[V2_SubjectFilters] = deriveEncoder +} case class V1_DBFrontPage(id: Long, document: String) +object V1_DBFrontPage { + implicit val decoder: Decoder[V1_DBFrontPage] = deriveDecoder + implicit val encoder: Encoder[V1_DBFrontPage] = deriveEncoder +} case class V1_DBFrontPageData(topical: List[String], categories: List[V1_DBSubjectCollection]) +object V1_DBFrontPageData { + implicit val decoder: Decoder[V1_DBFrontPageData] = deriveDecoder + implicit val encoder: Encoder[V1_DBFrontPageData] = deriveEncoder +} case class V1_DBSubjectCollection(name: String, subjects: List[String]) +object V1_DBSubjectCollection { + implicit val decoder: Decoder[V1_DBSubjectCollection] = deriveDecoder + implicit val encoder: Encoder[V1_DBSubjectCollection] = deriveEncoder +} diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V3__introduce_layout.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V3__introduce_layout.scala index 3fcceb6298..32e19192ad 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V3__introduce_layout.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V3__introduce_layout.scala @@ -8,7 +8,6 @@ package no.ndla.frontpageapi.db.migration -import io.circe.generic.auto.* import io.circe.generic.semiauto.* import io.circe.parser.* import io.circe.syntax.* @@ -22,8 +21,12 @@ import scala.util.{Failure, Success} class V3__introduce_layout extends BaseJavaMigration { - implicit val decoder: Decoder[V1_DBFrontPageData] = deriveDecoder - implicit val encoder: Encoder[V1_DBFrontPageData] = deriveEncoder + implicit val decoder: Decoder[V1_DBFrontPageData] = deriveDecoder + implicit val encoder: Encoder[V1_DBFrontPageData] = deriveEncoder + implicit val v2decoder: Decoder[V2_SubjectFrontPageData] = deriveDecoder + implicit val v2encoder: Encoder[V2_SubjectFrontPageData] = deriveEncoder + implicit val v3decoder: Decoder[V3_SubjectFrontPageData] = deriveDecoder + implicit val v3encoder: Encoder[V3_SubjectFrontPageData] = deriveEncoder override def migrate(context: Context): Unit = DB(context.getConnection) .autoClose(false) @@ -71,6 +74,10 @@ class V3__introduce_layout extends BaseJavaMigration { } case class V2_DBSubjectPage(id: Long, document: String) +object V2_DBSubjectPage { + implicit val encoder: Encoder[V2_DBSubjectPage] = deriveEncoder + implicit val decoder: Decoder[V2_DBSubjectPage] = deriveDecoder +} case class V2_SubjectFrontPageData( id: Option[Long], name: String, @@ -87,8 +94,20 @@ case class V2_SubjectFrontPageData( goTo: List[String] ) case class V2_BannerImage(mobileImageId: Long, desktopImageId: Long) +object V2_BannerImage { + implicit val encoder: Encoder[V2_BannerImage] = deriveEncoder + implicit val decoder: Decoder[V2_BannerImage] = deriveDecoder +} case class V2_AboutSubject(title: String, description: String, visualElement: V2_VisualElement) +object V2_AboutSubject { + implicit val encoder: Encoder[V2_AboutSubject] = deriveEncoder + implicit val decoder: Decoder[V2_AboutSubject] = deriveDecoder +} case class V2_VisualElement(`type`: String, id: String, alt: Option[String]) +object V2_VisualElement { + implicit val encoder: Encoder[V2_VisualElement] = deriveEncoder + implicit val decoder: Decoder[V2_VisualElement] = deriveDecoder +} case class V3_SubjectFrontPageData( id: Option[Long], @@ -105,3 +124,7 @@ case class V3_SubjectFrontPageData( latestContent: Option[List[String]], goTo: List[String] ) +object V3_SubjectFrontPageData { + implicit val encoder: Encoder[V3_SubjectFrontPageData] = deriveEncoder + implicit val decoder: Decoder[V3_SubjectFrontPageData] = deriveDecoder +} diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V4__add_language_to_about.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V4__add_language_to_about.scala index 703d106298..89f56c0905 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V4__add_language_to_about.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V4__add_language_to_about.scala @@ -8,7 +8,6 @@ package no.ndla.frontpageapi.db.migration -import io.circe.generic.auto.* import io.circe.generic.semiauto.* import io.circe.parser.parse import io.circe.syntax.* @@ -22,10 +21,6 @@ import scalikejdbc.* import scala.util.{Failure, Success} class V4__add_language_to_about extends BaseJavaMigration { - - implicit val decoder: Decoder[V1_DBFrontPageData] = deriveDecoder - implicit val encoder: Encoder[V1_DBFrontPageData] = deriveEncoder - override def migrate(context: Context): Unit = DB(context.getConnection) .autoClose(false) .withinTx { implicit session => @@ -90,4 +85,12 @@ case class V4_SubjectFrontPageData( latestContent: Option[List[String]], goTo: List[String] ) +object V4_SubjectFrontPageData { + implicit val encoder: Encoder[V4_SubjectFrontPageData] = deriveEncoder + implicit val decoder: Decoder[V4_SubjectFrontPageData] = deriveDecoder +} case class V4_AboutSubject(title: String, description: String, language: String, visualElement: V2_VisualElement) +object V4_AboutSubject { + implicit val encoder: Encoder[V4_AboutSubject] = deriveEncoder + implicit val decoder: Decoder[V4_AboutSubject] = deriveDecoder +} diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V5__add_meta_description.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V5__add_meta_description.scala index 71c84918f4..0db65e1de8 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V5__add_meta_description.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V5__add_meta_description.scala @@ -8,7 +8,6 @@ package no.ndla.frontpageapi.db.migration -import io.circe.generic.auto.* import io.circe.generic.semiauto.* import io.circe.parser.parse import io.circe.syntax.* @@ -23,8 +22,16 @@ import scala.util.{Failure, Success} class V5__add_meta_description extends BaseJavaMigration { - implicit val decoder: Decoder[V1_DBFrontPageData] = deriveDecoder - implicit val encoder: Encoder[V1_DBFrontPageData] = deriveEncoder + implicit val decoder: Decoder[V1_DBFrontPageData] = deriveDecoder + implicit val encoder: Encoder[V1_DBFrontPageData] = deriveEncoder + implicit val v4decoder: Decoder[V4_SubjectFrontPageData] = deriveDecoder + implicit val v4encoder: Encoder[V4_SubjectFrontPageData] = deriveEncoder + + implicit val v5decoder: Decoder[V5_SubjectFrontPageData] = deriveDecoder + implicit val v5encoder: Encoder[V5_SubjectFrontPageData] = deriveEncoder + + implicit val v5MetaDescriptionDecoder: Decoder[V5_MetaDescription] = deriveDecoder + implicit val v5MetaDescriptionEncoder: Encoder[V5_MetaDescription] = deriveEncoder override def migrate(context: Context): Unit = DB(context.getConnection) .autoClose(false) @@ -73,6 +80,10 @@ class V5__add_meta_description extends BaseJavaMigration { } case class V5_MetaDescription(metaDescription: String, language: String) +object V5_MetaDescription { + implicit val encoder: Encoder[V5_MetaDescription] = deriveEncoder + implicit val decoder: Decoder[V5_MetaDescription] = deriveDecoder +} case class V5_SubjectFrontPageData( id: Option[Long], name: String, @@ -89,3 +100,7 @@ case class V5_SubjectFrontPageData( latestContent: Option[List[String]], goTo: List[String] ) +object V5_SubjectFrontPageData { + implicit val encoder: Encoder[V5_SubjectFrontPageData] = deriveEncoder + implicit val decoder: Decoder[V5_SubjectFrontPageData] = deriveDecoder +} diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V8__add_subject_links.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V8__add_subject_links.scala index 054545d1c0..6d5a14a6b6 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V8__add_subject_links.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/db/migration/V8__add_subject_links.scala @@ -8,7 +8,6 @@ package no.ndla.frontpageapi.db.migration -import io.circe.generic.auto.* import io.circe.generic.semiauto.* import io.circe.parser.parse import io.circe.syntax.* @@ -22,10 +21,6 @@ import scalikejdbc.* import scala.util.{Failure, Success} class V8__add_subject_links extends BaseJavaMigration { - - implicit val decoder: Decoder[V1_DBFrontPageData] = deriveDecoder - implicit val encoder: Encoder[V1_DBFrontPageData] = deriveEncoder - override def migrate(context: Context): Unit = DB(context.getConnection) .autoClose(false) .withinTx { implicit session => @@ -78,3 +73,7 @@ case class V8_SubjectFrontPageData( buildsOn: List[String], leadsTo: List[String] ) +object V8_SubjectFrontPageData { + implicit val encoder: Encoder[V8_SubjectFrontPageData] = deriveEncoder + implicit val decoder: Decoder[V8_SubjectFrontPageData] = deriveDecoder +} diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdateBannerImageDTO.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdateBannerImageDTO.scala index 8e54d9eaf4..4c5308fd39 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdateBannerImageDTO.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdateBannerImageDTO.scala @@ -8,4 +8,11 @@ package no.ndla.frontpageapi.model.api +import io.circe.generic.semiauto.{deriveEncoder, deriveDecoder} +import io.circe.{Encoder, Decoder} + case class NewOrUpdateBannerImageDTO(mobileImageId: Option[Long], desktopImageId: Long) +object NewOrUpdateBannerImageDTO { + implicit val encoder: Encoder[NewOrUpdateBannerImageDTO] = deriveEncoder + implicit val decoder: Decoder[NewOrUpdateBannerImageDTO] = deriveDecoder +} diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdatedAboutSubjectDTO.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdatedAboutSubjectDTO.scala index 2e84b22550..6512a01d92 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdatedAboutSubjectDTO.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdatedAboutSubjectDTO.scala @@ -8,9 +8,17 @@ package no.ndla.frontpageapi.model.api +import io.circe.generic.semiauto.{deriveEncoder, deriveDecoder} +import io.circe.{Encoder, Decoder} + case class NewOrUpdatedAboutSubjectDTO( title: String, description: String, language: String, visualElement: NewOrUpdatedVisualElementDTO ) +object NewOrUpdatedAboutSubjectDTO { + + implicit val encoder: Encoder[NewOrUpdatedAboutSubjectDTO] = deriveEncoder + implicit val decoder: Decoder[NewOrUpdatedAboutSubjectDTO] = deriveDecoder +} diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdatedMetaDescriptionDTO.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdatedMetaDescriptionDTO.scala index 3e05a05e0d..f9a00db1ae 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdatedMetaDescriptionDTO.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdatedMetaDescriptionDTO.scala @@ -8,7 +8,14 @@ package no.ndla.frontpageapi.model.api +import io.circe.generic.semiauto.{deriveEncoder, deriveDecoder} +import io.circe.{Encoder, Decoder} + case class NewOrUpdatedMetaDescriptionDTO( metaDescription: String, language: String ) +object NewOrUpdatedMetaDescriptionDTO { + implicit val encoder: Encoder[NewOrUpdatedMetaDescriptionDTO] = deriveEncoder + implicit val decoder: Decoder[NewOrUpdatedMetaDescriptionDTO] = deriveDecoder +} diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdatedVisualElementDTO.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdatedVisualElementDTO.scala index 2a8f7f6448..febfae84e9 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdatedVisualElementDTO.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewOrUpdatedVisualElementDTO.scala @@ -8,4 +8,11 @@ package no.ndla.frontpageapi.model.api +import io.circe.generic.semiauto.{deriveEncoder, deriveDecoder} +import io.circe.{Encoder, Decoder} + case class NewOrUpdatedVisualElementDTO(`type`: String, id: String, alt: Option[String]) +object NewOrUpdatedVisualElementDTO { + implicit val encoder: Encoder[NewOrUpdatedVisualElementDTO] = deriveEncoder + implicit val decoder: Decoder[NewOrUpdatedVisualElementDTO] = deriveDecoder +} diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewSubjectPageDTO.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewSubjectPageDTO.scala index f72f7f7746..2357998ac1 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewSubjectPageDTO.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/NewSubjectPageDTO.scala @@ -8,6 +8,10 @@ package no.ndla.frontpageapi.model.api +import io.circe.generic.semiauto.* +import io.circe.Encoder +import io.circe.Decoder + case class NewSubjectPageDTO( name: String, externalId: Option[String], @@ -19,3 +23,8 @@ case class NewSubjectPageDTO( buildsOn: Option[List[String]], leadsTo: Option[List[String]] ) + +object NewSubjectPageDTO { + implicit val encoder: Encoder[NewSubjectPageDTO] = deriveEncoder + implicit val decoder: Decoder[NewSubjectPageDTO] = deriveDecoder +} diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/UpdatedSubjectPageDTO.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/UpdatedSubjectPageDTO.scala index 3131f9b083..cc0f5a1c55 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/UpdatedSubjectPageDTO.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/api/UpdatedSubjectPageDTO.scala @@ -8,6 +8,9 @@ package no.ndla.frontpageapi.model.api +import io.circe.generic.semiauto.* +import io.circe.{Decoder, Encoder} + case class UpdatedSubjectPageDTO( name: Option[String], externalId: Option[String], @@ -19,3 +22,7 @@ case class UpdatedSubjectPageDTO( buildsOn: Option[List[String]], leadsTo: Option[List[String]] ) +object UpdatedSubjectPageDTO { + implicit val encoder: Encoder[UpdatedSubjectPageDTO] = deriveEncoder + implicit val decoder: Decoder[UpdatedSubjectPageDTO] = deriveDecoder +} diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/domain/FilmFrontPage.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/domain/FilmFrontPage.scala index 300cc12df8..11b4d4ee90 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/domain/FilmFrontPage.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/domain/FilmFrontPage.scala @@ -10,7 +10,6 @@ package no.ndla.frontpageapi.model.domain import cats.implicits.* import io.circe.generic.semiauto.* -import io.circe.generic.auto.* import io.circe.parser.* import io.circe.{Decoder, Encoder} import no.ndla.common.model.domain.frontpage.{AboutSubject, MovieTheme} diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/domain/FrontPage.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/domain/FrontPage.scala index 4c87ba27dc..742c4baf94 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/domain/FrontPage.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/model/domain/FrontPage.scala @@ -8,9 +8,8 @@ package no.ndla.frontpageapi.model.domain -import io.circe.Encoder +import io.circe.{Decoder, Encoder} import io.circe.generic.semiauto.* -import io.circe.generic.auto.* import io.circe.parser.* import no.ndla.frontpageapi.Props import scalikejdbc.WrappedResultSet @@ -20,6 +19,10 @@ import cats.implicits.* import scala.util.Try case class Menu(articleId: Long, menu: List[Menu], hideLevel: Boolean) +object Menu { + implicit val encoder: Encoder[Menu] = deriveEncoder[Menu] + implicit val decoder: Decoder[Menu] = deriveDecoder[Menu] +} case class FrontPage( articleId: Long, @@ -28,6 +31,7 @@ case class FrontPage( object FrontPage { implicit val encoder: Encoder[FrontPage] = deriveEncoder[FrontPage] + implicit val decoder: Decoder[FrontPage] = deriveDecoder[FrontPage] private[domain] def decodeJson(document: String): Try[FrontPage] = { parse(document).flatMap(_.as[FrontPage]).toTry diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/repository/FilmFrontPageRepository.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/repository/FilmFrontPageRepository.scala index 5b3cb97356..a4207209c7 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/repository/FilmFrontPageRepository.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/repository/FilmFrontPageRepository.scala @@ -19,7 +19,7 @@ import scala.util.{Failure, Success, Try} trait FilmFrontPageRepository { this: DataSource & DBFilmFrontPage => - val filmFrontPageRepository: FilmFrontPageRepository + lazy val filmFrontPageRepository: FilmFrontPageRepository class FilmFrontPageRepository { val logger: Logger = org.log4s.getLogger diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/repository/FrontPageRepository.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/repository/FrontPageRepository.scala index 5a59863413..5bc430a9af 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/repository/FrontPageRepository.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/repository/FrontPageRepository.scala @@ -20,7 +20,7 @@ import scala.util.Try trait FrontPageRepository { this: DataSource & DBFrontPage => - val frontPageRepository: FrontPageRepository + lazy val frontPageRepository: FrontPageRepository class FrontPageRepository extends StrictLogging { import FrontPage._ diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/repository/SubjectPageRepository.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/repository/SubjectPageRepository.scala index 0974791b42..728ab431e6 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/repository/SubjectPageRepository.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/repository/SubjectPageRepository.scala @@ -13,7 +13,6 @@ import org.log4s.{Logger, getLogger} import org.postgresql.util.PGobject import scalikejdbc.* import io.circe.syntax.* -import io.circe.generic.auto.* import no.ndla.common.model.domain.frontpage.SubjectPage import no.ndla.database.DataSource import no.ndla.frontpageapi.model.domain.DBSubjectPage @@ -22,7 +21,7 @@ import scala.util.{Failure, Success, Try} trait SubjectPageRepository { this: DataSource & DBSubjectPage => - val subjectPageRepository: SubjectPageRepository + lazy val subjectPageRepository: SubjectPageRepository class SubjectPageRepository { val logger: Logger = getLogger diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/service/ConverterService.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/service/ConverterService.scala index 20d5c59aec..75c3d40335 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/service/ConverterService.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/service/ConverterService.scala @@ -34,8 +34,6 @@ trait ConverterService { this: Props => object ConverterService { - import props.{BrightcoveAccountId, BrightcovePlayer, RawImageApiUrl} - private def toApiMenu(menu: domain.Menu): model.api.MenuDTO = model.api.MenuDTO(menu.articleId, menu.menu.map(toApiMenu), Some(menu.hideLevel)) @@ -138,7 +136,7 @@ trait ConverterService { val url = visual.`type` match { case VisualElementType.Image => createImageUrl(visual.id.toLong) case VisualElementType.Brightcove => - s"https://players.brightcove.net/$BrightcoveAccountId/${BrightcovePlayer}_default/index.html?videoId=${visual.id}" + s"https://players.brightcove.net/${props.BrightcoveAccountId}/${props.BrightcovePlayer}_default/index.html?videoId=${visual.id}" } VisualElementDTO(visual.`type`.entryName, url, visual.alt) } @@ -236,6 +234,6 @@ trait ConverterService { } private def createImageUrl(id: Long): String = createImageUrl(id.toString) - private def createImageUrl(id: String): String = s"$RawImageApiUrl/id/$id" + private def createImageUrl(id: String): String = s"${props.RawImageApiUrl}/id/$id" } } diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/service/ReadService.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/service/ReadService.scala index 8cfd9446a2..f11628c294 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/service/ReadService.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/service/ReadService.scala @@ -23,7 +23,7 @@ import scala.util.{Failure, Success, Try} trait ReadService { this: SubjectPageRepository & FrontPageRepository & FilmFrontPageRepository & ConverterService => - val readService: ReadService + lazy val readService: ReadService class ReadService { @@ -47,7 +47,7 @@ trait ReadService { } } - def subjectPage(id: Long, language: String, fallback: Boolean): Try[SubjectPageDTO] = { + def subjectPage(id: Long, language: String, fallback: Boolean): Try[SubjectPageDTO] = permitTry { val maybeSubject = subjectPageRepository.withId(id).? val converted = maybeSubject.traverse(ConverterService.toApiSubjectPage(_, language, fallback)).? converted.toTry(SubjectPageNotFoundException(id)) @@ -58,7 +58,7 @@ trait ReadService { pageSize: Int, language: String, fallback: Boolean - ): Try[List[SubjectPageDTO]] = { + ): Try[List[SubjectPageDTO]] = permitTry { val offset = pageSize * (page - 1) val data = subjectPageRepository.all(offset, pageSize).? val converted = data.map(ConverterService.toApiSubjectPage(_, language, fallback)) diff --git a/frontpage-api/src/main/scala/no/ndla/frontpageapi/service/WriteService.scala b/frontpage-api/src/main/scala/no/ndla/frontpageapi/service/WriteService.scala index 5002e41e3e..4d5359211c 100644 --- a/frontpage-api/src/main/scala/no/ndla/frontpageapi/service/WriteService.scala +++ b/frontpage-api/src/main/scala/no/ndla/frontpageapi/service/WriteService.scala @@ -21,7 +21,7 @@ import scala.util.{Failure, Success, Try} trait WriteService { this: SubjectPageRepository & FrontPageRepository & FilmFrontPageRepository & Props & ConverterService => - val writeService: WriteService + lazy val writeService: WriteService class WriteService { diff --git a/frontpage-api/src/test/scala/no/ndla/frontpageapi/TestData.scala b/frontpage-api/src/test/scala/no/ndla/frontpageapi/TestData.scala index c6b7772698..79469bd543 100644 --- a/frontpage-api/src/test/scala/no/ndla/frontpageapi/TestData.scala +++ b/frontpage-api/src/test/scala/no/ndla/frontpageapi/TestData.scala @@ -8,7 +8,6 @@ package no.ndla.frontpageapi -import io.circe.generic.auto.* import io.circe.syntax.* import no.ndla.common.model import no.ndla.common.model.api.frontpage.{AboutSubjectDTO, BannerImageDTO, SubjectPageDTO, VisualElementDTO} diff --git a/frontpage-api/src/test/scala/no/ndla/frontpageapi/TestEnvironment.scala b/frontpage-api/src/test/scala/no/ndla/frontpageapi/TestEnvironment.scala index d11d6975a9..b6452567ef 100644 --- a/frontpage-api/src/test/scala/no/ndla/frontpageapi/TestEnvironment.scala +++ b/frontpage-api/src/test/scala/no/ndla/frontpageapi/TestEnvironment.scala @@ -41,22 +41,22 @@ trait TestEnvironment with DBMigrator { override lazy val props = new FrontpageApiProperties - override val clock: SystemClock = mock[SystemClock] - override val migrator: DBMigrator = mock[DBMigrator] - override val dataSource: HikariDataSource = mock[HikariDataSource] + override lazy val clock: SystemClock = mock[SystemClock] + override lazy val migrator: DBMigrator = mock[DBMigrator] + override lazy val dataSource: HikariDataSource = mock[HikariDataSource] - override val filmPageController: FilmPageController = mock[FilmPageController] - override val subjectPageController: SubjectPageController = mock[SubjectPageController] - override val frontPageController: FrontPageController = mock[FrontPageController] - override val subjectPageRepository: SubjectPageRepository = mock[SubjectPageRepository] - override val frontPageRepository: FrontPageRepository = mock[FrontPageRepository] - override val filmFrontPageRepository: FilmFrontPageRepository = mock[FilmFrontPageRepository] - override val healthController: TapirHealthController = mock[TapirHealthController] - override val readService: ReadService = mock[ReadService] - override val writeService: WriteService = mock[WriteService] + override lazy val filmPageController: FilmPageController = mock[FilmPageController] + override lazy val subjectPageController: SubjectPageController = mock[SubjectPageController] + override lazy val frontPageController: FrontPageController = mock[FrontPageController] + override lazy val subjectPageRepository: SubjectPageRepository = mock[SubjectPageRepository] + override lazy val frontPageRepository: FrontPageRepository = mock[FrontPageRepository] + override lazy val filmFrontPageRepository: FilmFrontPageRepository = mock[FilmFrontPageRepository] + override lazy val healthController: TapirHealthController = mock[TapirHealthController] + override lazy val readService: ReadService = mock[ReadService] + override lazy val writeService: WriteService = mock[WriteService] - override val ndlaClient: NdlaClient = mock[NdlaClient] - override val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] + override lazy val ndlaClient: NdlaClient = mock[NdlaClient] + override lazy val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] def services: List[TapirController] = List.empty val swagger: SwaggerController = mock[SwaggerController] diff --git a/frontpage-api/src/test/scala/no/ndla/frontpageapi/db/migration/V4__AddLanguageToAboutTest.scala b/frontpage-api/src/test/scala/no/ndla/frontpageapi/db/migration/V4__AddLanguageToAboutTest.scala index c0448564ec..57dde281ef 100644 --- a/frontpage-api/src/test/scala/no/ndla/frontpageapi/db/migration/V4__AddLanguageToAboutTest.scala +++ b/frontpage-api/src/test/scala/no/ndla/frontpageapi/db/migration/V4__AddLanguageToAboutTest.scala @@ -8,7 +8,6 @@ package no.ndla.frontpageapi.db.migration -import no.ndla.frontpageapi.db.migration.{V2_DBSubjectPage, V4__add_language_to_about} import no.ndla.frontpageapi.{TestEnvironment, UnitSuite} class V4__AddLanguageToAboutTest extends UnitSuite with TestEnvironment { diff --git a/frontpage-api/src/test/scala/no/ndla/frontpageapi/db/migration/V5__AddMetaDescriptionTest.scala b/frontpage-api/src/test/scala/no/ndla/frontpageapi/db/migration/V5__AddMetaDescriptionTest.scala index 14fc4f5977..a070369828 100644 --- a/frontpage-api/src/test/scala/no/ndla/frontpageapi/db/migration/V5__AddMetaDescriptionTest.scala +++ b/frontpage-api/src/test/scala/no/ndla/frontpageapi/db/migration/V5__AddMetaDescriptionTest.scala @@ -8,7 +8,6 @@ package no.ndla.frontpageapi.db.migration -import no.ndla.frontpageapi.db.migration.{V2_DBSubjectPage, V5__add_meta_description} import no.ndla.frontpageapi.{TestEnvironment, UnitSuite} class V5__AddMetaDescriptionTest extends UnitSuite with TestEnvironment { diff --git a/frontpage-api/src/test/scala/no/ndla/frontpageapi/db/migration/V8__AddSubjectLinksTest.scala b/frontpage-api/src/test/scala/no/ndla/frontpageapi/db/migration/V8__AddSubjectLinksTest.scala index 0fe52608cf..5f2a9cedd7 100644 --- a/frontpage-api/src/test/scala/no/ndla/frontpageapi/db/migration/V8__AddSubjectLinksTest.scala +++ b/frontpage-api/src/test/scala/no/ndla/frontpageapi/db/migration/V8__AddSubjectLinksTest.scala @@ -8,7 +8,6 @@ package no.ndla.frontpageapi.db.migration -import no.ndla.frontpageapi.db.migration.{V2_DBSubjectPage, V8__add_subject_links} import no.ndla.frontpageapi.{TestEnvironment, UnitSuite} class V8__AddSubjectLinksTest extends UnitSuite with TestEnvironment { diff --git a/frontpage-api/src/test/scala/no/ndla/frontpageapi/model/api/FrontPageTest/FrontPageTest.scala b/frontpage-api/src/test/scala/no/ndla/frontpageapi/model/api/FrontPageTest/FrontPageTest.scala index b09a7c5871..8052cb9474 100644 --- a/frontpage-api/src/test/scala/no/ndla/frontpageapi/model/api/FrontPageTest/FrontPageTest.scala +++ b/frontpage-api/src/test/scala/no/ndla/frontpageapi/model/api/FrontPageTest/FrontPageTest.scala @@ -9,7 +9,6 @@ package no.ndla.frontpageapi.model.api.FrontPageTest import no.ndla.frontpageapi.{TestEnvironment, UnitSuite} -import io.circe.generic.auto.* import io.circe.syntax.* import io.circe.parser.* import no.ndla.common.model.api.{FrontPageDTO, MenuDTO} diff --git a/frontpage-api/src/test/scala/no/ndla/frontpageapi/model/domain/SubjectPageTest.scala b/frontpage-api/src/test/scala/no/ndla/frontpageapi/model/domain/SubjectPageTest.scala index e103935b32..33b39a68f8 100644 --- a/frontpage-api/src/test/scala/no/ndla/frontpageapi/model/domain/SubjectPageTest.scala +++ b/frontpage-api/src/test/scala/no/ndla/frontpageapi/model/domain/SubjectPageTest.scala @@ -15,8 +15,8 @@ import scala.util.Success class SubjectPageTest extends UnitSuite with TestEnvironment { test("decodeJson should use correct id") { - val Success(subject) = SubjectPage.decodeJson(TestData.domainSubjectJson, 10) - subject.id should be(Some(10)) + val subject = SubjectPage.decodeJson(TestData.domainSubjectJson, 10) + subject.map(_.id) should be(Success(Some(10))) } } diff --git a/frontpage-api/src/test/scala/no/ndla/frontpageapi/service/ConverterServiceTest.scala b/frontpage-api/src/test/scala/no/ndla/frontpageapi/service/ConverterServiceTest.scala index 0addf01b84..f6ef474d43 100644 --- a/frontpage-api/src/test/scala/no/ndla/frontpageapi/service/ConverterServiceTest.scala +++ b/frontpage-api/src/test/scala/no/ndla/frontpageapi/service/ConverterServiceTest.scala @@ -42,12 +42,12 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { val about = TestData.apiNewSubjectPage.about.map(_.copy(visualElement = visualElement)) val page = TestData.apiNewSubjectPage.copy(about = about) - val Failure(res: ValidationException) = ConverterService.toDomainSubjectPage(page) - val expectedError = ValidationException( + val result = ConverterService.toDomainSubjectPage(page) + val expectedError = ValidationException( "visualElement.type", "'not an image' is an invalid visual element type" ) - res should be(expectedError) + result should be(Failure(expectedError)) } test("toDomainSubjectPage should return a success if visual element type is valid") { diff --git a/frontpage-api/src/test/scala/no/ndla/frontpageapi/service/ReadServiceTest.scala b/frontpage-api/src/test/scala/no/ndla/frontpageapi/service/ReadServiceTest.scala index 712bd5fc34..5afe7cd587 100644 --- a/frontpage-api/src/test/scala/no/ndla/frontpageapi/service/ReadServiceTest.scala +++ b/frontpage-api/src/test/scala/no/ndla/frontpageapi/service/ReadServiceTest.scala @@ -17,7 +17,7 @@ import org.mockito.Mockito.when import scala.util.Success class ReadServiceTest extends UnitSuite with TestEnvironment { - override val readService: ReadService = new ReadService + override lazy val readService: ReadService = new ReadService test("That all subjectpages does not fail on 404 because of language") { val norwegianSubjectPage = TestData.domainSubjectPage.copy( @@ -39,7 +39,8 @@ class ReadServiceTest extends UnitSuite with TestEnvironment { ) ) - when(subjectPageRepository.all(any, any)(any)).thenReturn(Success(List(norwegianSubjectPage, englishSubjectPage))) + when(subjectPageRepository.all(any, any)(using any)) + .thenReturn(Success(List(norwegianSubjectPage, englishSubjectPage))) val result = readService.subjectPages(1, 10, "en", fallback = false) result.get.map(_.id) should be(List(2)) diff --git a/frontpage-api/src/test/scala/no/ndla/frontpageapi/service/WriteServiceTest.scala b/frontpage-api/src/test/scala/no/ndla/frontpageapi/service/WriteServiceTest.scala index ea5b8dc051..252fdafec8 100644 --- a/frontpage-api/src/test/scala/no/ndla/frontpageapi/service/WriteServiceTest.scala +++ b/frontpage-api/src/test/scala/no/ndla/frontpageapi/service/WriteServiceTest.scala @@ -18,7 +18,7 @@ import org.mockito.Mockito.when import scala.util.Success class WriteServiceTest extends UnitSuite with TestEnvironment { - override val writeService: WriteService = new WriteService + override lazy val writeService: WriteService = new WriteService test("That language is deleted for subject page") { val subjectPage = TestData.domainSubjectPage.copy( @@ -27,7 +27,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { metaDescription = TestData.domainSubjectPage.metaDescription ++ Seq(MetaDescription("Description", "nn")) ) when(subjectPageRepository.withId(any)).thenReturn(Success(Some(subjectPage))) - when(subjectPageRepository.updateSubjectPage(any)(any)).thenAnswer(i => Success(i.getArgument(0))) + when(subjectPageRepository.updateSubjectPage(any)(using any)).thenAnswer(i => Success(i.getArgument(0))) val result = writeService.deleteSubjectPageLanguage(subjectPage.id.get, "nn") result should be( @@ -41,7 +41,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { test("That deleting last language for subject page throws exception") { when(subjectPageRepository.withId(any)).thenReturn(Success(Some(TestData.domainSubjectPage))) - when(subjectPageRepository.updateSubjectPage(any)(any)).thenAnswer(i => Success(i.getArgument(0))) + when(subjectPageRepository.updateSubjectPage(any)(using any)).thenAnswer(i => Success(i.getArgument(0))) val result = writeService.deleteSubjectPageLanguage(TestData.domainSubjectPage.id.get, "nb") result.isFailure should be(true) @@ -55,8 +55,8 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { movieTheme.copy(name = movieTheme.name ++ Seq(MovieThemeName("FooBar", "nn"))) ) ) - when(filmFrontPageRepository.get(any)).thenReturn(Some(filmFrontPage)) - when(filmFrontPageRepository.update(any)(any)).thenAnswer(i => Success(i.getArgument(0))) + when(filmFrontPageRepository.get(using any)).thenReturn(Some(filmFrontPage)) + when(filmFrontPageRepository.update(any)(using any)).thenAnswer(i => Success(i.getArgument(0))) val result = writeService.deleteFilmFrontPageLanguage("nn") result should be(Success(ConverterService.toApiFilmFrontPage(TestData.domainFilmFrontPage, None))) @@ -69,8 +69,8 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { movieTheme.copy(name = movieTheme.name.filter(_.language == "nb")) ) ) - when(filmFrontPageRepository.get(any)).thenReturn(Some(filmFrontPage)) - when(filmFrontPageRepository.update(any)(any)).thenAnswer(i => Success(i.getArgument(0))) + when(filmFrontPageRepository.get(using any)).thenReturn(Some(filmFrontPage)) + when(filmFrontPageRepository.update(any)(using any)).thenAnswer(i => Success(i.getArgument(0))) val result = writeService.deleteFilmFrontPageLanguage("nb") result.isFailure should be(true) diff --git a/image-api/src/main/scala/no/ndla/imageapi/ComponentRegistry.scala b/image-api/src/main/scala/no/ndla/imageapi/ComponentRegistry.scala index c4065033d8..52a8470765 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/ComponentRegistry.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/ComponentRegistry.scala @@ -66,40 +66,40 @@ class ComponentRegistry(properties: ImageApiProperties) with Random with NdlaS3Client with SwaggerDocControllerConfig { - override val props: ImageApiProperties = properties + override lazy val props: ImageApiProperties = properties - override val migrator: DBMigrator = DBMigrator( + override lazy val migrator: DBMigrator = DBMigrator( new V6__AddAgreementToImages, new V7__TranslateUntranslatedAuthors ) override lazy val dataSource: HikariDataSource = DataSource.getHikariDataSource - lazy val s3Client = new NdlaS3Client(props.StorageName, props.StorageRegion) + override lazy val s3Client = new NdlaS3Client(props.StorageName, props.StorageRegion) - lazy val imageIndexService = new ImageIndexService - lazy val imageSearchService = new ImageSearchService - lazy val tagIndexService = new TagIndexService - lazy val tagSearchService = new TagSearchService - lazy val imageRepository = new ImageRepository - lazy val readService = new ReadService - lazy val writeService = new WriteService - lazy val validationService = new ValidationService - lazy val imageStorage = new AmazonImageStorageService - lazy val ndlaClient = new NdlaClient - lazy val converterService = new ConverterService - var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer) - lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient - lazy val searchConverterService = new SearchConverterService + override lazy val imageIndexService = new ImageIndexService + override lazy val imageSearchService = new ImageSearchService + override lazy val tagIndexService = new TagIndexService + override lazy val tagSearchService = new TagSearchService + override lazy val imageRepository = new ImageRepository + override lazy val readService = new ReadService + override lazy val writeService = new WriteService + override lazy val validationService = new ValidationService + override lazy val imageStorage = new AmazonImageStorageService + override lazy val ndlaClient = new NdlaClient + override lazy val converterService = new ConverterService + var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer) + override lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient + override lazy val searchConverterService = new SearchConverterService - lazy val imageConverter = new ImageConverter - lazy val clock = new SystemClock - lazy val random = new Random + override lazy val imageConverter = new ImageConverter + override lazy val clock = new SystemClock + override lazy val random = new Random - lazy val imageControllerV2 = new ImageControllerV2 - lazy val imageControllerV3 = new ImageControllerV3 - lazy val rawController = new RawController - lazy val internController = new InternController - lazy val healthController = new HealthController + override lazy val imageControllerV2 = new ImageControllerV2 + override lazy val imageControllerV3 = new ImageControllerV3 + override lazy val rawController = new RawController + override lazy val internController = new InternController + override lazy val healthController = new HealthController val swagger = new SwaggerController( List[TapirController]( diff --git a/image-api/src/main/scala/no/ndla/imageapi/ImageApiProperties.scala b/image-api/src/main/scala/no/ndla/imageapi/ImageApiProperties.scala index 39e3fd9013..9addde3904 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/ImageApiProperties.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/ImageApiProperties.scala @@ -16,7 +16,7 @@ import no.ndla.network.{AuthUser, Domains} import scala.util.Properties.* trait Props extends HasBaseProps with HasDatabaseProps { - val props: ImageApiProperties + lazy val props: ImageApiProperties } class ImageApiProperties extends BaseProps with DatabaseProps with StrictLogging { diff --git a/image-api/src/main/scala/no/ndla/imageapi/controller/BaseImageController.scala b/image-api/src/main/scala/no/ndla/imageapi/controller/BaseImageController.scala index 65dadbe31b..9a9bae2cd7 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/controller/BaseImageController.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/controller/BaseImageController.scala @@ -27,9 +27,6 @@ trait BaseImageController { /** Base class for sharing code between Image controllers. */ trait BaseImageController { - - import props.* - val queryParam: EndpointInput.Query[Option[String]] = query[Option[String]]("query") .description("Return only images with titles, alt-texts or tags matching the specified query.") @@ -66,7 +63,7 @@ trait BaseImageController { .description("The page number of the search hits to display.") val pageSize: EndpointInput.Query[Option[Int]] = query[Option[Int]]("page-size") .description( - s"The number of search hits to display for each page. Defaults to $DefaultPageSize and max is $MaxPageSize." + s"The number of search hits to display for each page. Defaults to ${props.DefaultPageSize} and max is ${props.MaxPageSize}." ) val pathImageId: EndpointInput.PathCapture[Long] = @@ -77,11 +74,11 @@ trait BaseImageController { path[String]("external_id").description("External node id of the image that needs to be fetched.") val scrollId: EndpointInput.Query[Option[String]] = query[Option[String]]("search-context").description( - s"""A unique string obtained from a search you want to keep scrolling in. To obtain one from a search, provide one of the following values: ${InitialScrollContextKeywords + s"""A unique string obtained from a search you want to keep scrolling in. To obtain one from a search, provide one of the following values: ${props.InitialScrollContextKeywords .mkString("[", ",", "]")}. |When scrolling, the parameters from the initial search is used, except in the case of '${this.language.name}'. - |This value may change between scrolls. Always use the one in the latest scroll result (The context, if unused, dies after $ElasticSearchScrollKeepAlive). - |If you are not paginating past $ElasticSearchIndexMaxResultWindow hits, you can ignore this and use '${this.pageNo.name}' and '${this.pageSize.name}' instead. + |This value may change between scrolls. Always use the one in the latest scroll result (The context, if unused, dies after ${props.ElasticSearchScrollKeepAlive}). + |If you are not paginating past ${props.ElasticSearchIndexMaxResultWindow} hits, you can ignore this and use '${this.pageNo.name}' and '${this.pageSize.name}' instead. |""".stripMargin ) @@ -106,7 +103,7 @@ trait BaseImageController { query[Option[Boolean]]("podcast-friendly") .description("Filter images that are podcast friendly. Width==heigth and between 1400 and 3000.") - val maxImageFileSizeBytes: Int = MaxImageFileSizeBytes + val maxImageFileSizeBytes: Int = props.MaxImageFileSizeBytes def doWithStream[T](filePart: Part[File])(f: UploadedFile => Try[T]): Try[T] = { val file = UploadedFile.fromFilePart(filePart) diff --git a/image-api/src/main/scala/no/ndla/imageapi/controller/HealthController.scala b/image-api/src/main/scala/no/ndla/imageapi/controller/HealthController.scala index a3b3635c62..86795123d3 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/controller/HealthController.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/controller/HealthController.scala @@ -15,7 +15,7 @@ import no.ndla.network.tapir.TapirHealthController trait HealthController { this: ImageStorageService & ImageRepository & Props & TapirHealthController => - val healthController: HealthController + lazy val healthController: HealthController class HealthController extends TapirHealthController { diff --git a/image-api/src/main/scala/no/ndla/imageapi/controller/ImageControllerV2.scala b/image-api/src/main/scala/no/ndla/imageapi/controller/ImageControllerV2.scala index 9fefc24e1b..e69c63d071 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/controller/ImageControllerV2.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/controller/ImageControllerV2.scala @@ -33,12 +33,10 @@ import scala.util.{Failure, Success, Try} trait ImageControllerV2 { this: ImageRepository & ImageSearchService & ConverterService & ReadService & WriteService & SearchConverterService & Props & ErrorHandling & BaseImageController & TapirController => - val imageControllerV2: ImageControllerV2 + lazy val imageControllerV2: ImageControllerV2 class ImageControllerV2 extends TapirController with BaseImageController { import ErrorHelpers.* - import props.* - override val serviceName: String = "images V2" override val prefix: EndpointInput[Unit] = "image-api" / "v2" / "images" override val endpoints: List[ServerEndpoint[Any, Eff]] = List( @@ -65,7 +63,7 @@ trait ImageControllerV2 { orFunction: => Try[(SearchResultDTO, DynamicHeaders)] ): Try[(SearchResultDTO, DynamicHeaders)] = scrollId match { - case Some(scroll) if !InitialScrollContextKeywords.contains(scroll) => + case Some(scroll) if !props.InitialScrollContextKeywords.contains(scroll) => imageSearchService.scrollV2(scroll, language.code, user) match { case Success(scrollResult) => val body = searchConverterService.asApiSearchResult(scrollResult) @@ -167,7 +165,7 @@ trait ImageControllerV2 { ) => scrollSearchOr(scrollId, language, user) { val sort = Sort.valueOf(sortStr) - val shouldScroll = scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = scrollId.exists(props.InitialScrollContextKeywords.contains) val modelReleasedStatus = modelReleased.values.flatMap(ModelReleasedStatus.valueOf) search( @@ -208,7 +206,7 @@ trait ImageControllerV2 { val page = searchParams.page val podcastFriendly = searchParams.podcastFriendly val sort = searchParams.sort - val shouldScroll = searchParams.scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = searchParams.scrollId.exists(props.InitialScrollContextKeywords.contains) val modelReleasedStatus = searchParams.modelReleased.getOrElse(Seq.empty).flatMap(ModelReleasedStatus.valueOf) diff --git a/image-api/src/main/scala/no/ndla/imageapi/controller/ImageControllerV3.scala b/image-api/src/main/scala/no/ndla/imageapi/controller/ImageControllerV3.scala index 93a828817d..be3ec315ee 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/controller/ImageControllerV3.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/controller/ImageControllerV3.scala @@ -33,12 +33,10 @@ import scala.util.{Failure, Success, Try} trait ImageControllerV3 { this: ImageRepository & ImageSearchService & ConverterService & ReadService & WriteService & SearchConverterService & Props & ErrorHandling & BaseImageController & TapirController => - val imageControllerV3: ImageControllerV3 + lazy val imageControllerV3: ImageControllerV3 class ImageControllerV3 extends TapirController with BaseImageController { import ErrorHelpers.* - import props.* - override val serviceName: String = "images V3" override val prefix: EndpointInput[Unit] = "image-api" / "v3" / "images" @@ -56,7 +54,7 @@ trait ImageControllerV3 { user: Option[TokenUser] )(orFunction: => Try[(SearchResultV3DTO, DynamicHeaders)]): Try[(SearchResultV3DTO, DynamicHeaders)] = scrollId match { - case Some(scroll) if !InitialScrollContextKeywords.contains(scroll) => + case Some(scroll) if !props.InitialScrollContextKeywords.contains(scroll) => for { scrollResult <- imageSearchService.scroll(scroll, language) body <- searchConverterService.asApiSearchResultV3(scrollResult, language, user) @@ -162,7 +160,7 @@ trait ImageControllerV3 { ) => scrollSearchOr(scrollId, language.code, user) { val sort = Sort.valueOf(sortStr) - val shouldScroll = scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = scrollId.exists(props.InitialScrollContextKeywords.contains) val modelReleasedStatus = modelReleased.values.flatMap(ModelReleasedStatus.valueOf) val licenseOpt = license.orElse(Option.when(includeCopyrighted)("all")) @@ -209,7 +207,7 @@ trait ImageControllerV3 { val page = searchParams.page val podcastFriendly = searchParams.podcastFriendly val sort = searchParams.sort - val shouldScroll = searchParams.scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = searchParams.scrollId.exists(props.InitialScrollContextKeywords.contains) val modelReleasedStatus = searchParams.modelReleased.getOrElse(Seq.empty).flatMap(ModelReleasedStatus.valueOf) val userFilter = searchParams.users.getOrElse(List.empty) diff --git a/image-api/src/main/scala/no/ndla/imageapi/controller/InternController.scala b/image-api/src/main/scala/no/ndla/imageapi/controller/InternController.scala index e5c601b87f..dddf6547d3 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/controller/InternController.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/controller/InternController.scala @@ -32,7 +32,7 @@ import scala.util.{Failure, Success} trait InternController { this: ImageRepository & ReadService & ConverterService & ImageIndexService & TagIndexService & ImageRepository & Props & ErrorHandling & TapirController => - val internController: InternController + lazy val internController: InternController class InternController extends TapirController with StrictLogging { import ErrorHelpers.* diff --git a/image-api/src/main/scala/no/ndla/imageapi/controller/RawController.scala b/image-api/src/main/scala/no/ndla/imageapi/controller/RawController.scala index d97cdffd62..861034f3f0 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/controller/RawController.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/controller/RawController.scala @@ -26,7 +26,7 @@ import scala.util.{Failure, Success, Try} trait RawController { this: ImageStorageService & ImageConverter & ImageRepository & ErrorHandling & Props & ReadService & TapirController => - val rawController: RawController + lazy val rawController: RawController class RawController extends TapirController { override val serviceName: String = "raw" diff --git a/image-api/src/main/scala/no/ndla/imageapi/model/domain/ImageStream.scala b/image-api/src/main/scala/no/ndla/imageapi/model/domain/ImageStream.scala index 6254d22c85..4412094696 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/model/domain/ImageStream.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/model/domain/ImageStream.scala @@ -16,5 +16,5 @@ trait ImageStream { def stream: InputStream def fileName: String def format: String = fileName.substring(fileName.lastIndexOf(".") + 1) - val sourceImage: BufferedImage + lazy val sourceImage: BufferedImage } diff --git a/image-api/src/main/scala/no/ndla/imageapi/repository/ImageRepository.scala b/image-api/src/main/scala/no/ndla/imageapi/repository/ImageRepository.scala index 3b33927773..fb98faf4f6 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/repository/ImageRepository.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/repository/ImageRepository.scala @@ -21,7 +21,7 @@ import scala.util.{Success, Try} trait ImageRepository { this: DataSource & ConverterService => - val imageRepository: ImageRepository + lazy val imageRepository: ImageRepository class ImageRepository extends StrictLogging with Repository[ImageMetaInformation] { def imageCount(implicit session: DBSession = ReadOnlyAutoSession): Long = diff --git a/image-api/src/main/scala/no/ndla/imageapi/service/ConverterService.scala b/image-api/src/main/scala/no/ndla/imageapi/service/ConverterService.scala index abf343ccab..e8061bed9e 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/service/ConverterService.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/service/ConverterService.scala @@ -33,11 +33,9 @@ import scala.util.{Failure, Success, Try} trait ConverterService { this: Clock & Props => - val converterService: ConverterService + lazy val converterService: ConverterService class ConverterService extends StrictLogging { - import props.DefaultLanguage - def asApiAuthor(domainAuthor: commonDomain.Author): commonApi.AuthorDTO = { commonApi.AuthorDTO(domainAuthor.`type`, domainAuthor.name) } @@ -100,16 +98,16 @@ trait ConverterService { val rawPath = props.RawImageUrlBase val title = findByLanguageOrBestEffort(imageMeta.titles, language) .map(asApiImageTitle) - .getOrElse(api.ImageTitleDTO("", DefaultLanguage)) + .getOrElse(api.ImageTitleDTO("", props.DefaultLanguage)) val alttext = findByLanguageOrBestEffort(imageMeta.alttexts, language) .map(asApiImageAltText) - .getOrElse(api.ImageAltTextDTO("", DefaultLanguage)) + .getOrElse(api.ImageAltTextDTO("", props.DefaultLanguage)) val tags = findByLanguageOrBestEffort(imageMeta.tags, language) .map(asApiImageTag) - .getOrElse(api.ImageTagDTO(Seq(), DefaultLanguage)) + .getOrElse(api.ImageTagDTO(Seq(), props.DefaultLanguage)) val caption = findByLanguageOrBestEffort(imageMeta.captions, language) .map(asApiCaption) - .getOrElse(api.ImageCaptionDTO("", DefaultLanguage)) + .getOrElse(api.ImageCaptionDTO("", props.DefaultLanguage)) getImageFromMeta(imageMeta, language).flatMap(image => { val apiUrl = asApiUrl(image.fileName, rawPath.some) @@ -174,16 +172,16 @@ trait ConverterService { ): Try[api.ImageMetaInformationV2DTO] = { val title = findByLanguageOrBestEffort(imageMeta.titles, language) .map(asApiImageTitle) - .getOrElse(api.ImageTitleDTO("", DefaultLanguage)) + .getOrElse(api.ImageTitleDTO("", props.DefaultLanguage)) val alttext = findByLanguageOrBestEffort(imageMeta.alttexts, language) .map(asApiImageAltText) - .getOrElse(api.ImageAltTextDTO("", DefaultLanguage)) + .getOrElse(api.ImageAltTextDTO("", props.DefaultLanguage)) val tags = findByLanguageOrBestEffort(imageMeta.tags, language) .map(asApiImageTag) - .getOrElse(api.ImageTagDTO(Seq(), DefaultLanguage)) + .getOrElse(api.ImageTagDTO(Seq(), props.DefaultLanguage)) val caption = findByLanguageOrBestEffort(imageMeta.captions, language) .map(asApiCaption) - .getOrElse(api.ImageCaptionDTO("", DefaultLanguage)) + .getOrElse(api.ImageCaptionDTO("", props.DefaultLanguage)) getImageFromMeta(imageMeta, language).flatMap(image => { val apiUrl = asApiUrl(image.fileName, rawBaseUrl) diff --git a/image-api/src/main/scala/no/ndla/imageapi/service/ImageConverter.scala b/image-api/src/main/scala/no/ndla/imageapi/service/ImageConverter.scala index 12f44280f4..cead8be8a3 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/service/ImageConverter.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/service/ImageConverter.scala @@ -24,7 +24,7 @@ import scala.util.{Success, Try} trait ImageConverter { this: Props => - val imageConverter: ImageConverter + lazy val imageConverter: ImageConverter case class PixelPoint(x: Int, y: Int) // A point given with pixles case class PercentPoint(x: Double, y: Double) { // A point given with values from MinValue to MaxValue. MinValue,MinValue is top-left, MaxValue,MaxValue is bottom-right import PercentPoint.* diff --git a/image-api/src/main/scala/no/ndla/imageapi/service/ImageStorageService.scala b/image-api/src/main/scala/no/ndla/imageapi/service/ImageStorageService.scala index 8625dce39d..2c9df53059 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/service/ImageStorageService.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/service/ImageStorageService.scala @@ -26,13 +26,12 @@ import scala.util.{Failure, Success, Try} trait ImageStorageService { this: NdlaS3Client & ReadService & Props => - val imageStorage: AmazonImageStorageService + lazy val imageStorage: AmazonImageStorageService class AmazonImageStorageService extends StrictLogging { - import props.ValidMimeTypes case class NdlaImage(s3Object: NdlaS3Object, fileName: String) extends ImageStream { - lazy val imageContent: Array[Byte] = s3Object.stream.readAllBytes() - override val sourceImage: BufferedImage = ImageIO.read(stream) + lazy val imageContent: Array[Byte] = s3Object.stream.readAllBytes() + override lazy val sourceImage: BufferedImage = ImageIO.read(stream) override def contentType: String = { val s3ContentType = s3Object.contentType @@ -42,7 +41,7 @@ trait ImageStorageService { logger.warn(s"Couldn't get meta for $fileName so using s3 content-type of '$s3ContentType'", ex) s3ContentType case Success(meta) - if meta.contentType != "" && meta.contentType != "binary/octet-stream" && ValidMimeTypes.contains( + if meta.contentType != "" && meta.contentType != "binary/octet-stream" && props.ValidMimeTypes.contains( meta.contentType ) => updateContentType(s3Object.key, meta.contentType) match { diff --git a/image-api/src/main/scala/no/ndla/imageapi/service/Random.scala b/image-api/src/main/scala/no/ndla/imageapi/service/Random.scala index 6c4985f0c0..a9f63c2c46 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/service/Random.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/service/Random.scala @@ -9,7 +9,7 @@ package no.ndla.imageapi.service trait Random { - val random: Random + lazy val random: Random class Random { def string(length: Int): String = { scala.util.Random.alphanumeric.take(length).mkString diff --git a/image-api/src/main/scala/no/ndla/imageapi/service/ReadService.scala b/image-api/src/main/scala/no/ndla/imageapi/service/ReadService.scala index 77818bfcda..3a9c2fe0a0 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/service/ReadService.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/service/ReadService.scala @@ -26,7 +26,7 @@ import scala.util.{Failure, Success, Try} trait ReadService { this: ConverterService & ValidationService & ImageRepository & ImageIndexService & ImageStorageService & TagSearchService & SearchConverterService => - val readService: ReadService + lazy val readService: ReadService class ReadService extends StrictLogging { diff --git a/image-api/src/main/scala/no/ndla/imageapi/service/ValidationService.scala b/image-api/src/main/scala/no/ndla/imageapi/service/ValidationService.scala index 56d0c94dd8..1cd41012a0 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/service/ValidationService.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/service/ValidationService.scala @@ -22,29 +22,28 @@ import scala.util.{Failure, Success, Try} trait ValidationService { this: Props => - val validationService: ValidationService + lazy val validationService: ValidationService class ValidationService { - import props.{ValidFileExtensions, ValidMimeTypes} def validateImageFile(imageFile: UploadedFile): Option[ValidationMessage] = { val fn = imageFile.fileName.getOrElse("").stripPrefix("\"").stripSuffix("\"") - if (!hasValidFileExtension(fn, ValidFileExtensions)) + if (!hasValidFileExtension(fn, props.ValidFileExtensions)) return Some( ValidationMessage( "file", - s"The file $fn does not have a known file extension. Must be one of ${ValidFileExtensions + s"The file $fn does not have a known file extension. Must be one of ${props.ValidFileExtensions .mkString(",")}" ) ) val actualMimeType = imageFile.contentType.getOrElse("") - if (!ValidMimeTypes.contains(actualMimeType)) + if (!props.ValidMimeTypes.contains(actualMimeType)) return Some( ValidationMessage( "file", - s"The file $fn is not a valid image file. Only valid type is '${ValidMimeTypes.mkString(",")}', but was '$actualMimeType'" + s"The file $fn is not a valid image file. Only valid type is '${props.ValidMimeTypes.mkString(",")}', but was '$actualMimeType'" ) ) diff --git a/image-api/src/main/scala/no/ndla/imageapi/service/WriteService.scala b/image-api/src/main/scala/no/ndla/imageapi/service/WriteService.scala index e95114c00a..825dbe6f39 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/service/WriteService.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/service/WriteService.scala @@ -40,7 +40,7 @@ import scala.util.{Failure, Success, Try} trait WriteService { this: ConverterService & ValidationService & ImageRepository & ImageIndexService & ImageStorageService & TagIndexService & Clock & Props & Random => - val writeService: WriteService + lazy val writeService: WriteService class WriteService extends StrictLogging { @@ -87,7 +87,6 @@ trait WriteService { case None => logger.warn("Deleting language for image without imagefile. This is weird.") Success(()) - case _ => Success(()) } } @@ -95,7 +94,7 @@ trait WriteService { imageId: Long, language: String, user: TokenUser - ): Try[Option[ImageMetaInformation]] = + ): Try[Option[ImageMetaInformation]] = permitTry { imageRepository.withId(imageId) match { case Some(existing) if converterService.getSupportedLanguages(existing).contains(language) => val newImage = converterService.withoutLanguage(existing, language, user) @@ -114,6 +113,7 @@ trait WriteService { case None => Failure(new ImageNotFoundException(s"Image with id $imageId was not found, and could not be deleted.")) } + } def deleteImageAndFiles(imageId: Long): Try[Long] = { imageRepository.withId(imageId) match { @@ -167,11 +167,11 @@ trait WriteService { file: UploadedFile, copiedFrom: Option[ImageMetaInformation], language: String - ): Try[ImageMetaInformation] = { - validationService.validateImageFile(file) match { - case Some(validationMessage) => return Failure(new ValidationException(errors = Seq(validationMessage))) - case _ => - } + ): Try[ImageMetaInformation] = permitTry { + (validationService.validateImageFile(file) match { + case Some(validationMessage) => Failure(new ValidationException(errors = Seq(validationMessage))) + case _ => Success(()) + }).? validationService.validate(toInsert, copiedFrom).?? val insertedMeta = Try(imageRepository.insert(toInsert)).? @@ -213,7 +213,7 @@ trait WriteService { newImage: NewImageMetaInformationV2DTO, file: UploadedFile, user: TokenUser - ): Try[ImageMetaInformation] = { + ): Try[ImageMetaInformation] = permitTry { val toInsert = converterService.asDomainImageMetaInformationV2(newImage, user).? insertAndStoreImage(toInsert, file, None, newImage.language) } @@ -334,7 +334,7 @@ trait WriteService { oldImage: ImageMetaInformation, language: String, user: TokenUser - ): Try[ImageMetaInformation] = { + ): Try[ImageMetaInformation] = permitTry { val imageForLang = oldImage.images.getOrElse(Seq.empty).find(_.language == language) val allOtherPaths = oldImage.images.getOrElse(Seq.empty).filterNot(_.language == language).map(_.fileName) diff --git a/image-api/src/main/scala/no/ndla/imageapi/service/search/ImageIndexService.scala b/image-api/src/main/scala/no/ndla/imageapi/service/search/ImageIndexService.scala index 8a620665d1..6113671848 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/service/search/ImageIndexService.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/service/search/ImageIndexService.scala @@ -12,7 +12,6 @@ import com.sksamuel.elastic4s.ElasticDsl.* import com.sksamuel.elastic4s.fields.{ElasticField, ObjectField} import com.sksamuel.elastic4s.requests.indexes.IndexRequest import com.sksamuel.elastic4s.requests.mappings.MappingDefinition -import com.typesafe.scalalogging.StrictLogging import no.ndla.common.CirceUtil import no.ndla.imageapi.Props import no.ndla.imageapi.model.domain.ImageMetaInformation @@ -22,9 +21,9 @@ import no.ndla.search.SearchLanguage trait ImageIndexService { this: SearchConverterService & IndexService & ImageRepository & Props & SearchLanguage => - val imageIndexService: ImageIndexService + lazy val imageIndexService: ImageIndexService - class ImageIndexService extends StrictLogging with IndexService[ImageMetaInformation, SearchableImage] { + class ImageIndexService extends IndexService { override val documentType: String = props.SearchDocument override val searchIndex: String = props.SearchIndex override val repository: Repository[ImageMetaInformation] = imageRepository diff --git a/image-api/src/main/scala/no/ndla/imageapi/service/search/ImageSearchService.scala b/image-api/src/main/scala/no/ndla/imageapi/service/search/ImageSearchService.scala index 5f884fb6dd..9c011927bd 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/service/search/ImageSearchService.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/service/search/ImageSearchService.scala @@ -30,13 +30,13 @@ import no.ndla.search.Elastic4sClient import scala.util.{Failure, Success, Try} trait ImageSearchService { - this: Elastic4sClient & ImageIndexService & SearchService & SearchConverterService & Props & ErrorHandling => - val imageSearchService: ImageSearchService - class ImageSearchService extends StrictLogging with SearchService[(SearchableImage, MatchedLanguage)] { - import props.{ElasticSearchIndexMaxResultWindow, ElasticSearchScrollKeepAlive} - private val noCopyright = boolQuery().not(termQuery("license", License.Copyrighted.toString)) - override val searchIndex: String = props.SearchIndex - override val indexService: ImageIndexService = imageIndexService + this: Elastic4sClient & ImageIndexService & SearchService & SearchConverterService & Props & ErrorHandling & + IndexService => + lazy val imageSearchService: ImageSearchService + class ImageSearchService extends SearchService[(SearchableImage, MatchedLanguage)] with StrictLogging { + private val noCopyright = boolQuery().not(termQuery("license", License.Copyrighted.toString)) + override val searchIndex: String = props.SearchIndex + override val indexService: IndexService = imageIndexService def hitToApiModel(hit: String, matchedLanguage: String): Try[(SearchableImage, MatchedLanguage)] = { val searchableImage = CirceUtil.tryParseAs[SearchableImage](hit) @@ -170,9 +170,9 @@ trait ImageSearchService { val (startAt, numResults) = getStartAtAndNumResults(settings.page, settings.pageSize) val requestedResultWindow = settings.page.getOrElse(1) * numResults - if (requestedResultWindow > ElasticSearchIndexMaxResultWindow) { + if (requestedResultWindow > props.ElasticSearchIndexMaxResultWindow) { logger.info( - s"Max supported results are $ElasticSearchIndexMaxResultWindow, user requested $requestedResultWindow" + s"Max supported results are ${props.ElasticSearchIndexMaxResultWindow}, user requested $requestedResultWindow" ) Failure(new ResultWindowTooLargeException(ImageErrorHelpers.WINDOW_TOO_LARGE_DESCRIPTION)) } else { @@ -188,7 +188,7 @@ trait ImageSearchService { // Only add scroll param if it is first page val searchWithScroll = if (startAt == 0 && settings.shouldScroll) { - searchToExecute.scroll(ElasticSearchScrollKeepAlive) + searchToExecute.scroll(props.ElasticSearchScrollKeepAlive) } else { searchToExecute } e4sClient.execute(searchWithScroll) match { diff --git a/image-api/src/main/scala/no/ndla/imageapi/service/search/IndexService.scala b/image-api/src/main/scala/no/ndla/imageapi/service/search/IndexService.scala index 845d11fbf7..d6ef490e5b 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/service/search/IndexService.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/service/search/IndexService.scala @@ -12,6 +12,7 @@ import cats.implicits.* import com.sksamuel.elastic4s.requests.indexes.IndexRequest import com.typesafe.scalalogging.StrictLogging import no.ndla.imageapi.Props +import no.ndla.imageapi.model.domain.ImageMetaInformation import no.ndla.imageapi.repository.{ImageRepository, Repository} import no.ndla.search.model.domain.{BulkIndexResult, ReindexResult} import no.ndla.search.{BaseIndexService, Elastic4sClient} @@ -21,13 +22,13 @@ import scala.util.{Failure, Success, Try} trait IndexService { this: Elastic4sClient & ImageRepository & BaseIndexService & Props => - trait IndexService[D, T <: AnyRef] extends BaseIndexService with StrictLogging { + abstract class IndexService extends BaseIndexService with StrictLogging { override val MaxResultWindowOption: Int = props.ElasticSearchIndexMaxResultWindow - val repository: Repository[D] + val repository: Repository[ImageMetaInformation] - def createIndexRequests(domainModel: D, indexName: String): Seq[IndexRequest] + def createIndexRequests(domainModel: ImageMetaInformation, indexName: String): Seq[IndexRequest] - def indexDocument(imported: D): Try[D] = { + def indexDocument(imported: ImageMetaInformation): Try[ImageMetaInformation] = { for { _ <- createIndexIfNotExists() requests = createIndexRequests(imported, searchIndex) @@ -61,7 +62,7 @@ trait IndexService { } } - def indexDocuments(contents: Seq[D], indexName: String): Try[BulkIndexResult] = { + def indexDocuments(contents: Seq[ImageMetaInformation], indexName: String): Try[BulkIndexResult] = { if (contents.isEmpty) { Success(BulkIndexResult.empty) } else { diff --git a/image-api/src/main/scala/no/ndla/imageapi/service/search/SearchConverterService.scala b/image-api/src/main/scala/no/ndla/imageapi/service/search/SearchConverterService.scala index 8a0672b2f9..a8b5ae16f9 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/service/search/SearchConverterService.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/service/search/SearchConverterService.scala @@ -31,7 +31,7 @@ import scala.util.{Failure, Success, Try} trait SearchConverterService { this: ConverterService & Props & SearchLanguage => - val searchConverterService: SearchConverterService + lazy val searchConverterService: SearchConverterService class SearchConverterService extends StrictLogging { diff --git a/image-api/src/main/scala/no/ndla/imageapi/service/search/SearchService.scala b/image-api/src/main/scala/no/ndla/imageapi/service/search/SearchService.scala index 98a0d0f1f0..869483b73a 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/service/search/SearchService.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/service/search/SearchService.scala @@ -26,9 +26,9 @@ import cats.implicits.* trait SearchService { this: Elastic4sClient & IndexService & SearchConverterService & Props => - trait SearchService[T] extends StrictLogging { + abstract class SearchService[T] extends StrictLogging { val searchIndex: String - val indexService: IndexService[?, ?] + val indexService: IndexService def hitToApiModel(hit: String, language: String): Try[T] @@ -95,7 +95,7 @@ trait SearchService { } } - protected def getStartAtAndNumResults(page: Option[Int], pageSize: Option[Int]): (Int, Int) = { + def getStartAtAndNumResults(page: Option[Int], pageSize: Option[Int]): (Int, Int) = { val numResults = pageSize match { case Some(num) => if (num > 0) num.min(props.MaxPageSize) else props.DefaultPageSize diff --git a/image-api/src/main/scala/no/ndla/imageapi/service/search/TagIndexService.scala b/image-api/src/main/scala/no/ndla/imageapi/service/search/TagIndexService.scala index 60b94ba9bd..c54e41f6fd 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/service/search/TagIndexService.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/service/search/TagIndexService.scala @@ -11,7 +11,6 @@ package no.ndla.imageapi.service.search import com.sksamuel.elastic4s.ElasticDsl.* import com.sksamuel.elastic4s.requests.indexes.IndexRequest import com.sksamuel.elastic4s.requests.mappings.MappingDefinition -import com.typesafe.scalalogging.StrictLogging import no.ndla.common.CirceUtil import no.ndla.imageapi.Props import no.ndla.imageapi.model.domain.ImageMetaInformation @@ -20,9 +19,9 @@ import no.ndla.imageapi.repository.{ImageRepository, Repository} trait TagIndexService { this: SearchConverterService & IndexService & ImageRepository & Props => - val tagIndexService: TagIndexService + lazy val tagIndexService: TagIndexService - class TagIndexService extends StrictLogging with IndexService[ImageMetaInformation, SearchableTag] { + class TagIndexService extends IndexService { override val documentType: String = props.TagSearchDocument override val searchIndex: String = props.TagSearchIndex override val repository: Repository[ImageMetaInformation] = imageRepository diff --git a/image-api/src/main/scala/no/ndla/imageapi/service/search/TagSearchService.scala b/image-api/src/main/scala/no/ndla/imageapi/service/search/TagSearchService.scala index 76b461e62f..4f86e68ca0 100644 --- a/image-api/src/main/scala/no/ndla/imageapi/service/search/TagSearchService.scala +++ b/image-api/src/main/scala/no/ndla/imageapi/service/search/TagSearchService.scala @@ -25,13 +25,12 @@ import scala.util.{Failure, Success, Try} trait TagSearchService { this: Elastic4sClient & SearchConverterService & SearchService & TagIndexService & SearchConverterService & Props & - ErrorHandling => - val tagSearchService: TagSearchService + ErrorHandling & IndexService => + lazy val tagSearchService: TagSearchService - class TagSearchService extends StrictLogging with SearchService[String] { - import props.{ElasticSearchIndexMaxResultWindow, ElasticSearchScrollKeepAlive, TagSearchIndex} - override val searchIndex: String = TagSearchIndex - override val indexService: TagIndexService = tagIndexService + class TagSearchService extends SearchService[String] with StrictLogging { + override val searchIndex: String = props.TagSearchIndex + override val indexService: IndexService = tagIndexService override def hitToApiModel(hit: String, language: String): Try[String] = { CirceUtil.tryParseAs[SearchableTag](hit).map(_.tag) @@ -92,9 +91,9 @@ trait TagSearchService { val (startAt, numResults) = getStartAtAndNumResults(Some(page), Some(pageSize)) val requestedResultWindow = pageSize * page - if (requestedResultWindow > ElasticSearchIndexMaxResultWindow) { + if (requestedResultWindow > props.ElasticSearchIndexMaxResultWindow) { logger.info( - s"Max supported results are $ElasticSearchIndexMaxResultWindow, user requested $requestedResultWindow" + s"Max supported results are $props.ElasticSearchIndexMaxResultWindow, user requested $requestedResultWindow" ) Failure(new ResultWindowTooLargeException(ImageErrorHelpers.WINDOW_TOO_LARGE_DESCRIPTION)) } else { @@ -107,7 +106,7 @@ trait TagSearchService { val searchWithScroll = if (startAt != 0) { searchToExecute } - else { searchToExecute.scroll(ElasticSearchScrollKeepAlive) } + else { searchToExecute.scroll(props.ElasticSearchScrollKeepAlive) } e4sClient.execute(searchWithScroll) match { case Success(response) => diff --git a/image-api/src/test/scala/no/ndla/imageapi/TestData.scala b/image-api/src/test/scala/no/ndla/imageapi/TestData.scala index 6d16a8eb76..e447d6d7c8 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/TestData.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/TestData.scala @@ -22,9 +22,9 @@ import java.awt.image.BufferedImage import java.io.InputStream import javax.imageio.ImageIO -trait TestData { +trait TestDataTrait { - class TestData { + class TestDataClass { def updated(): NDLADate = NDLADate.of(2017, 4, 1, 12, 15, 32) diff --git a/image-api/src/test/scala/no/ndla/imageapi/TestEnvironment.scala b/image-api/src/test/scala/no/ndla/imageapi/TestEnvironment.scala index f1c2eac1f7..65f7a0de0a 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/TestEnvironment.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/TestEnvironment.scala @@ -70,41 +70,41 @@ trait TestEnvironment with Props with ErrorHandling with DBMigrator - with TestData + with TestDataTrait with Random { - lazy val props = new ImageApiProperties - val TestData = new TestData + override lazy val props = new ImageApiProperties + val TestData: TestDataClass = new TestDataClass - val migrator: DBMigrator = mock[DBMigrator] - val s3Client: NdlaS3Client = mock[NdlaS3Client] + override lazy val migrator: DBMigrator = mock[DBMigrator] + override lazy val s3Client: NdlaS3Client = mock[NdlaS3Client] - val dataSource: HikariDataSource = mock[HikariDataSource] - val imageIndexService: ImageIndexService = mock[ImageIndexService] - val imageSearchService: ImageSearchService = mock[ImageSearchService] + override lazy val dataSource: HikariDataSource = mock[HikariDataSource] + override lazy val imageIndexService: ImageIndexService = mock[ImageIndexService] + override lazy val imageSearchService: ImageSearchService = mock[ImageSearchService] - val tagIndexService: TagIndexService = mock[TagIndexService] - val tagSearchService: TagSearchService = mock[TagSearchService] + override lazy val tagIndexService: TagIndexService = mock[TagIndexService] + override lazy val tagSearchService: TagSearchService = mock[TagSearchService] - val imageRepository: ImageRepository = mock[ImageRepository] - val readService: ReadService = mock[ReadService] - val writeService: WriteService = mock[WriteService] - val imageStorage: AmazonImageStorageService = mock[AmazonImageStorageService] + override lazy val imageRepository: ImageRepository = mock[ImageRepository] + override lazy val readService: ReadService = mock[ReadService] + override lazy val writeService: WriteService = mock[WriteService] + override lazy val imageStorage: AmazonImageStorageService = mock[AmazonImageStorageService] - val ndlaClient: NdlaClient = mock[NdlaClient] - val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] - val rawController: RawController = mock[RawController] - val healthController: HealthController = mock[HealthController] - val internController: InternController = mock[InternController] - val imageControllerV2: ImageControllerV2 = mock[ImageControllerV2] - val imageControllerV3: ImageControllerV3 = mock[ImageControllerV3] - val converterService: ConverterService = mock[ConverterService] - val validationService: ValidationService = mock[ValidationService] - var e4sClient: NdlaE4sClient = mock[NdlaE4sClient] - val searchConverterService: SearchConverterService = mock[SearchConverterService] - val imageConverter: ImageConverter = mock[ImageConverter] + override lazy val ndlaClient: NdlaClient = mock[NdlaClient] + override lazy val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] + override lazy val rawController: RawController = mock[RawController] + override lazy val healthController: HealthController = mock[HealthController] + override lazy val internController: InternController = mock[InternController] + override lazy val imageControllerV2: ImageControllerV2 = mock[ImageControllerV2] + override lazy val imageControllerV3: ImageControllerV3 = mock[ImageControllerV3] + override lazy val converterService: ConverterService = mock[ConverterService] + override lazy val validationService: ValidationService = mock[ValidationService] + var e4sClient: NdlaE4sClient = mock[NdlaE4sClient] + override lazy val searchConverterService: SearchConverterService = mock[SearchConverterService] + override lazy val imageConverter: ImageConverter = mock[ImageConverter] - val clock: SystemClock = mock[SystemClock] - val random: Random = mock[Random] + override lazy val clock: SystemClock = mock[SystemClock] + override lazy val random: Random = mock[Random] def services: List[TapirController] = List.empty val swagger: SwaggerController = mock[SwaggerController] diff --git a/image-api/src/test/scala/no/ndla/imageapi/controller/ImageControllerV2Test.scala b/image-api/src/test/scala/no/ndla/imageapi/controller/ImageControllerV2Test.scala index 9024bcea93..22d1f60ee3 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/controller/ImageControllerV2Test.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/controller/ImageControllerV2Test.scala @@ -41,8 +41,8 @@ class ImageControllerV2Test extends UnitSuite with TestEnvironment with TapirCon val authHeaderWithWrongRole = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6Ik9FSTFNVVU0T0RrNU56TTVNekkyTXpaRE9EazFOMFl3UXpkRE1EUXlPRFZDUXpRM1FUSTBNQSJ9.eyJodHRwczovL25kbGEubm8vbmRsYV9pZCI6Inh4eHl5eSIsImlzcyI6Imh0dHBzOi8vbmRsYS5ldS5hdXRoMC5jb20vIiwic3ViIjoieHh4eXl5QGNsaWVudHMiLCJhdWQiOiJuZGxhX3N5c3RlbSIsImlhdCI6MTUxMDMwNTc3MywiZXhwIjoxNTEwMzkyMTczLCJwZXJtaXNzaW9ucyI6WyJzb21lOm90aGVyIl0sImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.u8o7-FXyVzWurle2tP1pngad8KRja6VjFdmy71T4m0k" - override val converterService = new ConverterService - val controller: ImageControllerV2 = new ImageControllerV2 { + override lazy val converterService = new ConverterService + override val controller: ImageControllerV2 = new ImageControllerV2 { override val maxImageFileSizeBytes: Int = 10 } diff --git a/image-api/src/test/scala/no/ndla/imageapi/controller/ImageControllerV3Test.scala b/image-api/src/test/scala/no/ndla/imageapi/controller/ImageControllerV3Test.scala index f15ec189a9..3fe06a8b4e 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/controller/ImageControllerV3Test.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/controller/ImageControllerV3Test.scala @@ -22,8 +22,8 @@ class ImageControllerV3Test extends UnitSuite with TestEnvironment with TapirCon val authHeaderWithWrongRole = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6Ik9FSTFNVVU0T0RrNU56TTVNekkyTXpaRE9EazFOMFl3UXpkRE1EUXlPRFZDUXpRM1FUSTBNQSJ9.eyJodHRwczovL25kbGEubm8vbmRsYV9pZCI6Inh4eHl5eSIsImlzcyI6Imh0dHBzOi8vbmRsYS5ldS5hdXRoMC5jb20vIiwic3ViIjoieHh4eXl5QGNsaWVudHMiLCJhdWQiOiJuZGxhX3N5c3RlbSIsImlhdCI6MTUxMDMwNTc3MywiZXhwIjoxNTEwMzkyMTczLCJwZXJtaXNzaW9ucyI6WyJzb21lOm90aGVyIl0sImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.u8o7-FXyVzWurle2tP1pngad8KRja6VjFdmy71T4m0k" - override val converterService = new ConverterService - val controller: ImageControllerV3 = new ImageControllerV3 + override lazy val converterService = new ConverterService + override val controller: ImageControllerV3 = new ImageControllerV3 override def beforeEach(): Unit = { reset(clock) diff --git a/image-api/src/test/scala/no/ndla/imageapi/controller/InternControllerTest.scala b/image-api/src/test/scala/no/ndla/imageapi/controller/InternControllerTest.scala index d1c7710236..5e1f519abb 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/controller/InternControllerTest.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/controller/InternControllerTest.scala @@ -25,8 +25,8 @@ import org.mockito.ArgumentMatchers.{any, eq as eqTo} import org.mockito.Mockito.{doReturn, never, reset, verify, verifyNoMoreInteractions, when} class InternControllerTest extends UnitSuite with TestEnvironment with TapirControllerTest { - override val converterService = new ConverterService - val controller: InternController = new InternController + override lazy val converterService = new ConverterService + override val controller: InternController = new InternController val updated: NDLADate = NDLADate.of(2017, 4, 1, 12, 15, 32) val BySa: LicenseDefinition = getLicense(CC_BY.toString).get diff --git a/image-api/src/test/scala/no/ndla/imageapi/controller/RawControllerTest.scala b/image-api/src/test/scala/no/ndla/imageapi/controller/RawControllerTest.scala index cdfdbbbf64..85f7f9aadd 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/controller/RawControllerTest.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/controller/RawControllerTest.scala @@ -26,8 +26,8 @@ class RawControllerTest extends UnitSuite with TestEnvironment with TapirControl val imageGifName = "ndla_logo.gif" val imageSvgName = "logo.svg" - override val imageConverter = new ImageConverter - val controller: RawController = new RawController + override lazy val imageConverter = new ImageConverter + override val controller: RawController = new RawController val id = 1L val idGif = 1L diff --git a/image-api/src/test/scala/no/ndla/imageapi/integration/ImageStorageServiceTest.scala b/image-api/src/test/scala/no/ndla/imageapi/integration/ImageStorageServiceTest.scala index e29d605b07..fb3c4ad79f 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/integration/ImageStorageServiceTest.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/integration/ImageStorageServiceTest.scala @@ -18,11 +18,11 @@ import scala.util.{Failure, Success} class ImageStorageServiceTest extends UnitSuite with TestEnvironment { - val ImageStorageName = props.StorageName - val ImageWithNoThumb = TestData.nonexistingWithoutThumb - val Content = "content" - val ContentType = "image/jpeg" - override val imageStorage = new AmazonImageStorageService + val ImageStorageName = props.StorageName + val ImageWithNoThumb = TestData.nonexistingWithoutThumb + val Content = "content" + val ContentType = "image/jpeg" + override lazy val imageStorage = new AmazonImageStorageService override def beforeEach(): Unit = { reset(s3Client) diff --git a/image-api/src/test/scala/no/ndla/imageapi/repository/ImageRepositoryTest.scala b/image-api/src/test/scala/no/ndla/imageapi/repository/ImageRepositoryTest.scala index 9b83b24542..9fece1919d 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/repository/ImageRepositoryTest.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/repository/ImageRepositoryTest.scala @@ -18,8 +18,8 @@ import scala.util.{Success, Try} import scalikejdbc.* class ImageRepositoryTest extends DatabaseIntegrationSuite with UnitSuite with TestEnvironment { - override val dataSource = testDataSource.get - override val migrator = new DBMigrator + override lazy val dataSource = testDataSource.get + override lazy val migrator = new DBMigrator var repository: ImageRepository = _ this.setDatabaseEnvironment() diff --git a/image-api/src/test/scala/no/ndla/imageapi/service/ConverterServiceTest.scala b/image-api/src/test/scala/no/ndla/imageapi/service/ConverterServiceTest.scala index 9a04228cec..875213ba81 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/service/ConverterServiceTest.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/service/ConverterServiceTest.scala @@ -18,7 +18,7 @@ import scala.util.Success class ConverterServiceTest extends UnitSuite with TestEnvironment { - override val converterService = new ConverterService + override lazy val converterService = new ConverterService val updated: NDLADate = NDLADate.of(2017, 4, 1, 12, 15, 32) @@ -77,13 +77,21 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { test("That asApiImageMetaInformationWithDomainUrl returns links with domain urls") { { val Success(apiImage) = - converterService.asApiImageMetaInformationWithDomainUrlV2(DefaultImageMetaInformation, Some("nb"), None) + converterService.asApiImageMetaInformationWithDomainUrlV2( + DefaultImageMetaInformation, + Some("nb"), + None + ): @unchecked apiImage.metaUrl should equal(s"${props.ImageApiV2UrlBase}1") apiImage.imageUrl should equal(s"${props.RawImageUrlBase}/123.png") } { val Success(apiImage) = - converterService.asApiImageMetaInformationWithDomainUrlV2(WantingImageMetaInformation, Some("nb"), None) + converterService.asApiImageMetaInformationWithDomainUrlV2( + WantingImageMetaInformation, + Some("nb"), + None + ): @unchecked apiImage.metaUrl should equal(s"${props.ImageApiV2UrlBase}1") apiImage.imageUrl should equal(s"${props.RawImageUrlBase}/123.png") } @@ -91,14 +99,18 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { test("That asApiImageMetaInformationWithApplicationUrlAndSingleLanguage returns links with applicationUrl") { val Success(apiImage) = - converterService.asApiImageMetaInformationWithApplicationUrlV2(DefaultImageMetaInformation, None, None) + converterService.asApiImageMetaInformationWithApplicationUrlV2( + DefaultImageMetaInformation, + None, + None + ): @unchecked apiImage.metaUrl should equal(s"${props.Domain}/image-api/v2/images/1") apiImage.imageUrl should equal(s"${props.Domain}/image-api/raw/123.png") } test("That asApiImageMetaInformationWithDomainUrlAndSingleLanguage returns links with domain urls") { val Success(apiImage) = - converterService.asApiImageMetaInformationWithDomainUrlV2(DefaultImageMetaInformation, None, None) + converterService.asApiImageMetaInformationWithDomainUrlV2(DefaultImageMetaInformation, None, None): @unchecked apiImage.metaUrl should equal("http://api-gateway.ndla-local/image-api/v2/images/1") apiImage.imageUrl should equal("http://api-gateway.ndla-local/image-api/raw/123.png") } @@ -110,7 +122,7 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { DefaultImageMetaInformation, Some("RandomLangauge"), None - ) + ): @unchecked apiImage.metaUrl should equal(s"${props.Domain}/image-api/v2/images/1") apiImage.imageUrl should equal(s"${props.Domain}/image-api/raw/123.png") @@ -122,28 +134,32 @@ class ConverterServiceTest extends UnitSuite with TestEnvironment { DefaultImageMetaInformation, Some("RandomLangauge"), None - ) + ): @unchecked apiImage.metaUrl should equal("http://api-gateway.ndla-local/image-api/v2/images/1") apiImage.imageUrl should equal("http://api-gateway.ndla-local/image-api/raw/123.png") } test("that asImageMetaInformationV2 properly") { - val Success(result1) = converterService.asImageMetaInformationV2(MultiLangImage, Some("nb"), "", None, None) + val Success(result1) = + converterService.asImageMetaInformationV2(MultiLangImage, Some("nb"), "", None, None): @unchecked result1.id should be("2") result1.title.language should be("nn") - val Success(result2) = converterService.asImageMetaInformationV2(MultiLangImage, Some("en"), "", None, None) + val Success(result2) = + converterService.asImageMetaInformationV2(MultiLangImage, Some("en"), "", None, None): @unchecked result2.id should be("2") result2.title.language should be("en") - val Success(result3) = converterService.asImageMetaInformationV2(MultiLangImage, Some("nn"), "", None, None) + val Success(result3) = + converterService.asImageMetaInformationV2(MultiLangImage, Some("nn"), "", None, None): @unchecked result3.id should be("2") result3.title.language should be("nn") } test("that asImageMetaInformationV2 returns sorted supportedLanguages") { - val Success(result) = converterService.asImageMetaInformationV2(MultiLangImage, Some("nb"), "", None, None) + val Success(result) = + converterService.asImageMetaInformationV2(MultiLangImage, Some("nb"), "", None, None): @unchecked result.supportedLanguages should be(Seq("nn", "en", "und")) } diff --git a/image-api/src/test/scala/no/ndla/imageapi/service/ReadServiceTest.scala b/image-api/src/test/scala/no/ndla/imageapi/service/ReadServiceTest.scala index 3d55284e5d..30df860e72 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/service/ReadServiceTest.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/service/ReadServiceTest.scala @@ -23,8 +23,8 @@ import scalikejdbc.DBSession import scala.util.{Failure, Success} class ReadServiceTest extends UnitSuite with TestEnvironment { - override val readService = new ReadService - override val converterService = new ConverterService + override lazy val readService = new ReadService + override lazy val converterService = new ConverterService test("That path to id conversion works as expected for id paths") { val id = 1234L diff --git a/image-api/src/test/scala/no/ndla/imageapi/service/ValidationServiceTest.scala b/image-api/src/test/scala/no/ndla/imageapi/service/ValidationServiceTest.scala index 14fee1cf3d..0f9dfe19b8 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/service/ValidationServiceTest.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/service/ValidationServiceTest.scala @@ -18,7 +18,7 @@ import no.ndla.mapping.License.CC_BY import org.mockito.Mockito.{reset, when} class ValidationServiceTest extends UnitSuite with TestEnvironment { - override val validationService = new ValidationService + override lazy val validationService = new ValidationService val fileMock: UploadedFile = mock[UploadedFile] def updated(): NDLADate = NDLADate.of(2017, 4, 1, 12, 15, 32) @@ -67,7 +67,7 @@ class ValidationServiceTest extends UnitSuite with TestEnvironment { test("validateImageFile returns a validation message if file has an unknown extension") { val fileName = "image.asdf" when(fileMock.fileName).thenReturn(Some(fileName)) - val Some(result) = validationService.validateImageFile(fileMock) + val Some(result) = validationService.validateImageFile(fileMock): @unchecked result.message.contains(s"The file $fileName does not have a known file extension") should be(true) } @@ -76,7 +76,7 @@ class ValidationServiceTest extends UnitSuite with TestEnvironment { val fileName = "image.jpg" when(fileMock.fileName).thenReturn(Some(fileName)) when(fileMock.contentType).thenReturn(Some("text/html")) - val Some(result) = validationService.validateImageFile(fileMock) + val Some(result) = validationService.validateImageFile(fileMock): @unchecked result.message.contains(s"The file $fileName is not a valid image file.") should be(true) } diff --git a/image-api/src/test/scala/no/ndla/imageapi/service/WriteServiceTest.scala b/image-api/src/test/scala/no/ndla/imageapi/service/WriteServiceTest.scala index 5239e41f09..492db485a5 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/service/WriteServiceTest.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/service/WriteServiceTest.scala @@ -28,10 +28,10 @@ import java.io.ByteArrayInputStream import scala.util.{Failure, Success} class WriteServiceTest extends UnitSuite with TestEnvironment { - override val writeService = new WriteService - override val converterService = new ConverterService - val newFileName = "AbCdeF.mp3" - val fileMock1: UploadedFile = mock[UploadedFile] + override lazy val writeService = new WriteService + override lazy val converterService = new ConverterService + val newFileName = "AbCdeF.mp3" + val fileMock1: UploadedFile = mock[UploadedFile] val newImageMeta: NewImageMetaInformationV2DTO = NewImageMetaInformationV2DTO( "title", @@ -85,7 +85,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { when(fileMock1.contentType).thenReturn(Some("image/jpeg")) val bytes = TestData.NdlaLogoImage.stream.readAllBytes() when(fileMock1.stream).thenReturn(new ByteArrayInputStream(bytes)) - when(fileMock1.fileSize).thenReturn(1024) + when(fileMock1.fileSize).thenReturn(1024L) when(fileMock1.fileName).thenReturn(Some("file.jpg")) when(random.string(any)).thenCallRealMethod() @@ -94,7 +94,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { reset(imageStorage) reset(tagIndexService) when(imageRepository.insert(any[ImageMetaInformation])(any[DBSession])) - .thenReturn(domainImageMeta.copy(id = Some(1))) + .thenReturn(domainImageMeta.copy(id = Some(1L))) } test("randomFileName should return a random filename with a given length and extension") { @@ -185,16 +185,17 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { .thenReturn(Success(newFileName)) when(imageIndexService.indexDocument(any[ImageMetaInformation])).thenReturn(Failure(new RuntimeException)) when(imageStorage.deleteObject(any)).thenReturn(Success(())) - when(imageRepository.insert(any)(any)).thenReturn(domainImageMeta.copy(id = Some(100))) - when(imageRepository.insertImageFile(any, any, any)(any)).thenAnswer((i: InvocationOnMock) => { + when(imageRepository.insert(any)(using any)).thenReturn(domainImageMeta.copy(id = Some(100L))) + when(imageRepository.insertImageFile(any, any, any)(using any)).thenAnswer((i: InvocationOnMock) => { val imageId = i.getArgument[Long](0) val fileName = i.getArgument[String](1) val document = i.getArgument[ImageFileDataDocument](2) - Success(document.toFull(1, fileName, imageId)) + Success(document.toFull(1L, fileName, imageId)) }) - writeService.storeNewImage(newImageMeta, fileMock1, TokenUser.SystemUser).isFailure should be(true) - verify(imageRepository, times(1)).insert(any[ImageMetaInformation])(any[DBSession]) + val result = writeService.storeNewImage(newImageMeta, fileMock1, TokenUser.SystemUser) + result.isFailure should be(true) + verify(imageRepository, times(1)).insert(any[ImageMetaInformation])(using any[DBSession]) verify(imageStorage, times(1)).deleteObject(any[String]) } @@ -578,7 +579,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { when(fileMock.fileName).thenReturn(Some("someupload.jpg")) when(fileMock.contentType).thenReturn(Some("image/jpg")) when(fileMock.stream).thenReturn(TestData.NdlaLogoImage.stream) - when(fileMock.fileSize).thenReturn(1337) + when(fileMock.fileSize).thenReturn(1337L) when(validationService.validateImageFile(any)).thenReturn(None) when(validationService.validate(any, any)).thenAnswer((i: InvocationOnMock) => { Success(i.getArgument[domain.ImageMetaInformation](0)) @@ -671,7 +672,7 @@ class WriteServiceTest extends UnitSuite with TestEnvironment { when(fileMock.fileName).thenReturn(Some("someupload.jpg")) when(fileMock.contentType).thenReturn(Some("image/jpg")) when(fileMock.stream).thenReturn(TestData.NdlaLogoImage.stream) - when(fileMock.fileSize).thenReturn(1337) + when(fileMock.fileSize).thenReturn(1337L) when(validationService.validateImageFile(any)).thenReturn(None) when(validationService.validate(any, any)).thenAnswer((i: InvocationOnMock) => { Success(i.getArgument[domain.ImageMetaInformation](0)) diff --git a/image-api/src/test/scala/no/ndla/imageapi/service/search/ImageSearchServiceTest.scala b/image-api/src/test/scala/no/ndla/imageapi/service/search/ImageSearchServiceTest.scala index c2fc1a04da..92066476fa 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/service/search/ImageSearchServiceTest.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/service/search/ImageSearchServiceTest.scala @@ -22,28 +22,20 @@ import no.ndla.network.tapir.auth.TokenUser import no.ndla.scalatestsuite.ElasticsearchIntegrationSuite import org.mockito.ArgumentMatchers.any import org.mockito.Mockito.when -import org.scalatest.PrivateMethodTester import scala.util.Success -class ImageSearchServiceTest - extends ElasticsearchIntegrationSuite - with UnitSuite - with TestEnvironment - with PrivateMethodTester { +class ImageSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite with TestEnvironment { import TestData.searchSettings - import props.{DefaultPageSize, MaxPageSize} e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("http://localhost:9200")) - override val searchConverterService = new SearchConverterService - override val converterService = new ConverterService - override val imageIndexService: ImageIndexService = new ImageIndexService { + override lazy val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val imageIndexService: ImageIndexService = new ImageIndexService { override val indexShards = 1 } - override val imageSearchService = new ImageSearchService - - val getStartAtAndNumResults: PrivateMethod[(Int, Int)] = PrivateMethod[(Int, Int)](Symbol("getStartAtAndNumResults")) + override lazy val imageSearchService = new ImageSearchService val largeImage: ImageFileData = ImageFileData(1, "large-full-url", 10000, "jpg", None, "und", 4) val smallImage: ImageFileData = ImageFileData(2, "small-full-url", 100, "jpg", None, "und", 6) @@ -191,20 +183,20 @@ class ImageSearchServiceTest } test("That getStartAtAndNumResults returns default values for None-input") { - imageSearchService invokePrivate getStartAtAndNumResults(None, None) should equal((0, DefaultPageSize)) + imageSearchService.getStartAtAndNumResults(None, None) should equal((0, props.DefaultPageSize)) } test("That getStartAtAndNumResults returns SEARCH_MAX_PAGE_SIZE for value greater than SEARCH_MAX_PAGE_SIZE") { - imageSearchService invokePrivate getStartAtAndNumResults(None, Some(10001)) should equal((0, MaxPageSize)) + imageSearchService.getStartAtAndNumResults(None, Some(10001)) should equal((0, props.MaxPageSize)) } test( "That getStartAtAndNumResults returns the correct calculated start at for page and page-size with default page-size" ) { val page = 74 - val expectedStartAt = (page - 1) * DefaultPageSize - imageSearchService invokePrivate getStartAtAndNumResults(Some(page), None) should equal( - (expectedStartAt, DefaultPageSize) + val expectedStartAt = (page - 1) * props.DefaultPageSize + imageSearchService.getStartAtAndNumResults(Some(page), None) should equal( + (expectedStartAt, props.DefaultPageSize) ) } @@ -212,7 +204,7 @@ class ImageSearchServiceTest val page = 123 val pageSize = 43 val expectedStartAt = (page - 1) * pageSize - imageSearchService invokePrivate getStartAtAndNumResults(Some(page), Some(pageSize)) should equal( + imageSearchService.getStartAtAndNumResults(Some(page), Some(pageSize)) should equal( (expectedStartAt, pageSize) ) } @@ -227,7 +219,8 @@ class ImageSearchServiceTest } test("That all filtering on minimumsize only returns images larger than minimumsize") { - val Success(searchResult) = imageSearchService.matchingQuery(searchSettings.copy(minimumSize = Some(500)), None) + val Success(searchResult) = + imageSearchService.matchingQuery(searchSettings.copy(minimumSize = Some(500)), None): @unchecked searchResult.totalCount should be(2) searchResult.results.size should be(2) searchResult.results.head.id should be("1") @@ -236,7 +229,7 @@ class ImageSearchServiceTest test("That all filtering on license only returns images with given license") { val Success(searchResult) = - imageSearchService.matchingQuery(searchSettings.copy(license = Some(PublicDomain.toString)), None) + imageSearchService.matchingQuery(searchSettings.copy(license = Some(PublicDomain.toString)), None): @unchecked searchResult.totalCount should be(1) searchResult.results.size should be(1) searchResult.results.head.id should be("2") @@ -244,9 +237,9 @@ class ImageSearchServiceTest test("That paging returns only hits on current page and not more than page-size") { val Success(searchResultPage1) = - imageSearchService.matchingQuery(searchSettings.copy(page = Some(1), pageSize = Some(2)), None) + imageSearchService.matchingQuery(searchSettings.copy(page = Some(1), pageSize = Some(2)), None): @unchecked val Success(searchResultPage2) = - imageSearchService.matchingQuery(searchSettings.copy(page = Some(2), pageSize = Some(2)), None) + imageSearchService.matchingQuery(searchSettings.copy(page = Some(2), pageSize = Some(2)), None): @unchecked searchResultPage1.totalCount should be(5) searchResultPage1.page.get should be(1) searchResultPage1.pageSize should be(2) @@ -267,7 +260,7 @@ class ImageSearchServiceTest imageSearchService.matchingQuery( searchSettings.copy(minimumSize = Some(500), license = Some(PublicDomain.toString)), None - ) + ): @unchecked searchResult.totalCount should be(1) searchResult.results.size should be(1) searchResult.results.head.id should be("2") @@ -275,7 +268,7 @@ class ImageSearchServiceTest test("That search matches title and alttext ordered by relevance") { val res = imageSearchService.matchingQuery(searchSettings.copy(query = Some("bil")), None) - val Success(searchResult) = res + val Success(searchResult) = res: @unchecked searchResult.totalCount should be(2) searchResult.results.size should be(2) searchResult.results.head.id should be("1") @@ -284,7 +277,10 @@ class ImageSearchServiceTest test("That search matches title") { val Success(searchResult) = - imageSearchService.matchingQuery(searchSettings.copy(query = Some("Pingvinen"), language = "nb"), None) + imageSearchService.matchingQuery( + searchSettings.copy(query = Some("Pingvinen"), language = "nb"), + None + ): @unchecked searchResult.totalCount should be(1) searchResult.results.size should be(1) searchResult.results.head.id should be("2") @@ -292,14 +288,15 @@ class ImageSearchServiceTest test("That search matches id search") { val Success(searchResult) = - imageSearchService.matchingQuery(searchSettings.copy(query = Some("1"), language = "nb"), None) + imageSearchService.matchingQuery(searchSettings.copy(query = Some("1"), language = "nb"), None): @unchecked searchResult.totalCount should be(1) searchResult.results.size should be(1) searchResult.results.head.id should be("1") } test("That search on author matches corresponding author on image") { - val Success(searchResult) = imageSearchService.matchingQuery(searchSettings.copy(query = Some("Bruce Wayne")), None) + val Success(searchResult) = + imageSearchService.matchingQuery(searchSettings.copy(query = Some("Bruce Wayne")), None): @unchecked searchResult.totalCount should be(1) searchResult.results.size should be(1) searchResult.results.head.id should be("2") @@ -307,7 +304,7 @@ class ImageSearchServiceTest test("That search matches tags") { val Success(searchResult) = - imageSearchService.matchingQuery(searchSettings.copy(query = Some("and"), language = "nb"), None) + imageSearchService.matchingQuery(searchSettings.copy(query = Some("and"), language = "nb"), None): @unchecked searchResult.totalCount should be(1) searchResult.results.size should be(1) searchResult.results.head.id should be("3") @@ -315,7 +312,7 @@ class ImageSearchServiceTest test("That search defaults to nb if no language is specified") { val Success(searchResult) = - imageSearchService.matchingQuery(searchSettings.copy(query = Some("Bilde av en and")), None) + imageSearchService.matchingQuery(searchSettings.copy(query = Some("Bilde av en and")), None): @unchecked searchResult.totalCount should be(4) searchResult.results.size should be(4) searchResult.results.head.id should be("1") @@ -325,7 +322,8 @@ class ImageSearchServiceTest } test("That search matches title with unknown language analyzed in Norwegian") { - val Success(searchResult) = imageSearchService.matchingQuery(searchSettings.copy(query = Some("blomstene")), None) + val Success(searchResult) = + imageSearchService.matchingQuery(searchSettings.copy(query = Some("blomstene")), None): @unchecked searchResult.totalCount should be(1) searchResult.results.size should be(1) searchResult.results.head.id should be("4") @@ -341,7 +339,7 @@ class ImageSearchServiceTest pageSize = Some(10) ), None - ) + ): @unchecked search1.results.map(_.id) should equal(Seq("1", "3")) val Success(search2) = imageSearchService.matchingQuery( @@ -352,7 +350,7 @@ class ImageSearchServiceTest pageSize = Some(10) ), None - ) + ): @unchecked search2.results.map(_.id) should equal(Seq("1", "2")) val Success(search3) = imageSearchService.matchingQuery( @@ -363,7 +361,7 @@ class ImageSearchServiceTest pageSize = Some(10) ), None - ) + ): @unchecked search3.results.map(_.id) should equal(Seq("2", "3")) val Success(search4) = imageSearchService.matchingQuery( @@ -374,7 +372,7 @@ class ImageSearchServiceTest pageSize = Some(10) ), None - ) + ): @unchecked search4.results.map(_.id) should equal(Seq("1")) } @@ -385,7 +383,7 @@ class ImageSearchServiceTest language = "*" ), None - ) + ): @unchecked searchResult1.totalCount should be(1) searchResult1.results.size should be(1) searchResult1.results.head.id should be("5") @@ -399,7 +397,7 @@ class ImageSearchServiceTest sort = Sort.ByTitleDesc ), None - ) + ): @unchecked searchResult2.totalCount should be(1) searchResult2.results.size should be(1) searchResult2.results.head.id should be("5") @@ -413,7 +411,7 @@ class ImageSearchServiceTest language = "ait" // Arikem ), None - ) + ): @unchecked searchResult1.totalCount should be(0) } @@ -424,7 +422,7 @@ class ImageSearchServiceTest language = "en" ), None - ) + ): @unchecked searchResult.totalCount should be(1) searchResult.results.size should be(1) searchResult.results.head.id should be("5") @@ -437,7 +435,7 @@ class ImageSearchServiceTest language = "nn" ), None - ) + ): @unchecked searchResult2.totalCount should be(1) searchResult2.results.size should be(1) searchResult2.results.head.id should be("5") @@ -452,7 +450,7 @@ class ImageSearchServiceTest language = "nn" ), None - ) + ): @unchecked result.totalCount should be(1) result.results.size should be(1) @@ -469,11 +467,11 @@ class ImageSearchServiceTest shouldScroll = true ), None - ) + ): @unchecked - val Success(scroll1) = imageSearchService.scrollV2(initialSearch.scrollId.get, "*", None) - val Success(scroll2) = imageSearchService.scrollV2(scroll1.scrollId.get, "*", None) - val Success(scroll3) = imageSearchService.scrollV2(scroll2.scrollId.get, "*", None) + val Success(scroll1) = imageSearchService.scrollV2(initialSearch.scrollId.get, "*", None): @unchecked + val Success(scroll2) = imageSearchService.scrollV2(scroll1.scrollId.get, "*", None): @unchecked + val Success(scroll3) = imageSearchService.scrollV2(scroll2.scrollId.get, "*", None): @unchecked initialSearch.results.map(_.id) should be(expectedIds.head) scroll1.results.map(_.id) should be(expectedIds(1)) @@ -491,11 +489,11 @@ class ImageSearchServiceTest shouldScroll = true ), None - ) + ): @unchecked - val Success(scroll1) = imageSearchService.scroll(initialSearch.scrollId.get, "*") - val Success(scroll2) = imageSearchService.scroll(scroll1.scrollId.get, "*") - val Success(scroll3) = imageSearchService.scroll(scroll2.scrollId.get, "*") + val Success(scroll1) = imageSearchService.scroll(initialSearch.scrollId.get, "*"): @unchecked + val Success(scroll2) = imageSearchService.scroll(scroll1.scrollId.get, "*"): @unchecked + val Success(scroll3) = imageSearchService.scroll(scroll2.scrollId.get, "*"): @unchecked initialSearch.results.map(_._1.id) should be(expectedIds.head) scroll1.results.map(_._1.id) should be(expectedIds(1)) @@ -510,7 +508,7 @@ class ImageSearchServiceTest sort = Sort.ByTitleDesc ), None - ) + ): @unchecked searchResult1.results.map(_.id) should be(Seq("2", "3", "1")) } @@ -522,7 +520,7 @@ class ImageSearchServiceTest language = "*" ), None - ) + ): @unchecked searchResult1.results.map(_.id) should be(Seq()) @@ -532,7 +530,7 @@ class ImageSearchServiceTest language = "*" ), Some(TokenUser("someeditor", Set(IMAGE_API_WRITE), None)) - ) + ): @unchecked searchResult2.results.map(_.id) should be(Seq("2")) } @@ -545,7 +543,7 @@ class ImageSearchServiceTest modelReleased = Seq(NO) ), None - ) + ): @unchecked searchResult1.results.map(_.id) should be(Seq("1")) @@ -555,7 +553,7 @@ class ImageSearchServiceTest modelReleased = Seq(NOT_APPLICABLE) ), None - ) + ): @unchecked searchResult2.results.map(_.id) should be(Seq("2")) @@ -565,7 +563,7 @@ class ImageSearchServiceTest modelReleased = Seq(YES) ), None - ) + ): @unchecked searchResult3.results.map(_.id) should be(Seq("3", "4", "5")) @@ -575,7 +573,7 @@ class ImageSearchServiceTest modelReleased = Seq.empty ), None - ) + ): @unchecked searchResult4.results.map(_.id) should be(Seq("1", "2", "3", "4", "5")) @@ -585,7 +583,7 @@ class ImageSearchServiceTest modelReleased = Seq(NO, NOT_APPLICABLE) ), None - ) + ): @unchecked searchResult5.results.map(_.id) should be(Seq("1", "2")) @@ -593,7 +591,7 @@ class ImageSearchServiceTest test("That search result includes updatedBy field") { val Success(searchResult) = - imageSearchService.matchingQuery(searchSettings.copy(query = Some("1"), language = "nb"), None) + imageSearchService.matchingQuery(searchSettings.copy(query = Some("1"), language = "nb"), None): @unchecked searchResult.totalCount should be(1) searchResult.results.size should be(1) searchResult.results.head.lastUpdated should be(updated) @@ -607,7 +605,7 @@ class ImageSearchServiceTest language = "und" ), None - ) + ): @unchecked searchResult1.totalCount should be(1) searchResult1.results.size should be(1) searchResult1.results.head.id should be("5") @@ -620,7 +618,7 @@ class ImageSearchServiceTest language = "en" ), None - ) + ): @unchecked searchResult2.totalCount should be(1) searchResult2.results.size should be(1) searchResult2.results.head.id should be("5") @@ -635,7 +633,7 @@ class ImageSearchServiceTest podcastFriendly = Some(true) ), None - ) + ): @unchecked searchResult1.results.map(_.id) should be(Seq("5")) } diff --git a/image-api/src/test/scala/no/ndla/imageapi/service/search/SearchServiceTest.scala b/image-api/src/test/scala/no/ndla/imageapi/service/search/SearchServiceTest.scala index fa9a178055..160f37a41b 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/service/search/SearchServiceTest.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/service/search/SearchServiceTest.scala @@ -16,7 +16,7 @@ import scala.util.Success class SearchServiceTest extends UnitSuite with TestEnvironment { - override val imageSearchService = new ImageSearchService + override lazy val imageSearchService = new ImageSearchService test("That createEmptyIndexIfNoIndexesExist never creates empty index if an index already exists") { when(imageIndexService.findAllIndexes(any[String])).thenReturn(Success(Seq("index1"))) diff --git a/image-api/src/test/scala/no/ndla/imageapi/service/search/TagSearchServiceTest.scala b/image-api/src/test/scala/no/ndla/imageapi/service/search/TagSearchServiceTest.scala index a9295f9b21..a6b677d506 100644 --- a/image-api/src/test/scala/no/ndla/imageapi/service/search/TagSearchServiceTest.scala +++ b/image-api/src/test/scala/no/ndla/imageapi/service/search/TagSearchServiceTest.scala @@ -20,16 +20,16 @@ class TagSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("http://localhost:9200")) - val indexName = "tags-testing" - override val tagSearchService: TagSearchService = new TagSearchService { + val indexName = "tags-testing" + override lazy val tagSearchService: TagSearchService = new TagSearchService { override val searchIndex: String = indexName } - override val tagIndexService: TagIndexService = new TagIndexService { + override lazy val tagIndexService: TagIndexService = new TagIndexService { override val indexShards: Int = 1 override val searchIndex: String = indexName } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService val image1: ImageMetaInformation = TestData.elg.copy( tags = Seq( @@ -88,14 +88,14 @@ class TagSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite } test("That searching for tags returns sensible results") { - val Success(result) = tagSearchService.matchingQuery("test", "nb", 1, 100, Sort.ByRelevanceDesc) + val Success(result) = tagSearchService.matchingQuery("test", "nb", 1, 100, Sort.ByRelevanceDesc): @unchecked result.totalCount should be(3) result.results should be(Seq("test", "testemer", "testing")) } test("That only prefixes are matched") { - val Success(result) = tagSearchService.matchingQuery("kylling", "nb", 1, 100, Sort.ByRelevanceDesc) + val Success(result) = tagSearchService.matchingQuery("kylling", "nb", 1, 100, Sort.ByRelevanceDesc): @unchecked result.totalCount should be(1) result.results should be(Seq("kyllingfilet")) diff --git a/integration-tests/src/test/scala/no/ndla/integrationtests/draftapi/articleapi/ArticleApiClientTest.scala b/integration-tests/src/test/scala/no/ndla/integrationtests/draftapi/articleapi/ArticleApiClientTest.scala index 93f3d03783..459e5b91f6 100644 --- a/integration-tests/src/test/scala/no/ndla/integrationtests/draftapi/articleapi/ArticleApiClientTest.scala +++ b/integration-tests/src/test/scala/no/ndla/integrationtests/draftapi/articleapi/ArticleApiClientTest.scala @@ -34,7 +34,7 @@ class ArticleApiClientTest with DatabaseIntegrationSuite with UnitSuite with draftapi.TestEnvironment { - override val ndlaClient = new NdlaClient + override lazy val ndlaClient = new NdlaClient // NOTE: There is some weirdness with loading the resources in validation library if this isn't called. // For some reason this fixes that. @@ -80,8 +80,8 @@ class ArticleApiClientTest super.afterAll() } - val idResponse: ContentIdDTO = ContentIdDTO(1) - override val converterService = new ConverterService + val idResponse: ContentIdDTO = ContentIdDTO(1) + override lazy val converterService = new ConverterService val testCopyright: common.draft.DraftCopyright = common.draft.DraftCopyright( Some("CC-BY-SA-4.0"), @@ -141,9 +141,9 @@ class ArticleApiClientTest val authHeaderMap: Map[String, String] = Map("Authorization" -> s"Bearer $exampleToken") val authUser: TokenUser = TokenUser.SystemUser.copy(originalToken = Some(exampleToken)) - class LocalArticleApiTestData extends articleapi.Props with articleapi.TestData { - override val props: ArticleApiProperties = articleApiProperties - val td = new TestData + class LocalArticleApiTestData extends articleapi.Props with articleapi.TestDataT { + override lazy val props: ArticleApiProperties = articleApiProperties + val td = new TestDataClass def setupArticles(): Try[Boolean] = (1L to 10) diff --git a/integration-tests/src/test/scala/no/ndla/integrationtests/searchapi/articleapi/ArticleApiClientTest.scala b/integration-tests/src/test/scala/no/ndla/integrationtests/searchapi/articleapi/ArticleApiClientTest.scala index 9623259465..41f43841a7 100644 --- a/integration-tests/src/test/scala/no/ndla/integrationtests/searchapi/articleapi/ArticleApiClientTest.scala +++ b/integration-tests/src/test/scala/no/ndla/integrationtests/searchapi/articleapi/ArticleApiClientTest.scala @@ -30,9 +30,9 @@ class ArticleApiClientTest with UnitSuite with searchapi.TestEnvironment with HasDatabaseProps { - override val ndlaClient = new NdlaClient - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val ndlaClient = new NdlaClient + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService val articleApiPort: Int = findFreePort val pgc: PostgreSQLContainer[?] = postgresContainer.get @@ -77,9 +77,9 @@ class ArticleApiClientTest "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik9FSTFNVVU0T0RrNU56TTVNekkyTXpaRE9EazFOMFl3UXpkRE1EUXlPRFZDUXpRM1FUSTBNQSJ9.eyJodHRwczovL25kbGEubm8vY2xpZW50X2lkIjogInh4eHl5eSIsICJpc3MiOiAiaHR0cHM6Ly9uZGxhLmV1LmF1dGgwLmNvbS8iLCAic3ViIjogInh4eHl5eUBjbGllbnRzIiwgImF1ZCI6ICJuZGxhX3N5c3RlbSIsICJpYXQiOiAxNTEwMzA1NzczLCAiZXhwIjogMTUxMDM5MjE3MywgInNjb3BlIjogImFydGljbGVzLXRlc3Q6cHVibGlzaCBkcmFmdHMtdGVzdDp3cml0ZSBkcmFmdHMtdGVzdDpzZXRfdG9fcHVibGlzaCBhcnRpY2xlcy10ZXN0OndyaXRlIiwgImd0eSI6ICJjbGllbnQtY3JlZGVudGlhbHMifQ.gsM-U84ykgaxMSbL55w6UYIIQUouPIB6YOmJuj1KhLFnrYctu5vwYBo80zyr1je9kO_6L-rI7SUnrHVao9DFBZJmfFfeojTxIT3CE58hoCdxZQZdPUGePjQzROWRWeDfG96iqhRcepjbVF9pMhKp6FNqEVOxkX00RZg9vFT8iMM" val authHeaderMap: Map[String, String] = Map("Authorization" -> s"Bearer $exampleToken") - class LocalArticleApiTestData extends articleapi.Props with articleapi.TestData { - override val props: ArticleApiProperties = articleApiProperties - val td = new TestData + class LocalArticleApiTestData extends articleapi.Props with articleapi.TestDataT { + override lazy val props: ArticleApiProperties = articleApiProperties + val td = new TestDataClass def setupArticles(): Try[Boolean] = (1L to 10) diff --git a/integration-tests/src/test/scala/no/ndla/integrationtests/searchapi/draftapi/DraftApiClientTest.scala b/integration-tests/src/test/scala/no/ndla/integrationtests/searchapi/draftapi/DraftApiClientTest.scala index a18607121f..69048699b0 100644 --- a/integration-tests/src/test/scala/no/ndla/integrationtests/searchapi/draftapi/DraftApiClientTest.scala +++ b/integration-tests/src/test/scala/no/ndla/integrationtests/searchapi/draftapi/DraftApiClientTest.scala @@ -32,9 +32,9 @@ class DraftApiClientTest with UnitSuite with searchapi.TestEnvironment with HasDatabaseProps { - override val ndlaClient = new NdlaClient - override val searchConverterService = new SearchConverterService - override val DBUtil = new DBUtility + override lazy val ndlaClient = new NdlaClient + override lazy val searchConverterService = new SearchConverterService + override lazy val DBUtil = new DBUtility val draftApiPort: Int = findFreePort val pgc: PostgreSQLContainer[?] = postgresContainer.get diff --git a/integration-tests/src/test/scala/no/ndla/integrationtests/searchapi/learningpathapi/LearningpathApiClientTest.scala b/integration-tests/src/test/scala/no/ndla/integrationtests/searchapi/learningpathapi/LearningpathApiClientTest.scala index 0e6e06af03..9e359bab00 100644 --- a/integration-tests/src/test/scala/no/ndla/integrationtests/searchapi/learningpathapi/LearningpathApiClientTest.scala +++ b/integration-tests/src/test/scala/no/ndla/integrationtests/searchapi/learningpathapi/LearningpathApiClientTest.scala @@ -32,9 +32,9 @@ class LearningpathApiClientTest with UnitSuite with searchapi.TestEnvironment with HasDatabaseProps { - override val ndlaClient = new NdlaClient - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val ndlaClient = new NdlaClient + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService val learningpathApiPort: Int = findFreePort val pgc: PostgreSQLContainer[?] = postgresContainer.get diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/ComponentRegistry.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/ComponentRegistry.scala index 7c01e1dec8..8a25839af0 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/ComponentRegistry.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/ComponentRegistry.scala @@ -82,8 +82,8 @@ class ComponentRegistry(properties: LearningpathApiProperties) with ErrorHandling with SwaggerDocControllerConfig with SearchLanguage { - override val props: LearningpathApiProperties = properties - override val migrator: DBMigrator = DBMigrator( + override lazy val props: LearningpathApiProperties = properties + override lazy val migrator: DBMigrator = DBMigrator( new V11__CreatedByNdlaStatusForOwnersWithRoles, new V13__StoreNDLAStepsAsIframeTypes, new V14__ConvertLanguageUnknown, @@ -92,32 +92,32 @@ class ComponentRegistry(properties: LearningpathApiProperties) new V33__AiDefaultEnabledOrgs ) override lazy val dataSource: HikariDataSource = DataSource.getHikariDataSource - override val DBUtil: DBUtility = new DBUtility + override lazy val DBUtil: DBUtility = new DBUtility - lazy val learningPathRepository = new LearningPathRepository - lazy val readService = new ReadService - lazy val updateService = new UpdateService - lazy val searchConverterService = new SearchConverterService - lazy val searchService = new SearchService - lazy val searchIndexService = new SearchIndexService - lazy val converterService = new ConverterService - lazy val clock = new SystemClock - lazy val uuidUtil = new UUIDUtil - lazy val taxonomyApiClient = new TaxonomyApiClient - lazy val ndlaClient = new NdlaClient - lazy val languageValidator = new LanguageValidator - lazy val titleValidator = new TitleValidator - lazy val learningPathValidator = new LearningPathValidator - lazy val learningStepValidator = new LearningStepValidator - var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer) - lazy val searchApiClient = new SearchApiClient - lazy val oembedProxyClient = new OembedProxyClient - lazy val myndlaApiClient = new MyNDLAApiClient + override lazy val learningPathRepository = new LearningPathRepository + override lazy val readService = new ReadService + override lazy val updateService = new UpdateService + override lazy val searchConverterService = new SearchConverterService + override lazy val searchService = new SearchService + override lazy val searchIndexService = new SearchIndexService + override lazy val converterService = new ConverterService + override lazy val clock = new SystemClock + override lazy val uuidUtil = new UUIDUtil + override lazy val taxonomyApiClient = new TaxonomyApiClient + override lazy val ndlaClient = new NdlaClient + override lazy val languageValidator = new LanguageValidator + override lazy val titleValidator = new TitleValidator + override lazy val learningPathValidator = new LearningPathValidator + override lazy val learningStepValidator = new LearningStepValidator + var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer) + override lazy val searchApiClient = new SearchApiClient + override lazy val oembedProxyClient = new OembedProxyClient + override lazy val myndlaApiClient = new MyNDLAApiClient - lazy val learningpathControllerV2 = new LearningpathControllerV2 - lazy val internController = new InternController - lazy val statsController = new StatsController - lazy val healthController: TapirHealthController = new TapirHealthController + override lazy val learningpathControllerV2 = new LearningpathControllerV2 + override lazy val internController = new InternController + override lazy val statsController = new StatsController + override lazy val healthController: TapirHealthController = new TapirHealthController val swagger = new SwaggerController( List[TapirController]( diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/LearningpathApiProperties.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/LearningpathApiProperties.scala index d9efe7791f..3068417c39 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/LearningpathApiProperties.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/LearningpathApiProperties.scala @@ -16,7 +16,7 @@ import no.ndla.network.{AuthUser, Domains} import scala.util.Properties.* trait Props extends HasBaseProps with HasDatabaseProps { - val props: LearningpathApiProperties + lazy val props: LearningpathApiProperties } class LearningpathApiProperties extends BaseProps with DatabaseProps with StrictLogging { diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/controller/InternController.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/controller/InternController.scala index 3e212624c8..785f4c4ed3 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/controller/InternController.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/controller/InternController.scala @@ -29,7 +29,7 @@ import scala.util.{Failure, Success} trait InternController { this: SearchIndexService & SearchService & LearningPathRepositoryComponent & ReadService & UpdateService & Props & ErrorHandling & TapirController => - val internController: InternController + lazy val internController: InternController class InternController extends TapirController { override val prefix: EndpointInput[Unit] = "intern" diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/controller/LearningpathControllerV2.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/controller/LearningpathControllerV2.scala index c536294a82..af13c96736 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/controller/LearningpathControllerV2.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/controller/LearningpathControllerV2.scala @@ -38,17 +38,11 @@ trait LearningpathControllerV2 { this: ReadService & UpdateService & SearchService & LanguageValidator & ConverterService & TaxonomyApiClient & SearchConverterServiceComponent & Props & ErrorHandling & TapirController => - val learningpathControllerV2: LearningpathControllerV2 + lazy val learningpathControllerV2: LearningpathControllerV2 class LearningpathControllerV2 extends TapirController { import ErrorHelpers.* - import props.{ - DefaultLanguage, - ElasticSearchIndexMaxResultWindow, - ElasticSearchScrollKeepAlive, - InitialScrollContextKeywords - } override val serviceName: String = "learningpaths" override val prefix: EndpointInput[Unit] = "learningpath-api" / "v2" / serviceName @@ -94,11 +88,11 @@ trait LearningpathControllerV2 { private val learningPathStatus = path[String]("STATUS").description("Status of LearningPaths") private val scrollId = query[Option[String]]("search-context") .description( - s"""A unique string obtained from a search you want to keep scrolling in. To obtain one from a search, provide one of the following values: ${InitialScrollContextKeywords + s"""A unique string obtained from a search you want to keep scrolling in. To obtain one from a search, provide one of the following values: ${props.InitialScrollContextKeywords .mkString("[", ",", "]")}. |When scrolling, the parameters from the initial search is used, except in the case of '${this.language.name}' and '${this.fallback.name}'. - |This value may change between scrolls. Always use the one in the latest scroll result (The context, if unused, dies after $ElasticSearchScrollKeepAlive). - |If you are not paginating past $ElasticSearchIndexMaxResultWindow hits, you can ignore this and use '${this.pageNo.name}' and '${this.pageSize.name}' instead. + |This value may change between scrolls. Always use the one in the latest scroll result (The context, if unused, dies after ${props.ElasticSearchScrollKeepAlive}). + |If you are not paginating past ${props.ElasticSearchIndexMaxResultWindow} hits, you can ignore this and use '${this.pageNo.name}' and '${this.pageSize.name}' instead. |""".stripMargin ) private val verificationStatus = query[Option[String]]("verificationStatus") @@ -120,7 +114,7 @@ trait LearningpathControllerV2 { orFunction: => Try[(SearchResultV2DTO, DynamicHeaders)] ): Try[(SearchResultV2DTO, DynamicHeaders)] = scrollId match { - case Some(scroll) if !InitialScrollContextKeywords.contains(scroll) => + case Some(scroll) if !props.InitialScrollContextKeywords.contains(scroll) => searchService.scroll(scroll, language.code) match { case Success(scrollResult) => val body = searchConverterService.asApiSearchResult(scrollResult) @@ -236,7 +230,7 @@ trait LearningpathControllerV2 { .serverLogicPure { case (query, tag, idList, language, pageNo, pageSize, sortStr, fallback, scrollId, verificationStatus) => scrollSearchOr(scrollId, language) { - val shouldScroll = scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = scrollId.exists(props.InitialScrollContextKeywords.contains) search( query, language.code, @@ -263,7 +257,7 @@ trait LearningpathControllerV2 { .serverLogicPure { searchParams => val language = searchParams.language.getOrElse(LanguageCode(AllLanguages)) scrollSearchOr(searchParams.scrollId, language) { - val shouldScroll = searchParams.scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = searchParams.scrollId.exists(props.InitialScrollContextKeywords.contains) search( query = searchParams.query, searchLanguage = language.code, @@ -402,7 +396,7 @@ trait LearningpathControllerV2 { .learningStepStatusForV2( pathId, stepId, - DefaultLanguage, + props.DefaultLanguage, fallback, user ) @@ -417,7 +411,7 @@ trait LearningpathControllerV2 { .out(jsonBody[List[LearningPathV2DTO]]) .errorOut(errorOutputsFor(401, 403, 404)) .withRequiredMyNDLAUserOrTokenUser - .serverLogicPure { user => _ => readService.withOwnerV2(user, DefaultLanguage, true).asRight } + .serverLogicPure { user => _ => readService.withOwnerV2(user, props.DefaultLanguage, true).asRight } def getLicenses: ServerEndpoint[Any, Eff] = endpoint.get .summary("Show all valid licenses") @@ -617,7 +611,7 @@ trait LearningpathControllerV2 { pathId, pathStatus, user, - DefaultLanguage, + props.DefaultLanguage, updateLearningPathStatus.message ) .map { learningPath => @@ -649,7 +643,7 @@ trait LearningpathControllerV2 { pathId, learningpath.LearningPathStatus.DELETED, user, - DefaultLanguage + props.DefaultLanguage ) match { case Failure(ex) => returnLeftError(ex) case Success(_) => diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/controller/StatsController.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/controller/StatsController.scala index 91ee7c25d2..3129b17144 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/controller/StatsController.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/controller/StatsController.scala @@ -19,7 +19,7 @@ import sttp.tapir.server.ServerEndpoint trait StatsController { this: ReadService & Props & ErrorHandling & TapirController => - val statsController: StatsController + lazy val statsController: StatsController class StatsController extends TapirController { override val serviceName: String = "stats" diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/integration/OembedProxyClient.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/integration/OembedProxyClient.scala index 550b2d3b7e..9e2a826da8 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/integration/OembedProxyClient.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/integration/OembedProxyClient.scala @@ -28,12 +28,11 @@ object OembedResponse { trait OembedProxyClient { this: NdlaClient & Props => - val oembedProxyClient: OembedProxyClient + lazy val oembedProxyClient: OembedProxyClient class OembedProxyClient extends StrictLogging { - import props.ApiGatewayHost private val OembedProxyTimeout = 90.seconds - private val OembedProxyBaseUrl = s"http://$ApiGatewayHost/oembed-proxy/v1" + private val OembedProxyBaseUrl = s"http://${props.ApiGatewayHost}/oembed-proxy/v1" def getIframeUrl(url: String): Try[String] = { getOembed(url) match { diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/integration/SearchApiClient.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/integration/SearchApiClient.scala index b162c0238c..0c6849e995 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/integration/SearchApiClient.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/integration/SearchApiClient.scala @@ -26,17 +26,16 @@ import scala.util.{Failure, Success, Try} trait SearchApiClient { this: NdlaClient & Props => - val searchApiClient: SearchApiClient + lazy val searchApiClient: SearchApiClient class SearchApiClient extends StrictLogging { - import props.SearchApiHost private val IndexTimeout = 90.seconds @unused - private val SearchApiBaseUrl = s"http://$SearchApiHost" + private val SearchApiBaseUrl = s"http://${props.SearchApiHost}" def deleteLearningPathDocument(id: Long, user: Option[TokenUser]): Try[?] = { val req = quickRequest - .delete(uri"http://$SearchApiHost/intern/learningpath/$id") + .delete(uri"http://${props.SearchApiHost}/intern/learningpath/$id") .readTimeout(IndexTimeout) doRawRequest(req, user) @@ -49,7 +48,7 @@ trait SearchApiClient { val body = CirceUtil.toJsonString(document) val req = quickRequest - .post(uri"http://$SearchApiHost/intern/learningpath/") + .post(uri"http://${props.SearchApiHost}/intern/learningpath/") .header("Content-Type", "application/json", replaceExisting = true) .body(body) .readTimeout(IndexTimeout) diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/integration/TaxonomyApiClient.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/integration/TaxonomyApiClient.scala index 2ebbd2a165..cdc7cb1b81 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/integration/TaxonomyApiClient.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/integration/TaxonomyApiClient.scala @@ -30,12 +30,11 @@ import scala.util.{Failure, Success, Try} trait TaxonomyApiClient { this: NdlaClient & Props => - val taxonomyApiClient: TaxonomyApiClient + lazy val taxonomyApiClient: TaxonomyApiClient class TaxonomyApiClient extends StrictLogging { - import props.{TaxonomyUrl, DefaultLanguage} private val taxonomyTimeout = 20.seconds - private val TaxonomyApiEndpoint = s"$TaxonomyUrl/v1" + private val TaxonomyApiEndpoint = s"${props.TaxonomyUrl}/v1" private val LearningPathResourceTypeId = "urn:resourcetype:learningPath" def updateTaxonomyForLearningPath( @@ -49,7 +48,7 @@ trait TaxonomyApiClient { case Some(learningPathId) => val contentUri = s"urn:learningpath:$learningPathId" - Language.findByLanguageOrBestEffort(learningPath.title, DefaultLanguage) match { + Language.findByLanguageOrBestEffort(learningPath.title, props.DefaultLanguage) match { case None => Failure(TaxonomyUpdateException("Can't update taxonomy resource when learningpath is missing titles.")) case Some(mainTitle) => diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/model/api/LearningPathDomainDumpDTO.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/model/api/LearningPathDomainDumpDTO.scala index 9f4cef23d2..02860a3d8f 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/model/api/LearningPathDomainDumpDTO.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/model/api/LearningPathDomainDumpDTO.scala @@ -11,6 +11,7 @@ package no.ndla.learningpathapi.model.api import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} import no.ndla.common.model.domain.learningpath.{LearningPath, LearningStep} +import sttp.tapir.Schema import sttp.tapir.Schema.annotations.description @description("Information about learningpaths") @@ -24,6 +25,8 @@ case class LearningPathDomainDumpDTO( object LearningPathDomainDumpDTO { implicit val encoder: Encoder[LearningPathDomainDumpDTO] = deriveEncoder implicit val decoder: Decoder[LearningPathDomainDumpDTO] = deriveDecoder + import sttp.tapir.generic.auto.* + implicit def schema: Schema[LearningPathDomainDumpDTO] = Schema.derivedSchema } @description("Information about learningsteps") diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/repository/LearningPathRepositoryComponent.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/repository/LearningPathRepositoryComponent.scala index 9611664605..ae7e139f97 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/repository/LearningPathRepositoryComponent.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/repository/LearningPathRepositoryComponent.scala @@ -29,7 +29,7 @@ import scala.util.Try trait LearningPathRepositoryComponent extends StrictLogging { this: DataSource & Props => - val learningPathRepository: LearningPathRepository + lazy val learningPathRepository: LearningPathRepository def inTransaction[A](work: DBSession => A)(implicit session: DBSession = null): A = { Option(session) match { diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/ConverterService.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/ConverterService.scala index 595809fd78..d21c26492a 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/ConverterService.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/ConverterService.scala @@ -12,7 +12,7 @@ import cats.implicits.* import io.lemonlabs.uri.typesafe.dsl.* import no.ndla.common.converter.CommonConverter import no.ndla.common.errors.{AccessDeniedException, NotFoundException} -import no.ndla.common.implicits.OptionImplicit +import no.ndla.common.implicits.* import no.ndla.common.model.api.{Delete, Missing, ResponsibleDTO, UpdateOrDelete, UpdateWith} import no.ndla.common.model.domain.{ContributorType, Responsible, learningpath} import no.ndla.common.model.domain.learningpath.{ @@ -48,11 +48,9 @@ trait ConverterService { this: LearningPathRepositoryComponent & LanguageValidator & LearningPathValidator & OembedProxyClient & Clock & CommonConverter & Props => - val converterService: ConverterService + lazy val converterService: ConverterService class ConverterService { - import props.* - def asEmbedUrlV2(embedUrl: api.EmbedUrlV2DTO, language: String): EmbedUrl = { learningpath.EmbedUrl(embedUrl.url, language, EmbedType.valueOfOrError(embedUrl.embedType)) } @@ -748,7 +746,7 @@ trait ConverterService { ) hostOpt match { - case Some(host) if NdlaFrontendHostNames.contains(host.toString) => + case Some(host) if props.NdlaFrontendHostNames.contains(host.toString) => oembedProxyClient .getIframeUrl(embedUrl.url) .map(newUrl => { @@ -775,25 +773,25 @@ trait ConverterService { } def createUrlToLearningPath(lp: LearningPath): String = { - s"$Domain$LearningpathControllerPath${lp.id.get}" + s"${props.Domain}${props.LearningpathControllerPath}${lp.id.get}" } def createUrlToLearningPath(lp: api.LearningPathV2DTO): String = { - s"$Domain$LearningpathControllerPath${lp.id}" + s"${props.Domain}${props.LearningpathControllerPath}${lp.id}" } private def createUrlToImageApi(imageId: String): String = { - s"${ExternalApiUrls.ImageApiUrl}/$imageId" + s"${props.ExternalApiUrls.ImageApiUrl}/$imageId" } private def createUrlToImageApiRaw(imageId: String): String = { - s"${ExternalApiUrls.ImageApiRawUrl}/id/$imageId" + s"${props.ExternalApiUrls.ImageApiRawUrl}/id/$imageId" } private def createEmbedUrl(embedUrlOrPath: EmbedUrlV2DTO): EmbedUrlV2DTO = { embedUrlOrPath.url.hostOption match { case Some(_) => embedUrlOrPath case None => - embedUrlOrPath.copy(url = s"$NdlaFrontendProtocol://$NdlaFrontendHost${embedUrlOrPath.url}") + embedUrlOrPath.copy(url = s"${props.NdlaFrontendProtocol}://${props.NdlaFrontendHost}${embedUrlOrPath.url}") } } diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/ReadService.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/ReadService.scala index 3acb10c3b0..d61bf2be54 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/ReadService.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/ReadService.scala @@ -27,7 +27,7 @@ import scala.util.{Failure, Success, Try} trait ReadService { this: LearningPathRepositoryComponent & ConverterService & Clock & MyNDLAApiClient => - val readService: ReadService + lazy val readService: ReadService class ReadService { diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/UpdateService.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/UpdateService.scala index d7285e02fe..900f0bf899 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/UpdateService.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/UpdateService.scala @@ -27,14 +27,14 @@ import no.ndla.network.model.{CombinedUser, CombinedUserRequired} import no.ndla.network.tapir.auth.TokenUser import scala.annotation.tailrec -import scala.util.{Failure, Success, Try} +import scala.util.{Failure, Success, Try, boundary} import no.ndla.language.Language import no.ndla.database.DBUtility trait UpdateService { this: LearningPathRepositoryComponent & ReadService & ConverterService & SearchIndexService & Clock & LearningStepValidator & LearningPathValidator & TaxonomyApiClient & SearchApiClient & DBUtility & Props => - val updateService: UpdateService + lazy val updateService: UpdateService class UpdateService { @@ -286,49 +286,51 @@ trait UpdateService { learningStepToUpdate: UpdatedLearningStepV2DTO, owner: CombinedUserRequired ): Try[LearningStepV2DTO] = writeDuringWriteRestrictionOrAccessDenied(owner) { - withId(learningPathId).flatMap(_.canEditLearningpath(owner)) match { - case Failure(ex) => Failure(ex) - case Success(learningPath) => - learningPathRepository.learningStepWithId(learningPathId, learningStepId) match { - case None => - Failure( - NotFoundException( - s"Could not find learningstep with id '$learningStepId' to update with learningpath id '$learningPathId'." + permitTry { + withId(learningPathId).flatMap(_.canEditLearningpath(owner)) match { + case Failure(ex) => Failure(ex) + case Success(learningPath) => + learningPathRepository.learningStepWithId(learningPathId, learningStepId) match { + case None => + Failure( + NotFoundException( + s"Could not find learningstep with id '$learningStepId' to update with learningpath id '$learningPathId'." + ) ) - ) - case Some(existing) => - val validated = for { - toUpdate <- converterService.mergeLearningSteps(existing, learningStepToUpdate) - validated <- learningStepValidator.validate(toUpdate, allowUnknownLanguage = true) - } yield validated - - validated match { - case Failure(ex) => Failure(ex) - case Success(toUpdate) => - learningStepValidator.validate(toUpdate, allowUnknownLanguage = true).?? - - val (updatedStep, updatedPath) = inTransaction { implicit session => - val updatedStep = - learningPathRepository.updateLearningStep(toUpdate) - val pathToUpdate = converterService.insertLearningStep(learningPath, updatedStep, owner) - val updatedPath = learningPathRepository.update(pathToUpdate) - - (updatedStep, updatedPath) - } - - updateSearchAndTaxonomy(updatedPath, owner.tokenUser) - .flatMap(_ => - converterService.asApiLearningStepV2( - updatedStep, - updatedPath, - learningStepToUpdate.language, - fallback = true, - owner + case Some(existing) => + val validated = for { + toUpdate <- converterService.mergeLearningSteps(existing, learningStepToUpdate) + validated <- learningStepValidator.validate(toUpdate, allowUnknownLanguage = true) + } yield validated + + validated match { + case Failure(ex) => Failure(ex) + case Success(toUpdate) => + learningStepValidator.validate(toUpdate, allowUnknownLanguage = true).?? + + val (updatedStep, updatedPath) = inTransaction { implicit session => + val updatedStep = + learningPathRepository.updateLearningStep(toUpdate) + val pathToUpdate = converterService.insertLearningStep(learningPath, updatedStep, owner) + val updatedPath = learningPathRepository.update(pathToUpdate) + + (updatedStep, updatedPath) + } + + updateSearchAndTaxonomy(updatedPath, owner.tokenUser) + .flatMap(_ => + converterService.asApiLearningStepV2( + updatedStep, + updatedPath, + learningStepToUpdate.language, + fallback = true, + owner + ) ) - ) - } + } - } + } + } } } @@ -367,33 +369,35 @@ trait UpdateService { owner: CombinedUserRequired ): Try[LearningStepV2DTO] = writeDuringWriteRestrictionOrAccessDenied(owner) { - withId(learningPathId).flatMap(_.canEditLearningpath(owner)) match { - case Failure(ex) => Failure(ex) - case Success(learningPath) => - val stepsToChange = learningPathRepository.learningStepsFor(learningPathId) - val stepToUpdate = stepsToChange.find(_.id.contains(learningStepId)) match { - case Some(ls) if ls.status == DELETED && newStatus == DELETED => - val msg = s"Learningstep with id $learningStepId for learningpath with id $learningPathId not found" - return Failure(NotFoundException(msg)) - case None => - val msg = s"Learningstep with id $learningStepId for learningpath with id $learningPathId not found" - return Failure(NotFoundException(msg)) - case Some(ls) => ls - } + boundary { - val (updatedPath, updatedStep) = - updateWithStepSeqNo(learningStepId, newStatus, learningPath, stepToUpdate, stepsToChange, owner) + withId(learningPathId).flatMap(_.canEditLearningpath(owner)) match { + case Failure(ex) => Failure(ex) + case Success(learningPath) => + val stepsToChange = learningPathRepository.learningStepsFor(learningPathId) + val stepToUpdate = stepsToChange.find(_.id.contains(learningStepId)) match { + case Some(ls) if ls.status == DELETED && newStatus == DELETED => + val msg = s"Learningstep with id $learningStepId for learningpath with id $learningPathId not found" + boundary.break(Failure(NotFoundException(msg))) + case None => + val msg = s"Learningstep with id $learningStepId for learningpath with id $learningPathId not found" + boundary.break(Failure(NotFoundException(msg))) + case Some(ls) => ls + } - updateSearchAndTaxonomy(updatedPath, owner.tokenUser).flatMap(_ => - converterService.asApiLearningStepV2( - updatedStep, - updatedPath, - props.DefaultLanguage, - fallback = true, - owner - ) - ) + val (updatedPath, updatedStep) = + updateWithStepSeqNo(learningStepId, newStatus, learningPath, stepToUpdate, stepsToChange, owner) + updateSearchAndTaxonomy(updatedPath, owner.tokenUser).flatMap(_ => + converterService.asApiLearningStepV2( + updatedStep, + updatedPath, + props.DefaultLanguage, + fallback = true, + owner + ) + ) + } } } @@ -425,7 +429,7 @@ trait UpdateService { def addOrSubtract(seqNo: Int): Int = if (from > to) seqNo + 1 else seqNo - 1 inTransaction { implicit session => - learningPathRepository.updateLearningStep(learningStep.copy(seqNo = seqNo)): Unit + val _ = learningPathRepository.updateLearningStep(learningStep.copy(seqNo = seqNo)) toUpdate.foreach(step => { learningPathRepository.updateLearningStep(step.copy(seqNo = addOrSubtract(step.seqNo))) }) diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/search/SearchConverterServiceComponent.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/search/SearchConverterServiceComponent.scala index ee49a3ba8f..e2a0fa04b4 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/search/SearchConverterServiceComponent.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/search/SearchConverterServiceComponent.scala @@ -24,11 +24,9 @@ import no.ndla.search.model.{LanguageValue, SearchableLanguageList, SearchableLa trait SearchConverterServiceComponent { this: ConverterService & Props & SearchLanguage => - val searchConverterService: SearchConverterService + lazy val searchConverterService: SearchConverterService class SearchConverterService { - import props.DefaultLanguage - def asApiLearningPathSummaryV2( searchableLearningPath: SearchableLearningPath, language: String @@ -48,11 +46,11 @@ trait SearchConverterServiceComponent { searchableLearningPath.id, revision = None, findByLanguageOrBestEffort(titles, language) - .getOrElse(api.TitleDTO("", DefaultLanguage)), + .getOrElse(api.TitleDTO("", props.DefaultLanguage)), findByLanguageOrBestEffort(descriptions, language) - .getOrElse(api.DescriptionDTO("", DefaultLanguage)), + .getOrElse(api.DescriptionDTO("", props.DefaultLanguage)), findByLanguageOrBestEffort(introductions, language) - .getOrElse(api.IntroductionDTO("", DefaultLanguage)), + .getOrElse(api.IntroductionDTO("", props.DefaultLanguage)), createUrlToLearningPath(searchableLearningPath.id), searchableLearningPath.coverPhotoUrl, searchableLearningPath.duration, @@ -60,7 +58,7 @@ trait SearchConverterServiceComponent { searchableLearningPath.created, searchableLearningPath.lastUpdated, findByLanguageOrBestEffort(tags, language) - .getOrElse(api.LearningPathTagsDTO(Seq(), DefaultLanguage)), + .getOrElse(api.LearningPathTagsDTO(Seq(), props.DefaultLanguage)), searchableLearningPath.copyright, supportedLanguages, searchableLearningPath.isBasedOn, diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/search/SearchIndexService.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/search/SearchIndexService.scala index 104f1eb17c..c7d0bde1fd 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/search/SearchIndexService.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/search/SearchIndexService.scala @@ -28,13 +28,12 @@ import no.ndla.search.model.domain.{BulkIndexResult, ElasticIndexingException, R trait SearchIndexService { this: Elastic4sClient & SearchConverterServiceComponent & LearningPathRepositoryComponent & SearchApiClient & BaseIndexService & Props & SearchLanguage => - val searchIndexService: SearchIndexService + lazy val searchIndexService: SearchIndexService class SearchIndexService extends BaseIndexService with StrictLogging { - import props.{SearchDocument, SearchIndex, ElasticSearchIndexMaxResultWindow} - override val documentType: String = SearchDocument - override val searchIndex: String = SearchIndex - override val MaxResultWindowOption: Int = ElasticSearchIndexMaxResultWindow + override val documentType: String = props.SearchDocument + override val searchIndex: String = props.SearchIndex + override val MaxResultWindowOption: Int = props.ElasticSearchIndexMaxResultWindow def indexDocuments: Try[ReindexResult] = indexDocuments(None) def indexDocuments(numShards: Option[Int]): Try[ReindexResult] = synchronized { diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/search/SearchService.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/search/SearchService.scala index fae2668024..5a451f63f1 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/search/SearchService.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/service/search/SearchService.scala @@ -33,16 +33,15 @@ import scala.util.{Failure, Success, Try} import no.ndla.learningpathapi.integration.TaxonomyApiClient trait SearchService extends StrictLogging { - this: SearchIndexService & Elastic4sClient & SearchConverterServiceComponent & TaxonomyApiClient & Props & ErrorHandling => - val searchService: SearchService + this: SearchIndexService & Elastic4sClient & SearchConverterServiceComponent & TaxonomyApiClient & Props & + ErrorHandling => + lazy val searchService: SearchService class SearchService { - import props.{DefaultLanguage, ElasticSearchIndexMaxResultWindow, ElasticSearchScrollKeepAlive} - def scroll(scrollId: String, language: String): Try[SearchResult] = e4sClient .execute { - searchScroll(scrollId, ElasticSearchScrollKeepAlive) + searchScroll(scrollId, props.ElasticSearchScrollKeepAlive) } .map(response => { val hits = getHitsV2(response.result, language) @@ -194,9 +193,9 @@ trait SearchService extends StrictLogging { val (startAt, numResults) = getStartAtAndNumResults(settings.page, settings.pageSize) val requestedResultWindow = settings.page.getOrElse(1) * numResults - if (requestedResultWindow > ElasticSearchIndexMaxResultWindow) { + if (requestedResultWindow > props.ElasticSearchIndexMaxResultWindow) { logger.info( - s"Max supported results are $ElasticSearchIndexMaxResultWindow, user requested $requestedResultWindow" + s"Max supported results are ${props.ElasticSearchIndexMaxResultWindow}, user requested $requestedResultWindow" ) Failure(LearningpathHelpers.ResultWindowTooLargeException()) } else { @@ -211,7 +210,7 @@ trait SearchService extends StrictLogging { // Only add scroll param if it is first page val searchWithScroll = if (startAt == 0 && settings.shouldScroll) { - searchToExecute.scroll(ElasticSearchScrollKeepAlive) + searchToExecute.scroll(props.ElasticSearchScrollKeepAlive) } else { searchToExecute.explain(true) } e4sClient.execute(searchWithScroll) match { @@ -263,7 +262,7 @@ trait SearchService extends StrictLogging { private def getSortDefinition(sort: Sort, language: String) = { val sortLanguage = language match { - case NoLanguage => DefaultLanguage + case NoLanguage => props.DefaultLanguage case _ => language } diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/LanguageValidator.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/LanguageValidator.scala index e8f6029663..63286df837 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/LanguageValidator.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/LanguageValidator.scala @@ -12,7 +12,7 @@ import no.ndla.common.errors.ValidationMessage import no.ndla.mapping.ISO639.get6391CodeFor6392CodeMappings trait LanguageValidator { - val languageValidator: LanguageValidator + lazy val languageValidator: LanguageValidator class LanguageValidator { private def languageCodeSupported6391(languageCode: String, allowUnknownLanguage: Boolean): Boolean = { diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/LearningPathValidator.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/LearningPathValidator.scala index 1ab1454536..4c0de26bf1 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/LearningPathValidator.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/LearningPathValidator.scala @@ -17,7 +17,7 @@ import no.ndla.mapping.License.getLicense trait LearningPathValidator { this: LanguageValidator & TitleValidator & TextValidator => - val learningPathValidator: LearningPathValidator + lazy val learningPathValidator: LearningPathValidator class LearningPathValidator(descriptionRequired: Boolean = false) { diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/LearningStepValidator.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/LearningStepValidator.scala index df895789a6..35c2dbeb0f 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/LearningStepValidator.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/LearningStepValidator.scala @@ -15,7 +15,7 @@ import scala.util.{Failure, Success, Try} trait LearningStepValidator { this: TitleValidator & LanguageValidator & TextValidator & UrlValidator => - val learningStepValidator: LearningStepValidator + lazy val learningStepValidator: LearningStepValidator class LearningStepValidator { val noHtmlTextValidator = new TextValidator(allowHtml = false) diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/TextValidator.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/TextValidator.scala index 5579d44376..c0db951d80 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/TextValidator.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/TextValidator.scala @@ -17,10 +17,8 @@ trait TextValidator { this: Props => class TextValidator(allowHtml: Boolean) { - import props.* - val IllegalContentInBasicText: String = - s"The content contains illegal html-characters. Allowed characters are ${AllowedHtmlTags.mkString(", ")}" + s"The content contains illegal html-characters. Allowed characters are ${props.AllowedHtmlTags.mkString(", ")}" val IllegalContentInPlainText = "The content contains illegal html-characters. No HTML is allowed." diff --git a/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/TitleValidator.scala b/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/TitleValidator.scala index 4e3d0d3452..82da0eab9a 100644 --- a/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/TitleValidator.scala +++ b/learningpath-api/src/main/scala/no/ndla/learningpathapi/validation/TitleValidator.scala @@ -13,7 +13,7 @@ import no.ndla.common.model.domain.Title trait TitleValidator { this: LanguageValidator & TextValidator => - val titleValidator: TitleValidator + lazy val titleValidator: TitleValidator class TitleValidator(titleRequired: Boolean = true) { private val MISSING_TITLE = "At least one title is required." diff --git a/learningpath-api/src/test/scala/no/ndla/learningpathapi/TestEnvironment.scala b/learningpath-api/src/test/scala/no/ndla/learningpathapi/TestEnvironment.scala index baed973d58..6929764f60 100644 --- a/learningpath-api/src/test/scala/no/ndla/learningpathapi/TestEnvironment.scala +++ b/learningpath-api/src/test/scala/no/ndla/learningpathapi/TestEnvironment.scala @@ -66,36 +66,35 @@ trait TestEnvironment with RedisClient { lazy val props = new LearningpathApiProperties - val migrator: DBMigrator = mock[DBMigrator] - val dataSource: HikariDataSource = mock[HikariDataSource] + override lazy val migrator: DBMigrator = mock[DBMigrator] + override lazy val dataSource: HikariDataSource = mock[HikariDataSource] - val learningPathRepository: LearningPathRepository = mock[LearningPathRepository] - val learningPathRepositoryComponent: LearningPathRepositoryComponent = mock[LearningPathRepositoryComponent] - val readService: ReadService = mock[ReadService] - val updateService: UpdateService = mock[UpdateService] - val searchConverterService: SearchConverterService = mock[SearchConverterService] - val searchService: SearchService = mock[SearchService] - val searchIndexService: SearchIndexService = mock[SearchIndexService] - val converterService: ConverterService = org.mockito.Mockito.spy(new ConverterService) - val clock: SystemClock = mock[SystemClock] - val uuidUtil: UUIDUtil = mock[UUIDUtil] - val taxonomyApiClient: TaxonomyApiClient = mock[TaxonomyApiClient] - val ndlaClient: NdlaClient = mock[NdlaClient] - val languageValidator: LanguageValidator = mock[LanguageValidator] - val learningpathControllerV2: LearningpathControllerV2 = mock[LearningpathControllerV2] - val statsController: StatsController = mock[StatsController] - val internController: InternController = mock[InternController] - val healthController: TapirHealthController = mock[TapirHealthController] - val learningStepValidator: LearningStepValidator = mock[LearningStepValidator] - val learningPathValidator: LearningPathValidator = mock[LearningPathValidator] - val titleValidator: TitleValidator = mock[TitleValidator] + override lazy val learningPathRepository: LearningPathRepository = mock[LearningPathRepository] + override lazy val readService: ReadService = mock[ReadService] + override lazy val updateService: UpdateService = mock[UpdateService] + override lazy val searchConverterService: SearchConverterService = mock[SearchConverterService] + override lazy val searchService: SearchService = mock[SearchService] + override lazy val searchIndexService: SearchIndexService = mock[SearchIndexService] + override lazy val converterService: ConverterService = org.mockito.Mockito.spy(new ConverterService) + override lazy val clock: SystemClock = mock[SystemClock] + override lazy val uuidUtil: UUIDUtil = mock[UUIDUtil] + override lazy val taxonomyApiClient: TaxonomyApiClient = mock[TaxonomyApiClient] + override lazy val ndlaClient: NdlaClient = mock[NdlaClient] + override lazy val languageValidator: LanguageValidator = mock[LanguageValidator] + override lazy val learningpathControllerV2: LearningpathControllerV2 = mock[LearningpathControllerV2] + override lazy val statsController: StatsController = mock[StatsController] + override lazy val internController: InternController = mock[InternController] + override lazy val healthController: TapirHealthController = mock[TapirHealthController] + override lazy val learningStepValidator: LearningStepValidator = mock[LearningStepValidator] + override lazy val learningPathValidator: LearningPathValidator = mock[LearningPathValidator] + override lazy val titleValidator: TitleValidator = mock[TitleValidator] var e4sClient: NdlaE4sClient = mock[NdlaE4sClient] - val searchApiClient: SearchApiClient = mock[SearchApiClient] - val oembedProxyClient: OembedProxyClient = mock[OembedProxyClient] - val feideApiClient: FeideApiClient = mock[FeideApiClient] - val redisClient: RedisClient = mock[RedisClient] - val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] - val DBUtil: DBUtility = mock[DBUtility] + override lazy val searchApiClient: SearchApiClient = mock[SearchApiClient] + override lazy val oembedProxyClient: OembedProxyClient = mock[OembedProxyClient] + override lazy val feideApiClient: FeideApiClient = mock[FeideApiClient] + override lazy val redisClient: RedisClient = mock[RedisClient] + override lazy val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] + override lazy val DBUtil: DBUtility = mock[DBUtility] def services: List[TapirController] = List.empty val swagger: SwaggerController = mock[SwaggerController] diff --git a/learningpath-api/src/test/scala/no/ndla/learningpathapi/repository/LearningPathRepositoryComponentIntegrationTest.scala b/learningpath-api/src/test/scala/no/ndla/learningpathapi/repository/LearningPathRepositoryComponentIntegrationTest.scala index 86cce3199e..c19e10349a 100644 --- a/learningpath-api/src/test/scala/no/ndla/learningpathapi/repository/LearningPathRepositoryComponentIntegrationTest.scala +++ b/learningpath-api/src/test/scala/no/ndla/learningpathapi/repository/LearningPathRepositoryComponentIntegrationTest.scala @@ -41,8 +41,8 @@ class LearningPathRepositoryComponentIntegrationTest with TestEnvironment { override val schemaName = "learningpathapi_test" - override val dataSource: HikariDataSource = testDataSource.get - override val migrator: DBMigrator = DBMigrator() + override lazy val dataSource: HikariDataSource = testDataSource.get + override lazy val migrator: DBMigrator = DBMigrator() var repository: LearningPathRepository = _ diff --git a/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/ConverterServiceTest.scala b/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/ConverterServiceTest.scala index c5baadceea..165b97780a 100644 --- a/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/ConverterServiceTest.scala +++ b/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/ConverterServiceTest.scala @@ -31,7 +31,6 @@ import scala.util.{Failure, Success} import no.ndla.common.model.domain.Priority class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { - import props.DefaultLanguage val clinton: commonApi.AuthorDTO = commonApi.AuthorDTO(ContributorType.Writer, "Crooked Hillary") val license: commonApi.LicenseDTO = commonApi.LicenseDTO( @@ -105,7 +104,7 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { ), articleId = Some(123L) ) - val apiTags: List[api.LearningPathTagsDTO] = List(api.LearningPathTagsDTO(Seq("tag"), DefaultLanguage)) + val apiTags: List[api.LearningPathTagsDTO] = List(api.LearningPathTagsDTO(Seq("tag"), props.DefaultLanguage)) val randomDate: NDLADate = NDLADate.now() var service: ConverterService = _ @@ -115,15 +114,15 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { revision = Some(1), externalId = None, isBasedOn = None, - title = List(Title("tittel", DefaultLanguage)), - description = List(Description("deskripsjon", DefaultLanguage)), + title = List(Title("tittel", props.DefaultLanguage)), + description = List(Description("deskripsjon", props.DefaultLanguage)), coverPhotoId = None, duration = Some(60), status = LearningPathStatus.PRIVATE, verificationStatus = LearningPathVerificationStatus.CREATED_BY_NDLA, created = randomDate, lastUpdated = randomDate, - tags = List(Tag(List("tag"), DefaultLanguage)), + tags = List(Tag(List("tag"), props.DefaultLanguage)), owner = "me", copyright = LearningpathCopyright(CC_BY.toString, List.empty), isMyNDLAOwner = false, @@ -143,8 +142,8 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { 1, 1, None, - api.TitleDTO("tittel", DefaultLanguage), - api.DescriptionDTO("deskripsjon", DefaultLanguage), + api.TitleDTO("tittel", props.DefaultLanguage), + api.DescriptionDTO("deskripsjon", props.DefaultLanguage), "http://api-gateway.ndla-local/learningpath-api/v2/learningpaths/1", List.empty, "http://api-gateway.ndla-local/learningpath-api/v2/learningpaths/1/learningsteps", @@ -154,7 +153,7 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { LearningPathVerificationStatus.CREATED_BY_NDLA.toString, randomDate, randomDate, - api.LearningPathTagsDTO(Seq("tag"), DefaultLanguage), + api.LearningPathTagsDTO(Seq("tag"), props.DefaultLanguage), api.CopyrightDTO( commonApi.LicenseDTO( CC_BY.toString, @@ -176,7 +175,7 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { ) service.asApiLearningpathV2( domainLearningPath.copy(title = domainLearningPath.title :+ Title("test", "en")), - DefaultLanguage, + props.DefaultLanguage, false, TokenUser("me", Set.empty, None).toCombined ) should equal(expected) @@ -199,8 +198,8 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { 1, 1, None, - api.TitleDTO("tittel", DefaultLanguage), - api.DescriptionDTO("deskripsjon", DefaultLanguage), + api.TitleDTO("tittel", props.DefaultLanguage), + api.DescriptionDTO("deskripsjon", props.DefaultLanguage), "http://api-gateway.ndla-local/learningpath-api/v2/learningpaths/1", List.empty, "http://api-gateway.ndla-local/learningpath-api/v2/learningpaths/1/learningsteps", @@ -210,7 +209,7 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { LearningPathVerificationStatus.CREATED_BY_NDLA.toString, randomDate, randomDate, - api.LearningPathTagsDTO(Seq("tag"), DefaultLanguage), + api.LearningPathTagsDTO(Seq("tag"), props.DefaultLanguage), api.CopyrightDTO( commonApi.LicenseDTO( CC_BY.toString, @@ -243,16 +242,16 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { api.LearningPathSummaryV2DTO( 1, Some(1), - api.TitleDTO("tittel", DefaultLanguage), - api.DescriptionDTO("deskripsjon", DefaultLanguage), - api.IntroductionDTO("", DefaultLanguage), + api.TitleDTO("tittel", props.DefaultLanguage), + api.DescriptionDTO("deskripsjon", props.DefaultLanguage), + api.IntroductionDTO("", props.DefaultLanguage), "http://api-gateway.ndla-local/learningpath-api/v2/learningpaths/1", None, Some(60), LearningPathStatus.PRIVATE.toString, randomDate, randomDate, - api.LearningPathTagsDTO(Seq("tag"), DefaultLanguage), + api.LearningPathTagsDTO(Seq("tag"), props.DefaultLanguage), api.CopyrightDTO( commonApi.LicenseDTO( CC_BY.toString, @@ -278,9 +277,9 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { 1, 1, 1, - api.TitleDTO("tittel", DefaultLanguage), + api.TitleDTO("tittel", props.DefaultLanguage), None, - Some(api.DescriptionDTO("deskripsjon", DefaultLanguage)), + Some(api.DescriptionDTO("deskripsjon", props.DefaultLanguage)), None, None, showTitle = false, @@ -289,13 +288,13 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { "http://api-gateway.ndla-local/learningpath-api/v2/learningpaths/1/learningsteps/1", canEdit = true, "ACTIVE", - Seq(DefaultLanguage) + Seq(props.DefaultLanguage) ) ) service.asApiLearningStepV2( domainLearningStep2, domainLearningPath, - DefaultLanguage, + props.DefaultLanguage, false, TokenUser("me", Set.empty, None).toCombined ) should equal(learningstep) @@ -325,9 +324,9 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { 1, 1, 1, - api.TitleDTO("tittel", DefaultLanguage), + api.TitleDTO("tittel", props.DefaultLanguage), None, - Some(api.DescriptionDTO("deskripsjon", DefaultLanguage)), + Some(api.DescriptionDTO("deskripsjon", props.DefaultLanguage)), None, None, showTitle = false, @@ -336,7 +335,7 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { "http://api-gateway.ndla-local/learningpath-api/v2/learningpaths/1/learningsteps/1", canEdit = true, "ACTIVE", - Seq(DefaultLanguage) + Seq(props.DefaultLanguage) ) ) service.asApiLearningStepV2( @@ -353,13 +352,15 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { api.LearningStepSummaryV2DTO( 1, 1, - api.TitleDTO("tittel", DefaultLanguage), + api.TitleDTO("tittel", props.DefaultLanguage), "INTRODUCTION", "http://api-gateway.ndla-local/learningpath-api/v2/learningpaths/1/learningsteps/1" ) ) - service.asApiLearningStepSummaryV2(domainLearningStep2, domainLearningPath, DefaultLanguage) should equal(expected) + service.asApiLearningStepSummaryV2(domainLearningStep2, domainLearningPath, props.DefaultLanguage) should equal( + expected + ) } test("asApiLearningStepSummaryV2 returns what we have when not supported language is given") { @@ -367,7 +368,7 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { api.LearningStepSummaryV2DTO( 1, 1, - api.TitleDTO("tittel", DefaultLanguage), + api.TitleDTO("tittel", props.DefaultLanguage), "INTRODUCTION", "http://api-gateway.ndla-local/learningpath-api/v2/learningpaths/1/learningsteps/1" ) @@ -380,8 +381,8 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { test("asApiLearningPathTagsSummary converts api LearningPathTags to api LearningPathTagsSummary") { val expected = - Some(api.LearningPathTagsSummaryDTO(DefaultLanguage, Seq(DefaultLanguage), Seq("tag"))) - service.asApiLearningPathTagsSummary(apiTags, DefaultLanguage, false) should equal(expected) + Some(api.LearningPathTagsSummaryDTO(props.DefaultLanguage, Seq(props.DefaultLanguage), Seq("tag"))) + service.asApiLearningPathTagsSummary(apiTags, props.DefaultLanguage, false) should equal(expected) } test("asApiLearningPathTagsSummary returns None if fallback is false and language is unsupported") { @@ -392,7 +393,7 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { "asApiLearningPathTagsSummary converts api LearningPathTags to api LearningPathTagsSummary if language is undefined and fallback is true" ) { val expected = - Some(api.LearningPathTagsSummaryDTO(DefaultLanguage, Seq(DefaultLanguage), Seq("tag"))) + Some(api.LearningPathTagsSummaryDTO(props.DefaultLanguage, Seq(props.DefaultLanguage), Seq("tag"))) service.asApiLearningPathTagsSummary(apiTags, "hurr durr I'm a language", true) should equal(expected) } @@ -464,7 +465,7 @@ class ConverterServiceTest extends UnitSuite with UnitTestEnvironment { s"${props.Domain}/image-api/raw/id/1", s"${props.Domain}/image-api/v3/images/1" ) - val Some(result) = service.asCoverPhoto("1") + val Some(result) = service.asCoverPhoto("1"): @unchecked result should equal(expectedResult) } diff --git a/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/ReadServiceTest.scala b/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/ReadServiceTest.scala index db79f17020..7a666462b2 100644 --- a/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/ReadServiceTest.scala +++ b/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/ReadServiceTest.scala @@ -145,7 +145,8 @@ class ReadServiceTest extends UnitSuite with UnitTestEnvironment { test("That withIdV2 returns None when id does not exist") { when(learningPathRepository.withId(eqTo(PUBLISHED_ID))(any[DBSession])).thenReturn(None) - val Failure(ex) = service.withIdV2(PUBLISHED_ID, "nb", fallback = false, TokenUser.PublicUser.toCombined) + val Failure(ex) = + service.withIdV2(PUBLISHED_ID, "nb", fallback = false, TokenUser.PublicUser.toCombined): @unchecked ex.isInstanceOf[NotFoundException] } @@ -170,14 +171,14 @@ class ReadServiceTest extends UnitSuite with UnitTestEnvironment { test("That withId throws an AccessDeniedException when the status is PRIVATE and no user") { when(learningPathRepository.withId(eqTo(PRIVATE_ID))(any[DBSession])) .thenReturn(Some(PRIVATE_LEARNINGPATH)) - val Failure(ex) = service.withIdV2(PRIVATE_ID, "nb", fallback = false, TokenUser.PublicUser.toCombined) + val Failure(ex) = service.withIdV2(PRIVATE_ID, "nb", fallback = false, TokenUser.PublicUser.toCombined): @unchecked ex should be(AccessDeniedException("You do not have access to the requested resource.")) } test("That withId throws an AccessDeniedException when the status is PRIVATE and user is not the owner") { when(learningPathRepository.withId(eqTo(PRIVATE_ID))(any[DBSession])) .thenReturn(Some(PRIVATE_LEARNINGPATH)) - val Failure(ex) = service.withIdV2(PRIVATE_ID, "nb", fallback = false, PUBLISHED_OWNER.toCombined) + val Failure(ex) = service.withIdV2(PRIVATE_ID, "nb", fallback = false, PUBLISHED_OWNER.toCombined): @unchecked ex should be(AccessDeniedException("You do not have access to the requested resource.")) } @@ -192,7 +193,7 @@ class ReadServiceTest extends UnitSuite with UnitTestEnvironment { test("That statusFor returns None when id does not exist") { when(learningPathRepository.withId(eqTo(PUBLISHED_ID))(any[DBSession])).thenReturn(None) - val Failure(ex) = service.statusFor(PUBLISHED_ID, TokenUser.PublicUser.toCombined) + val Failure(ex) = service.statusFor(PUBLISHED_ID, TokenUser.PublicUser.toCombined): @unchecked ex.isInstanceOf[NotFoundException] } @@ -208,14 +209,14 @@ class ReadServiceTest extends UnitSuite with UnitTestEnvironment { when(learningPathRepository.withId(eqTo(PRIVATE_ID))(any[DBSession])) .thenReturn(Some(PRIVATE_LEARNINGPATH)) - val Failure(ex) = service.statusFor(2, TokenUser.PublicUser.toCombined) + val Failure(ex) = service.statusFor(2, TokenUser.PublicUser.toCombined): @unchecked ex should be(AccessDeniedException("You do not have access to the requested resource.")) } test("That statusFor throws an AccessDeniedException when the status is PRIVATE and user is not the owner") { when(learningPathRepository.withId(eqTo(PRIVATE_ID))(any[DBSession])) .thenReturn(Some(PRIVATE_LEARNINGPATH)) - val Failure(ex) = service.statusFor(2, PUBLISHED_OWNER.toCombined) + val Failure(ex) = service.statusFor(2, PUBLISHED_OWNER.toCombined): @unchecked ex should be(AccessDeniedException("You do not have access to the requested resource.")) } @@ -235,7 +236,7 @@ class ReadServiceTest extends UnitSuite with UnitTestEnvironment { "nb", fallback = false, TokenUser.PublicUser.toCombined - ) + ): @unchecked ex.isInstanceOf[NotFoundException] } @@ -250,7 +251,7 @@ class ReadServiceTest extends UnitSuite with UnitTestEnvironment { "nb", fallback = false, TokenUser.PublicUser.toCombined - ) + ): @unchecked ex.isInstanceOf[NotFoundException] } @@ -299,7 +300,7 @@ class ReadServiceTest extends UnitSuite with UnitTestEnvironment { "nb", fallback = false, TokenUser.PublicUser.toCombined - ) + ): @unchecked ex should be(AccessDeniedException("You do not have access to the requested resource.")) } @@ -313,7 +314,7 @@ class ReadServiceTest extends UnitSuite with UnitTestEnvironment { "nb", fallback = false, PUBLISHED_OWNER.toCombined - ) + ): @unchecked ex should be(AccessDeniedException("You do not have access to the requested resource.")) } @@ -336,7 +337,13 @@ class ReadServiceTest extends UnitSuite with UnitTestEnvironment { test("That learningstepV2For returns None when the learningPath does not exist") { when(learningPathRepository.withId(eqTo(PUBLISHED_ID))(any[DBSession])).thenReturn(None) val Failure(ex) = - service.learningstepV2For(PUBLISHED_ID, STEP1.id.get, "nb", fallback = false, TokenUser.PublicUser.toCombined) + service.learningstepV2For( + PUBLISHED_ID, + STEP1.id.get, + "nb", + fallback = false, + TokenUser.PublicUser.toCombined + ): @unchecked ex.isInstanceOf[NotFoundException] } @@ -346,7 +353,13 @@ class ReadServiceTest extends UnitSuite with UnitTestEnvironment { when(learningPathRepository.learningStepWithId(eqTo(PUBLISHED_ID), eqTo(STEP1.id.get))(any[DBSession])) .thenReturn(None) val Failure(ex) = - service.learningstepV2For(PUBLISHED_ID, STEP1.id.get, "nb", fallback = false, TokenUser.PublicUser.toCombined) + service.learningstepV2For( + PUBLISHED_ID, + STEP1.id.get, + "nb", + fallback = false, + TokenUser.PublicUser.toCombined + ): @unchecked ex.isInstanceOf[NotFoundException] } @@ -380,7 +393,13 @@ class ReadServiceTest extends UnitSuite with UnitTestEnvironment { when(learningPathRepository.withId(eqTo(PRIVATE_ID))(any[DBSession])) .thenReturn(Some(PRIVATE_LEARNINGPATH)) val Failure(ex) = - service.learningstepV2For(PRIVATE_ID, STEP1.id.get, "nb", fallback = false, TokenUser.PublicUser.toCombined) + service.learningstepV2For( + PRIVATE_ID, + STEP1.id.get, + "nb", + fallback = false, + TokenUser.PublicUser.toCombined + ): @unchecked ex should be(AccessDeniedException("You do not have access to the requested resource.")) } @@ -388,7 +407,13 @@ class ReadServiceTest extends UnitSuite with UnitTestEnvironment { when(learningPathRepository.withId(eqTo(PRIVATE_ID))(any[DBSession])) .thenReturn(Some(PRIVATE_LEARNINGPATH)) val Failure(ex) = - service.learningstepV2For(PRIVATE_ID, STEP1.id.get, "nb", fallback = false, PUBLISHED_OWNER.toCombined) + service.learningstepV2For( + PRIVATE_ID, + STEP1.id.get, + "nb", + fallback = false, + PUBLISHED_OWNER.toCombined + ): @unchecked ex should be(AccessDeniedException("You do not have access to the requested resource.")) } diff --git a/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/UpdateServiceTest.scala b/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/UpdateServiceTest.scala index a2cfd84191..ada007eac9 100644 --- a/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/UpdateServiceTest.scala +++ b/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/UpdateServiceTest.scala @@ -343,7 +343,7 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { doAnswer((i: InvocationOnMock) => { val x = i.getArgument[DBSession => Try[?]](0) x(mock[DBSession]) - }).when(DBUtil).rollbackOnFailure(any) + }).when(DBUtil).rollbackOnFailure(any()) } test("That addLearningPathV2 inserts the given LearningPathV2") { @@ -360,7 +360,8 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { test("That updateLearningPathV2 returns Failure when the given ID does not exist") { when(learningPathRepository.withId(eqTo(PRIVATE_ID))(any[DBSession])).thenReturn(None) - val Failure(ex) = service.updateLearningPathV2(PRIVATE_ID, UPDATED_PRIVATE_LEARNINGPATHV2, PRIVATE_OWNER.toCombined) + val Failure(ex) = + service.updateLearningPathV2(PRIVATE_ID, UPDATED_PRIVATE_LEARNINGPATHV2, PRIVATE_OWNER.toCombined): @unchecked ex should be(NotFoundException("Could not find learningpath with id '2'.")) } @@ -420,7 +421,7 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { PRIVATE_ID, UPDATED_PRIVATE_LEARNINGPATHV2, TokenUser("not_the_owner", Set.empty, None).toCombined - ) + ): @unchecked ex should be(AccessDeniedException("You do not have access to the requested resource.")) } @@ -449,7 +450,12 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { test("That updateLearningPathStatusV2 returns None when the given ID does not exist") { when(learningPathRepository.withIdIncludingDeleted(eqTo(PRIVATE_ID))(any[DBSession])).thenReturn(None) val Failure(ex) = - service.updateLearningPathStatusV2(PRIVATE_ID, LearningPathStatus.PUBLISHED, PRIVATE_OWNER.toCombined, "nb") + service.updateLearningPathStatusV2( + PRIVATE_ID, + LearningPathStatus.PUBLISHED, + PRIVATE_OWNER.toCombined, + "nb" + ): @unchecked ex should be(NotFoundException(s"Could not find learningpath with id '$PRIVATE_ID'.")) } @@ -614,7 +620,12 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { when(learningPathRepository.withIdIncludingDeleted(eqTo(PRIVATE_ID))(any[DBSession])) .thenReturn(Some(PRIVATE_LEARNINGPATH)) val Failure(ex) = - service.updateLearningPathStatusV2(PRIVATE_ID, LearningPathStatus.PUBLISHED, PRIVATE_OWNER.toCombined, "nb") + service.updateLearningPathStatusV2( + PRIVATE_ID, + LearningPathStatus.PUBLISHED, + PRIVATE_OWNER.toCombined, + "nb" + ): @unchecked ex should be(AccessDeniedException("You need to be a publisher to publish learningpaths.")) } @@ -656,7 +667,12 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { when(learningPathRepository.withIdIncludingDeleted(eqTo(PUBLISHED_ID))(any[DBSession])) .thenReturn(Some(PUBLISHED_LEARNINGPATH)) val Failure(ex) = - service.updateLearningPathStatusV2(PUBLISHED_ID, LearningPathStatus.PRIVATE, PRIVATE_OWNER.toCombined, "nb") + service.updateLearningPathStatusV2( + PUBLISHED_ID, + LearningPathStatus.PRIVATE, + PRIVATE_OWNER.toCombined, + "nb" + ): @unchecked ex should be(AccessDeniedException("You do not have access to the requested resource.")) } @@ -712,7 +728,7 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { test("That addLearningStepV2 returns None when the given learningpath does not exist") { when(learningPathRepository.withId(eqTo(PRIVATE_ID))(any[DBSession])).thenReturn(None) - val Failure(ex) = service.addLearningStepV2(PRIVATE_ID, NEW_STEPV2, PRIVATE_OWNER.toCombined) + val Failure(ex) = service.addLearningStepV2(PRIVATE_ID, NEW_STEPV2, PRIVATE_OWNER.toCombined): @unchecked ex.isInstanceOf[NotFoundException] should be(true) verify(learningPathRepository, never).insertLearningStep(any[LearningStep])(any) verify(learningPathRepository, never).update(any[LearningPath])(any) @@ -763,7 +779,7 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { test("That addLearningStepV2 throws an AccessDeniedException when the given user is NOT the owner") { when(learningPathRepository.withId(eqTo(PUBLISHED_ID))(any[DBSession])).thenReturn(Some(PUBLISHED_LEARNINGPATH)) - val Failure(ex) = service.addLearningStepV2(PUBLISHED_ID, NEW_STEPV2, PRIVATE_OWNER.toCombined) + val Failure(ex) = service.addLearningStepV2(PUBLISHED_ID, NEW_STEPV2, PRIVATE_OWNER.toCombined): @unchecked ex should be(AccessDeniedException("You do not have access to the requested resource.")) } @@ -771,7 +787,7 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { when(learningPathRepository.withId(eqTo(PUBLISHED_ID))(any[DBSession])).thenReturn(None) val Failure(ex) = - service.updateLearningStepV2(PUBLISHED_ID, STEP1.id.get, UPDATED_STEPV2, PUBLISHED_OWNER.toCombined) + service.updateLearningStepV2(PUBLISHED_ID, STEP1.id.get, UPDATED_STEPV2, PUBLISHED_OWNER.toCombined): @unchecked ex.isInstanceOf[NotFoundException] should be(true) verify(learningPathRepository, never).updateLearningStep(any[LearningStep])(any) @@ -783,7 +799,7 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { when(learningPathRepository.learningStepWithId(eqTo(PUBLISHED_ID), eqTo(STEP1.id.get))(any[DBSession])) .thenReturn(None) val Failure(ex) = - service.updateLearningStepV2(PUBLISHED_ID, STEP1.id.get, UPDATED_STEPV2, PUBLISHED_OWNER.toCombined) + service.updateLearningStepV2(PUBLISHED_ID, STEP1.id.get, UPDATED_STEPV2, PUBLISHED_OWNER.toCombined): @unchecked ex.isInstanceOf[NotFoundException] should be(true) verify(learningPathRepository, never).updateLearningStep(any[LearningStep])(any[DBSession]) verify(learningPathRepository, never).update(any[LearningPath])(any[DBSession]) @@ -846,7 +862,8 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { test("That updateLearningStepV2 throws an AccessDeniedException when the given user is NOT the owner") { when(learningPathRepository.withId(eqTo(PRIVATE_ID))(any[DBSession])).thenReturn(Some(PRIVATE_LEARNINGPATH)) when(learningPathRepository.learningStepWithId(PRIVATE_ID, STEP1.id.get)).thenReturn(Some(STEP1)) - val Failure(ex) = service.updateLearningStepV2(PRIVATE_ID, STEP1.id.get, UPDATED_STEPV2, PUBLISHED_OWNER.toCombined) + val Failure(ex) = + service.updateLearningStepV2(PRIVATE_ID, STEP1.id.get, UPDATED_STEPV2, PUBLISHED_OWNER.toCombined): @unchecked ex should be(AccessDeniedException("You do not have access to the requested resource.")) } @@ -854,7 +871,12 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { when(learningPathRepository.withId(eqTo(PUBLISHED_ID))(any[DBSession])).thenReturn(None) val Failure(ex) = - service.updateLearningStepStatusV2(PUBLISHED_ID, STEP1.id.get, StepStatus.DELETED, PUBLISHED_OWNER.toCombined) + service.updateLearningStepStatusV2( + PUBLISHED_ID, + STEP1.id.get, + StepStatus.DELETED, + PUBLISHED_OWNER.toCombined + ): @unchecked ex.isInstanceOf[NotFoundException] should be(true) } @@ -865,7 +887,12 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { .thenReturn(Seq()) val Failure(ex) = - service.updateLearningStepStatusV2(PUBLISHED_ID, STEP1.id.get, StepStatus.DELETED, PUBLISHED_OWNER.toCombined) + service.updateLearningStepStatusV2( + PUBLISHED_ID, + STEP1.id.get, + StepStatus.DELETED, + PUBLISHED_OWNER.toCombined + ): @unchecked ex.isInstanceOf[NotFoundException] should be(true) } @@ -1001,7 +1028,12 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { when(learningPathRepository.learningStepWithId(PRIVATE_ID, STEP1.id.get)) .thenReturn(Some(STEP1)) val Failure(ex) = - service.updateLearningStepStatusV2(PRIVATE_ID, STEP1.id.get, StepStatus.DELETED, PUBLISHED_OWNER.toCombined) + service.updateLearningStepStatusV2( + PRIVATE_ID, + STEP1.id.get, + StepStatus.DELETED, + PUBLISHED_OWNER.toCombined + ): @unchecked ex should be(AccessDeniedException("You do not have access to the requested resource.")) } @@ -1012,7 +1044,7 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { .thenReturn(Some(STEP1)) val Failure(exception: ValidationException) = - service.updateSeqNo(PRIVATE_ID, STEP1.id.get, 100, PRIVATE_OWNER.toCombined) + service.updateSeqNo(PRIVATE_ID, STEP1.id.get, 100, PRIVATE_OWNER.toCombined): @unchecked exception.errors.length should be(1) exception.errors.head.field should equal("seqNo") @@ -1116,7 +1148,8 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { when(learningPathRepository.withId(eqTo(PRIVATE_ID))(any[DBSession])) .thenReturn(Some(PRIVATE_LEARNINGPATH)) - val Failure(ex) = service.newFromExistingV2(PRIVATE_ID, NEW_COPIED_LEARNINGPATHV2, PUBLISHED_OWNER.toCombined) + val Failure(ex) = + service.newFromExistingV2(PRIVATE_ID, NEW_COPIED_LEARNINGPATHV2, PUBLISHED_OWNER.toCombined): @unchecked ex should be(AccessDeniedException("You do not have access to the requested resource.")) } @@ -1286,7 +1319,8 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { test("That newFromExistingV2 returns None when given id does not exist") { when(learningPathRepository.withId(eqTo(PUBLISHED_ID))(any[DBSession])).thenReturn(None) - val Failure(ex) = service.newFromExistingV2(PUBLISHED_ID, NEW_COPIED_LEARNINGPATHV2, PUBLISHED_OWNER.toCombined) + val Failure(ex) = + service.newFromExistingV2(PUBLISHED_ID, NEW_COPIED_LEARNINGPATHV2, PUBLISHED_OWNER.toCombined): @unchecked ex.isInstanceOf[NotFoundException] should be(true) } @@ -1521,7 +1555,7 @@ class UpdateServiceTest extends UnitSuite with UnitTestEnvironment { STEP1.id.get, "nb", PRIVATE_OWNER.toCombined - ) + ): @unchecked result.getMessage should equal("Cannot delete last title for step with id 1") } diff --git a/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/search/SearchServiceTest.scala b/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/search/SearchServiceTest.scala index 7922d15bef..92459f2be3 100644 --- a/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/search/SearchServiceTest.scala +++ b/learningpath-api/src/test/scala/no/ndla/learningpathapi/service/search/SearchServiceTest.scala @@ -35,13 +35,12 @@ import scala.util.Success import no.ndla.common.model.domain.Priority class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite with TestEnvironment { - import props.{DefaultPageSize, MaxPageSize} e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.get) - override val searchConverterService: SearchConverterService = new SearchConverterService - override val searchIndexService: SearchIndexService = new SearchIndexService { + override lazy val searchConverterService: SearchConverterService = new SearchConverterService + override lazy val searchIndexService: SearchIndexService = new SearchIndexService { override val indexShards: Int = 1 // 1 shard for accurate scoring in tests } - override val searchService: SearchService = new SearchService + override lazy val searchService: SearchService = new SearchService val paul: Author = Author(ContributorType.Writer, "Truly Weird Rand Paul") val license: String = License.PublicDomain.toString @@ -206,7 +205,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit fallback = true, sort = Sort.ByIdDesc ) - ) + ): @unchecked res.results.length should be(res.totalCount) res.totalCount should be(5) } @@ -219,25 +218,25 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit fallback = false, sort = Sort.ByIdDesc ) - ) + ): @unchecked res.results.length should be(res.totalCount) res.totalCount should be(0) } test("That getStartAtAndNumResults returns default values for None-input") { - searchService.getStartAtAndNumResults(None, None) should equal((0, DefaultPageSize)) + searchService.getStartAtAndNumResults(None, None) should equal((0, props.DefaultPageSize)) } test("That getStartAtAndNumResults returns SEARCH_MAX_PAGE_SIZE for value greater than SEARCH_MAX_PAGE_SIZE") { - searchService.getStartAtAndNumResults(None, Some(10001)) should equal((0, MaxPageSize)) + searchService.getStartAtAndNumResults(None, Some(10001)) should equal((0, props.MaxPageSize)) } test( "That getStartAtAndNumResults returns the correct calculated start at for page and page-size with default page-size" ) { val page = 74 - val expectedStartAt = (page - 1) * DefaultPageSize - searchService.getStartAtAndNumResults(Some(page), None) should equal((expectedStartAt, DefaultPageSize)) + val expectedStartAt = (page - 1) * props.DefaultPageSize + searchService.getStartAtAndNumResults(Some(page), None) should equal((expectedStartAt, props.DefaultPageSize)) } test("That getStartAtAndNumResults returns the correct calculated start at for page and page-size") { @@ -252,7 +251,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit searchSettings.copy( sort = Sort.ByTitleDesc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(4) @@ -268,7 +267,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit searchSettings.copy( sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(4) @@ -283,7 +282,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit searchSettings.copy( sort = Sort.ByIdDesc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(4) @@ -299,7 +298,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit sort = Sort.ByIdAsc, language = Some("*") ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(5) @@ -315,7 +314,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit searchSettings.copy( sort = Sort.ByDurationDesc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(4) @@ -327,7 +326,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit searchSettings.copy( sort = Sort.ByDurationAsc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(4) @@ -339,7 +338,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit searchSettings.copy( sort = Sort.ByLastUpdatedDesc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(4) @@ -352,7 +351,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit searchSettings.copy( sort = Sort.ByLastUpdatedAsc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(4) @@ -367,7 +366,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit sort = Sort.ByTitleAsc, language = Some(Language.AllLanguages) ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(2) @@ -383,7 +382,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit language = Some("en"), fallback = true ) - ) + ): @unchecked searchResult.totalCount should be(1) } @@ -396,7 +395,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit language = Some("en"), fallback = false ) - ) + ): @unchecked searchResult.totalCount should be(0) } @@ -407,7 +406,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit query = Some("heltene"), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(1) @@ -422,7 +421,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit sort = Sort.ByTitleAsc, language = Some(Language.AllLanguages) ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(1) @@ -436,7 +435,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit sort = Sort.ByTitleAsc, language = Some("en") ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(1) @@ -450,7 +449,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit sort = Sort.ByTitleAsc, language = Some("djb") ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(1) @@ -463,7 +462,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit sort = Sort.ByTitleAsc, language = Some("kra") ) - ) + ): @unchecked searchResult.totalCount should be(0) } @@ -475,7 +474,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit taggedWith = Some("superhelt"), language = Some("nb") ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(2) @@ -492,7 +491,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit taggedWith = Some("kanfly"), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(1) @@ -505,7 +504,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit query = Some("tøff rar"), sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(2) @@ -521,7 +520,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit query = Some("and flaggermus fugl"), sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(3) @@ -539,7 +538,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit taggedWith = Some("kanfly"), sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(2) @@ -553,7 +552,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit query = Some("and flaggremsu"), sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(1) @@ -566,7 +565,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit query = Some("kjeltring + batman"), sort = Sort.ByRelevanceAsc ) - ) + ): @unchecked searchResult1.totalCount should be(0) val Success(searchResult2) = searchService.matchingQuery( @@ -574,7 +573,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit query = Some("tøff + morsom + -and"), sort = Sort.ByRelevanceAsc ) - ) + ): @unchecked val hits2 = searchResult2.results searchResult2.totalCount should be(1) @@ -585,7 +584,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit query = Some("tøff | morsom | kjeltring"), sort = Sort.ByIdAsc ) - ) + ): @unchecked val hits3 = searchResult3.results searchResult3.totalCount should be(3) @@ -599,14 +598,14 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit language = Some(Language.AllLanguages), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val Success(searchEn) = searchService.matchingQuery( searchSettings.copy( query = Some("Unrelated"), language = Some(Language.AllLanguages), sort = Sort.ByTitleAsc ) - ) + ): @unchecked searchEn.totalCount should be(1) searchEn.results.head.id should be(UnrelatedId) @@ -629,7 +628,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit sort = Sort.ByTitleAsc, language = Some(Language.AllLanguages) ) - ) + ): @unchecked search.totalCount should be(5) search.results.head.id should be(BatmanId) @@ -648,7 +647,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit sort = Sort.ByTitleAsc, language = Some(Language.AllLanguages) ) - ) + ): @unchecked search.results.head.supportedLanguages should be(Seq("nb", "en")) } @@ -658,7 +657,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit language = Some("en"), fallback = true ) - ) + ): @unchecked search.totalCount should be(5) search.results.head.id should be(PenguinId) @@ -682,11 +681,11 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit fallback = true, shouldScroll = true ) - ) + ): @unchecked - val Success(scroll1) = searchService.scroll(initialSearch.scrollId.get, "all") - val Success(scroll2) = searchService.scroll(scroll1.scrollId.get, "all") - val Success(scroll3) = searchService.scroll(scroll2.scrollId.get, "all") + val Success(scroll1) = searchService.scroll(initialSearch.scrollId.get, "all"): @unchecked + val Success(scroll2) = searchService.scroll(scroll1.scrollId.get, "all"): @unchecked + val Success(scroll3) = searchService.scroll(scroll2.scrollId.get, "all"): @unchecked initialSearch.results.map(_.id) should be(expectedIds.head) scroll1.results.map(_.id) should be(expectedIds(1)) @@ -704,7 +703,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit verificationStatus = Some("EXTERNAL"), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(1) @@ -720,7 +719,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit verificationStatus = Some("CREATED_BY_NDLA"), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits = searchResult.results searchResult.totalCount should be(1) @@ -733,7 +732,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit sort = Sort.ByIdAsc, language = Some(Language.AllLanguages) ) - ) + ): @unchecked searchResult.totalCount should be(5) searchResult.results.map(_.id) should be(Seq(1, 2, 3, 4, 5)) @@ -746,7 +745,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit language = Some(Language.AllLanguages), status = List(learningpath.LearningPathStatus.PUBLISHED, learningpath.LearningPathStatus.UNLISTED) ) - ) + ): @unchecked searchResult.totalCount should be(6) searchResult.results.map(_.id) should be(Seq(1, 2, 3, 4, 5, 6)) @@ -757,7 +756,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit language = Some(Language.AllLanguages), status = List(learningpath.LearningPathStatus.UNLISTED) ) - ) + ): @unchecked searchResult2.totalCount should be(1) searchResult2.results.map(_.id) should be(Seq(6)) @@ -770,7 +769,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit language = Some(Language.AllLanguages), withPaths = List("https://ndla.no/article/1", "https://ndla.no/article/2") ) - ) + ): @unchecked searchResult.totalCount should be(2) searchResult.results.map(_.id) should be(Seq(1, 2)) @@ -781,7 +780,7 @@ class SearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite wit language = Some(Language.AllLanguages), withPaths = List("https://ndla.no/article/2") ) - ) + ): @unchecked searchResult2.totalCount should be(0) } diff --git a/learningpath-api/src/test/scala/no/ndla/learningpathapi/validation/LearningPathValidatorTest.scala b/learningpath-api/src/test/scala/no/ndla/learningpathapi/validation/LearningPathValidatorTest.scala index 9330b0b1e4..2801534047 100644 --- a/learningpath-api/src/test/scala/no/ndla/learningpathapi/validation/LearningPathValidatorTest.scala +++ b/learningpath-api/src/test/scala/no/ndla/learningpathapi/validation/LearningPathValidatorTest.scala @@ -26,7 +26,7 @@ class LearningPathValidatorTest extends UnitSuite with TestEnvironment { var validator: LearningPathValidator = _ - override val clock = new SystemClock + override lazy val clock = new SystemClock override def beforeEach(): Unit = { validator = new LearningPathValidator diff --git a/learningpath-api/src/test/scala/no/ndla/learningpathapi/validation/TextValidatorTest.scala b/learningpath-api/src/test/scala/no/ndla/learningpathapi/validation/TextValidatorTest.scala index 6f7993084c..3d48b1fb26 100644 --- a/learningpath-api/src/test/scala/no/ndla/learningpathapi/validation/TextValidatorTest.scala +++ b/learningpath-api/src/test/scala/no/ndla/learningpathapi/validation/TextValidatorTest.scala @@ -11,8 +11,6 @@ package no.ndla.learningpathapi.validation import no.ndla.learningpathapi.{TestEnvironment, UnitSuite} class TextValidatorTest extends UnitSuite with TestEnvironment { - import props.AllowedHtmlTags - var allowedHtmlValidator: TextValidator = _ var noHtmlValidator: TextValidator = _ @@ -22,7 +20,7 @@ class TextValidatorTest extends UnitSuite with TestEnvironment { } test("That TextValidator allows all tags in AllowedHtmlTags tags") { - AllowedHtmlTags.foreach(tag => { + props.AllowedHtmlTags.foreach(tag => { val starttext = s"<$tag>This is text with $tag" val text = starttext + (if (tag.equals("br")) "" else s"") allowedHtmlValidator.validate("path1.path2", text) should equal(None) @@ -38,7 +36,7 @@ class TextValidatorTest extends UnitSuite with TestEnvironment { test("That TextValidator does not allow tags outside BasicHtmlTags") { val illegalTag = "aside" - AllowedHtmlTags.contains(illegalTag) should be(right = false) + props.AllowedHtmlTags.contains(illegalTag) should be(right = false) val text = s"<$illegalTag>This is text with $illegalTag" diff --git a/modules/DefaultOptions.mill b/modules/DefaultOptions.mill index 0c5e82af35..fffc9018e0 100644 --- a/modules/DefaultOptions.mill +++ b/modules/DefaultOptions.mill @@ -10,6 +10,11 @@ trait DefaultOptions extends ScalaModule { private def scala3: VersionCheckFunc = version => version.startsWith("3.") private def scala3and2: VersionCheckFunc = version => scala2(version) && scala3(version) + extension (opts: Seq[NDLAScalaOption]) + def filterByVersion(version: String): Seq[String] = opts + .filter { o => o.versionCheck(version) } + .map(_.name) + case class NDLAScalaOption(name: String, versionCheck: VersionCheckFunc = _ => true) given Conversion[String, NDLAScalaOption] = @@ -28,6 +33,7 @@ trait DefaultOptions extends ScalaModule { "-language:higherKinds", "-language:implicitConversions", "-Xfatal-warnings", + "-deprecation", "-Xlint:adapted-args" -> scala2, "-Xlint:constant" -> scala2, "-Xlint:delayedinit-select" -> scala2, @@ -57,10 +63,11 @@ trait DefaultOptions extends ScalaModule { "-Wunused:imports", "-Wunused:locals", "-Wunused:params", - "-Wunused:patvars", "-Wunused:privates", - "-Wconf:src=src_managed/.*:silent" - ).filter(_.versionCheck(scalaVersion())).map(_.name) + "-Wconf:src=src_managed/.*:silent", + "-Yretain-trees" -> scala3, + "-Xmax-inlines:50" -> scala3 + ).filterByVersion(scalaVersion()) } def scalacOptionsOnlyRun: T[Seq[String]] = Task { @@ -68,13 +75,13 @@ trait DefaultOptions extends ScalaModule { "-Wdead-code" -> scala2, "-Wvalue-discard", "-Wnonunit-statement" - ).filter(_.versionCheck(scalaVersion())).map(_.name) + ).filterByVersion(scalaVersion()) } def testJavaOptions: T[Seq[String]] = Task { Seq[NDLAScalaOption]( "-XX:+EnableDynamicAgentLoading", "-Xshare:off" - ).filter(_.versionCheck(scalaVersion())).map(_.name) + ).filterByVersion(scalaVersion()) } } diff --git a/modules/versions.mill b/modules/versions.mill index 720a0ae55f..b548a3d1a3 100644 --- a/modules/versions.mill +++ b/modules/versions.mill @@ -3,20 +3,19 @@ package build.modules import mill._, mill.scalalib._ object SharedDependencies { - val ScalaVersion = "2.13.16" + val ScalaVersion = "3.3.6" val HikariConnectionPoolV = "6.3.0" val ScalaLoggingV = "3.9.5" val ScalaTestV = "3.2.19" val Log4JV = "2.25.0" val AwsSdkV = "2.31.64" - val MockitoV = "1.17.30" val Elastic4sV = "8.11.5" val JacksonV = "2.19.1" val CatsEffectV = "3.6.1" val FlywayV = "11.10.5" val PostgresV = "42.7.7" val Http4sV = "0.23.30" - val TapirV = "1.11.34" + val TapirV = "1.11.41" val ApiSpecV = "0.11.9" val SttpV = "3.11.0" val CirceV = "0.14.14" @@ -43,7 +42,7 @@ object SharedDependencies { def testing: Seq[Dep] = Seq( mvn"org.scalatest::scalatest:$ScalaTestV", - mvn"org.scalatestplus::mockito-5-10:3.2.18.0", + mvn"org.scalatestplus::mockito-5-18:3.2.19.0", mvn"org.testcontainers:elasticsearch:$TestContainersV", mvn"org.testcontainers:testcontainers:$TestContainersV", mvn"org.testcontainers:postgresql:$TestContainersV" diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/ComponentRegistry.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/ComponentRegistry.scala index dc758f6cfa..57963e7f60 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/ComponentRegistry.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/ComponentRegistry.scala @@ -71,37 +71,36 @@ class ComponentRegistry(properties: MyNdlaApiProperties) with LearningPathApiClient with V16__MigrateResourcePaths with NdlaClient { - override val props: MyNdlaApiProperties = properties + override lazy val props: MyNdlaApiProperties = properties + override lazy val healthController: TapirHealthController = new TapirHealthController + override lazy val clock: SystemClock = new SystemClock + override lazy val folderController: FolderController = new FolderController + override lazy val robotController: RobotController = new RobotController + override lazy val feideApiClient: FeideApiClient = new FeideApiClient + override lazy val redisClient = new RedisClient(props.RedisHost, props.RedisPort) + override lazy val folderRepository: FolderRepository = new FolderRepository + override lazy val folderConverterService: FolderConverterService = new FolderConverterService + override lazy val folderReadService: FolderReadService = new FolderReadService + override lazy val folderWriteService: FolderWriteService = new FolderWriteService + override lazy val userRepository: UserRepository = new UserRepository + override lazy val robotRepository: RobotRepository = new RobotRepository + override lazy val robotService: RobotService = new RobotService + override lazy val userService: UserService = new UserService + override lazy val userController: UserController = new UserController + override lazy val configRepository: ConfigRepository = new ConfigRepository + override lazy val configService: ConfigService = new ConfigService + override lazy val configController: ConfigController = new ConfigController + lazy val statsController: StatsController = new StatsController + override lazy val nodebb: NodeBBClient = new NodeBBClient + override lazy val searchApiClient: SearchApiClient = new SearchApiClient + override lazy val taxonomyApiClient: TaxonomyApiClient = new TaxonomyApiClient + override lazy val learningPathApiClient: LearningPathApiClient = new LearningPathApiClient + override lazy val ndlaClient: NdlaClient = new NdlaClient + override lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient + lazy val v16__MigrateResourcePaths: V16__MigrateResourcePaths = new V16__MigrateResourcePaths + override lazy val DBUtil = new DBUtility - lazy val healthController: TapirHealthController = new TapirHealthController - lazy val clock: SystemClock = new SystemClock - lazy val folderController: FolderController = new FolderController - lazy val robotController: RobotController = new RobotController - lazy val feideApiClient: FeideApiClient = new FeideApiClient - lazy val redisClient = new RedisClient(props.RedisHost, props.RedisPort) - lazy val folderRepository: FolderRepository = new FolderRepository - lazy val folderConverterService: FolderConverterService = new FolderConverterService - lazy val folderReadService: FolderReadService = new FolderReadService - lazy val folderWriteService: FolderWriteService = new FolderWriteService - lazy val userRepository: UserRepository = new UserRepository - lazy val robotRepository: RobotRepository = new RobotRepository - lazy val robotService: RobotService = new RobotService - lazy val userService: UserService = new UserService - lazy val userController: UserController = new UserController - lazy val configRepository: ConfigRepository = new ConfigRepository - lazy val configService: ConfigService = new ConfigService - lazy val configController: ConfigController = new ConfigController - lazy val statsController: StatsController = new StatsController - lazy val nodebb: NodeBBClient = new NodeBBClient - lazy val searchApiClient: SearchApiClient = new SearchApiClient - lazy val taxonomyApiClient: TaxonomyApiClient = new TaxonomyApiClient - lazy val learningPathApiClient: LearningPathApiClient = new LearningPathApiClient - lazy val ndlaClient: NdlaClient = new NdlaClient - lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient - lazy val v16__MigrateResourcePaths: V16__MigrateResourcePaths = new V16__MigrateResourcePaths - lazy val DBUtil = new DBUtility - - override val migrator: DBMigrator = DBMigrator(v16__MigrateResourcePaths) + override lazy val migrator: DBMigrator = DBMigrator(v16__MigrateResourcePaths) override lazy val dataSource: HikariDataSource = DataSource.getHikariDataSource val swagger = new SwaggerController( diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/MyNdlaApiProperties.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/MyNdlaApiProperties.scala index 7306c2c999..8da1b21471 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/MyNdlaApiProperties.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/MyNdlaApiProperties.scala @@ -15,7 +15,7 @@ import no.ndla.network.AuthUser import scala.util.Properties.* trait Props extends HasBaseProps with HasDatabaseProps { - val props: MyNdlaApiProperties + lazy val props: MyNdlaApiProperties } class MyNdlaApiProperties extends BaseProps with DatabaseProps { diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/ConfigController.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/ConfigController.scala index 3cd3d13610..b22a120fbf 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/ConfigController.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/ConfigController.scala @@ -25,7 +25,7 @@ import no.ndla.network.tapir.auth.Permission.LEARNINGPATH_API_ADMIN trait ConfigController { this: ErrorHandling & ConfigService & TapirController => - val configController: ConfigController + lazy val configController: ConfigController class ConfigController extends TapirController { override val serviceName: String = "config" diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/FolderController.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/FolderController.scala index 92c0113702..411ffe3ec5 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/FolderController.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/FolderController.scala @@ -40,7 +40,7 @@ import java.util.UUID trait FolderController { this: FolderReadService & FolderWriteService & ErrorHandling & TapirController => - val folderController: FolderController + lazy val folderController: FolderController class FolderController extends TapirController { override val serviceName: String = "folders" diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/RobotController.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/RobotController.scala index 2ac506998e..afebac5a4e 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/RobotController.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/RobotController.scala @@ -23,7 +23,7 @@ import java.util.UUID trait RobotController { this: ErrorHandling & TapirController & RobotService => - val robotController: RobotController + lazy val robotController: RobotController class RobotController extends TapirController { override val serviceName: String = "robots" diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/UserController.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/UserController.scala index 13c5453a44..360b7382a0 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/UserController.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/UserController.scala @@ -23,7 +23,7 @@ import no.ndla.myndlaapi.service.{FolderReadService, FolderWriteService, UserSer trait UserController { this: ErrorHandling & UserService & MyNDLAAuthHelpers & FolderWriteService & FolderReadService & TapirController => - val userController: UserController + lazy val userController: UserController class UserController extends TapirController { override val serviceName: String = "users" diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/db/migrationwithdependencies/V16__MigrateResourcePaths.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/db/migrationwithdependencies/V16__MigrateResourcePaths.scala index 74f6ab8ad4..069aa69db9 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/db/migrationwithdependencies/V16__MigrateResourcePaths.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/db/migrationwithdependencies/V16__MigrateResourcePaths.scala @@ -20,7 +20,7 @@ trait V16__MigrateResourcePaths { class V16__MigrateResourcePaths extends TableMigration[ResourceRow] { override val tableName: String = "resources" - override val whereClause: scalikejdbc.SQLSyntax = sqls"path is not null" + override lazy val whereClause: scalikejdbc.SQLSyntax = sqls"path is not null" override val chunkSize: Int = 1000 override def extractRowData(rs: WrappedResultSet): ResourceRow = ResourceRow( UUID.fromString(rs.string("id")), diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/LearningPathApiClient.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/LearningPathApiClient.scala index c6a0a3476e..acb5fbe6cf 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/LearningPathApiClient.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/LearningPathApiClient.scala @@ -21,12 +21,11 @@ import scala.util.Try trait LearningPathApiClient { this: NdlaClient & Props => - val learningPathApiClient: LearningPathApiClient + lazy val learningPathApiClient: LearningPathApiClient class LearningPathApiClient extends StrictLogging { - import props.LearningpathApiUrl private val learningPathTimeout = 20.seconds - def getStats: Try[LearningPathStatsDTO] = get[LearningPathStatsDTO](s"$LearningpathApiUrl/intern/stats") + def getStats: Try[LearningPathStatsDTO] = get[LearningPathStatsDTO](s"${props.LearningpathApiUrl}/intern/stats") private def get[A: Decoder](url: String, params: (String, String)*): Try[A] = { val request = quickRequest.get(uri"$url".withParams(params*)).readTimeout(learningPathTimeout) diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/SearchApiClient.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/SearchApiClient.scala index c5881f366f..aa5f2feb8b 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/SearchApiClient.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/SearchApiClient.scala @@ -21,7 +21,7 @@ import scala.util.{Failure, Success, Try} trait SearchApiClient { this: NdlaClient & Props => - val searchApiClient: SearchApiClient + lazy val searchApiClient: SearchApiClient class SearchApiClient extends StrictLogging { private val internEndpoint = s"${props.SearchApiUrl}/intern" diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/TaxonomyApiClient.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/TaxonomyApiClient.scala index c540ec8298..acd085e7e9 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/TaxonomyApiClient.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/TaxonomyApiClient.scala @@ -20,7 +20,7 @@ import scala.util.{Success, Try} trait TaxonomyApiClient { this: NdlaClient & Props => - val taxonomyApiClient: TaxonomyApiClient + lazy val taxonomyApiClient: TaxonomyApiClient class TaxonomyApiClient extends StrictLogging { private val resolveEndpoint = s"${props.TaxonomyUrl}/v1/url/resolve" diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/nodebb/NodeBBClient.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/nodebb/NodeBBClient.scala index f126ea3c95..dc816a5668 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/nodebb/NodeBBClient.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/integration/nodebb/NodeBBClient.scala @@ -12,7 +12,7 @@ import cats.implicits.catsSyntaxOptionId import com.typesafe.scalalogging.StrictLogging import io.circe.generic.auto.* import io.circe.parser.parse -import no.ndla.common.implicits.TryQuestionMark +import no.ndla.common.implicits.* import no.ndla.myndlaapi.Props import no.ndla.network.model.FeideAccessToken import sttp.client3.Response @@ -20,11 +20,11 @@ import sttp.client3.quick.* import sttp.model.headers.CookieWithMeta import scala.annotation.tailrec -import scala.util.{Failure, Success, Try} +import scala.util.{Failure, Success, Try, boundary} trait NodeBBClient { this: Props => - val nodebb: NodeBBClient + lazy val nodebb: NodeBBClient class NodeBBClient extends StrictLogging { private val baseUrl: String = props.nodeBBUrl @@ -32,7 +32,7 @@ trait NodeBBClient { case class NodeBBSession(csrfToken: String, cookies: Seq[CookieWithMeta]) - private def getCSRFToken(feideToken: FeideAccessToken): Try[NodeBBSession] = { + private def getCSRFToken(feideToken: FeideAccessToken): Try[NodeBBSession] = permitTry { val request = quickRequest .get(uri"$baseUrl/api/config") .header("FeideAuthorization", s"Bearer $feideToken") @@ -65,19 +65,21 @@ trait NodeBBClient { } } - def getUserId(feideToken: FeideAccessToken): Try[Option[Long]] = { - val request = quickRequest - .get(uri"$baseUrl/api/config") - .header("FeideAuthorization", s"Bearer $feideToken") - val resp = doReq(request).? + def getUserId(feideToken: FeideAccessToken): Try[Option[Long]] = boundary { + permitTry { + val request = quickRequest + .get(uri"$baseUrl/api/config") + .header("FeideAuthorization", s"Bearer $feideToken") + val resp = doReq(request).? - if (resp.code.code == 403) return Success(None) + if (resp.code.code == 403) boundary.break(Success(None)) - val body = resp.body - parse(body) - .flatMap(_.as[UserSelf]) - .toTry - .map(_.uid.some) + val body = resp.body + parse(body) + .flatMap(_.as[UserSelf]) + .toTry + .map(_.uid.some) + } } def deleteUser(userId: Option[Long], feideToken: FeideAccessToken): Try[Unit] = { @@ -91,19 +93,20 @@ trait NodeBBClient { } } - def deleteUserWithCSRF(userId: Long, feideToken: FeideAccessToken, nodebbSession: NodeBBSession): Try[Unit] = { - val request = quickRequest - .delete(uri"$baseUrl/api/v3/users/$userId/account") - .header("FeideAuthorization", s"Bearer $feideToken") - .header("X-CSRF-Token", nodebbSession.csrfToken) - .cookies(nodebbSession.cookies) - val resp = doReq(request).? - if (resp.isSuccess) Success(()) - else { - val msg = s"Failed to delete nodebb user with id $userId, Got code: ${resp.code}, with body:\n\n${resp.body}" - logger.error(msg) - Failure(new Exception(msg)) + def deleteUserWithCSRF(userId: Long, feideToken: FeideAccessToken, nodebbSession: NodeBBSession): Try[Unit] = + permitTry { + val request = quickRequest + .delete(uri"$baseUrl/api/v3/users/$userId/account") + .header("FeideAuthorization", s"Bearer $feideToken") + .header("X-CSRF-Token", nodebbSession.csrfToken) + .cookies(nodebbSession.cookies) + val resp = doReq(request).? + if (resp.isSuccess) Success(()) + else { + val msg = s"Failed to delete nodebb user with id $userId, Got code: ${resp.code}, with body:\n\n${resp.body}" + logger.error(msg) + Failure(new Exception(msg)) + } } - } } } diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/model/api/FolderDTO.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/model/api/FolderDTO.scala index d0c775352a..750e8a1508 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/model/api/FolderDTO.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/model/api/FolderDTO.scala @@ -15,6 +15,7 @@ import io.circe.{Decoder, Encoder} import no.ndla.common.model.NDLADate import no.ndla.common.model.domain.ResourceType import no.ndla.myndlaapi.model.domain.{CopyableFolder, CopyableResource} +import sttp.tapir.Schema import sttp.tapir.Schema.annotations.description import java.util.UUID @@ -48,6 +49,8 @@ case class FolderDTO( object FolderDTO { implicit val folderEncoder: Encoder[FolderDTO] = deriveEncoder implicit val folderDecoder: Decoder[FolderDTO] = deriveDecoder + import sttp.tapir.generic.auto.* + implicit def schema: Schema[FolderDTO] = Schema.derivedSchema implicit val folderDataEncoder: Encoder[FolderDataDTO] = Encoder.instance { case folder: FolderDTO => folder.asJson } implicit val folderDataDecoder: Decoder[FolderDataDTO] = Decoder[FolderDTO].widen diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/DBMyNDLAUser.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/DBMyNDLAUser.scala index 78d14ed09e..79761392c3 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/DBMyNDLAUser.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/DBMyNDLAUser.scala @@ -17,11 +17,9 @@ object DBMyNDLAUser extends SQLSyntaxSupport[MyNDLAUser] { override val tableName = "my_ndla_users" def fromResultSet(lp: SyntaxProvider[MyNDLAUser])(rs: WrappedResultSet): MyNDLAUser = - fromResultSet((s: String) => lp.resultName.c(s))(rs) + fromResultSetWithWrapper((s: String) => lp.resultName.c(s))(rs) - def fromResultSet(rs: WrappedResultSet): MyNDLAUser = fromResultSet((s: String) => s)(rs) - - def fromResultSet(colNameWrapper: String => String)(rs: WrappedResultSet): MyNDLAUser = { + private def fromResultSetWithWrapper(colNameWrapper: String => String)(rs: WrappedResultSet): MyNDLAUser = { val jsonString = rs.string(colNameWrapper("document")) val metaData = CirceUtil.unsafeParseAs[MyNDLAUserDocument](jsonString) val id = rs.long(colNameWrapper("id")) diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/Resource.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/Resource.scala index 69d4e2be24..4585d9009d 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/Resource.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/Resource.scala @@ -12,7 +12,7 @@ import cats.implicits.* import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import io.circe.{Decoder, Encoder} import no.ndla.common.CirceUtil -import no.ndla.common.implicits.OptionImplicit +import no.ndla.common.implicits.* import no.ndla.common.model.NDLADate import no.ndla.common.model.domain.ResourceType import no.ndla.network.model.FeideID diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/RobotDefinition.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/RobotDefinition.scala index 1181f826ce..f644471641 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/RobotDefinition.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/RobotDefinition.scala @@ -12,7 +12,7 @@ import io.circe.{Decoder, Encoder} import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} import no.ndla.common.CirceUtil import no.ndla.common.errors.{AccessDeniedException, NotFoundException} -import no.ndla.common.implicits.OptionImplicit +import no.ndla.common.implicits.* import no.ndla.common.model.NDLADate import no.ndla.myndlaapi.model.api.robot.RobotConfigurationDTO import no.ndla.network.model.FeideID diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/SavedSharedFolder.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/SavedSharedFolder.scala index ecfe2e2840..e4d5348271 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/SavedSharedFolder.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/model/domain/SavedSharedFolder.scala @@ -28,10 +28,9 @@ object SavedSharedFolder extends SQLSyntaxSupport[SavedSharedFolder] { override val tableName = "saved_shared_folder" - def fromResultSet(sp: SyntaxProvider[SavedSharedFolder])(rs: WrappedResultSet): Try[SavedSharedFolder] = + def fromResultSet(sp: SyntaxProvider[SavedSharedFolder], rs: WrappedResultSet): Try[SavedSharedFolder] = { fromResultSet((s: String) => sp.resultName.c(s))(rs) - - def fromResultSet(rs: WrappedResultSet): Try[SavedSharedFolder] = fromResultSet((s: String) => s)(rs) + } def fromResultSet(colNameWrapper: String => String)(rs: WrappedResultSet): Try[SavedSharedFolder] = { import no.ndla.myndlaapi.uuidBinder diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/ConfigRepository.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/ConfigRepository.scala index 81743d2943..4b81b622c3 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/ConfigRepository.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/ConfigRepository.scala @@ -19,7 +19,7 @@ import sqls.count import scala.util.{Success, Try} trait ConfigRepository { - val configRepository: ConfigRepository + lazy val configRepository: ConfigRepository import ConfigMeta.* @@ -51,12 +51,12 @@ trait ConfigRepository { if (updatedCount != 1) { logger.info(s"No existing value for ${config.key}, inserting the value.") - withSQL { + val _ = withSQL { insertInto(ConfigMeta).namedValues( ConfigMeta.column.c("configkey") -> config.key.entryName, ConfigMeta.column.c("value") -> config ) - }.update(): Unit + }.update() Success(config) } else { logger.info(s"Value for ${config.key} updated.") diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/FolderRepository.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/FolderRepository.scala index ec19ec2e66..3ff5caf344 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/FolderRepository.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/FolderRepository.scala @@ -39,7 +39,7 @@ import scala.util.{Failure, Success, Try} trait FolderRepository { this: Clock & DBUtility => - val folderRepository: FolderRepository + lazy val folderRepository: FolderRepository class FolderRepository extends StrictLogging { def getSession(readOnly: Boolean): DBSession = @@ -60,7 +60,7 @@ trait FolderRepository { val shared = if (folderData.status == FolderStatus.SHARED) Some(created) else None val column = Folder.column.c _ - withSQL { + val _ = withSQL { insert .into(Folder) .namedValues( @@ -75,7 +75,7 @@ trait FolderRepository { column("shared") -> shared, column("description") -> folderData.description ) - }.update(): Unit + }.update() logger.info(s"Inserted new folder with id: $newId") folderData.toFullFolder( @@ -100,7 +100,7 @@ trait FolderRepository { val newId = UUID.randomUUID() val column = Resource.column.c _ - withSQL { + val _ = withSQL { insert .into(Resource) .namedValues( @@ -111,7 +111,7 @@ trait FolderRepository { column("created") -> created, column("document") -> DBUtil.asJsonb(document) ) - }.update(): Unit + }.update() logger.info(s"Inserted new resource with id: $newId") document.toFullResource(newId, path, resourceType, feideId, created, None) @@ -123,7 +123,7 @@ trait FolderRepository { rank: Int, favoritedDate: NDLADate )(implicit session: DBSession = AutoSession): Try[FolderResource] = Try { - withSQL { + val _ = withSQL { insert .into(FolderResource) .namedValues( @@ -132,7 +132,7 @@ trait FolderRepository { FolderResource.column.rank -> rank, FolderResource.column.favoritedDate -> favoritedDate ) - }.update(): Unit + }.update() logger.info(s"Inserted new folder-resource connection with folder id $folderId and resource id $resourceId") FolderResource(folderId = folderId, resourceId = resourceId, rank = rank, favoritedDate = favoritedDate) @@ -527,7 +527,7 @@ trait FolderRepository { .toManies( rs => Resource.fromResultSetSyntaxProviderWithConnection(r, fr)(rs).sequence, rs => Try(DBMyNDLAUser.fromResultSet(u)(rs)).toOption, - rs => Try(SavedSharedFolder.fromResultSet(sfu)(rs)).toOption + rs => Try(SavedSharedFolder.fromResultSet(sfu, rs)).toOption ) .map((folder, resources, user, savedSharedFolder) => { toCompileFolder(folder, resources.toList, user.toList, savedSharedFolder.toList) @@ -762,9 +762,7 @@ trait FolderRepository { ) } - insertSql - .batch(batchParams*) - .apply(): Unit + val _ = insertSql.batch(batchParams*).apply() } } @@ -808,9 +806,9 @@ trait FolderRepository { } batchParams.map { params => - insertSql + val _ = insertSql .batch(params*) - .apply(): Unit + .apply() } }.flatten } @@ -836,9 +834,7 @@ trait FolderRepository { ) } - insertSql - .batch(batchParams*) - .apply(): Unit + val _ = insertSql.batch(batchParams*).apply() } def numberOfResources()(implicit session: DBSession = ReadOnlyAutoSession): Try[Option[Long]] = Try { @@ -868,7 +864,7 @@ trait FolderRepository { def createFolderUserConnection(folderId: UUID, feideId: FeideID, rank: Int)(implicit session: DBSession = AutoSession ): Try[SavedSharedFolder] = Try { - withSQL { + val _ = withSQL { insert .into(SavedSharedFolder) .namedValues( @@ -876,7 +872,7 @@ trait FolderRepository { SavedSharedFolder.column.feideId -> feideId, SavedSharedFolder.column.rank -> rank ) - }.update(): Unit + }.update() logger.info(s"Inserted new sharedFolder-user connection with folder id $folderId and feide id $feideId") SavedSharedFolder(folderId = folderId, feideId = feideId, rank = rank) diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/RobotRepository.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/RobotRepository.scala index 317f196ef6..2d0eb0bda6 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/RobotRepository.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/RobotRepository.scala @@ -23,7 +23,7 @@ import scala.util.{Failure, Success, Try} trait RobotRepository { this: Clock & DBUtility => - val robotRepository: RobotRepository + lazy val robotRepository: RobotRepository class RobotRepository extends StrictLogging { def getSession(readOnly: Boolean): DBSession = @@ -36,7 +36,7 @@ trait RobotRepository { def updateRobotDefinition(robot: RobotDefinition)(implicit session: DBSession): Try[Unit] = Try { val column = RobotDefinition.column.c _ - withSQL { + val _ = withSQL { update(RobotDefinition) .set( column("status") -> robot.status.entryName, @@ -47,7 +47,7 @@ trait RobotRepository { .where .eq(column("id"), robot.id) } - .update(): Unit + .update() logger.info(s"Updted robot definition with ID: ${robot.id}") } diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/UserRepository.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/UserRepository.scala index 9f19bcc5da..0214eaa553 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/UserRepository.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/repository/UserRepository.scala @@ -22,7 +22,7 @@ import scala.util.{Failure, Success, Try} trait UserRepository { this: DBUtility => - val userRepository: UserRepository + lazy val userRepository: UserRepository class UserRepository extends StrictLogging { @@ -148,11 +148,11 @@ trait UserRepository { } def deleteAllUsers(implicit session: DBSession): Try[Unit] = Try { - sql"delete from ${DBMyNDLAUser.table}".execute(): Unit + val _ = sql"delete from ${DBMyNDLAUser.table}".execute() } def resetSequences(implicit session: DBSession): Try[Unit] = Try { - sql"alter sequence my_ndla_users_id_seq restart with 1".execute(): Unit + val _ = sql"alter sequence my_ndla_users_id_seq restart with 1".execute() } def userWithFeideId(feideId: FeideID)(implicit session: DBSession = ReadOnlyAutoSession): Try[Option[MyNDLAUser]] = diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/service/ConfigService.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/service/ConfigService.scala index a20b22d024..d5a61dd59d 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/service/ConfigService.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/service/ConfigService.scala @@ -22,7 +22,7 @@ import scala.util.{Failure, Success, Try} trait ConfigService { this: ConfigRepository & Clock => - val configService: ConfigService + lazy val configService: ConfigService class ConfigService { diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/service/FolderConverterService.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/service/FolderConverterService.scala index de34e8ffa0..22f2768728 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/service/FolderConverterService.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/service/FolderConverterService.scala @@ -12,7 +12,7 @@ import cats.implicits.* import com.typesafe.scalalogging.StrictLogging import no.ndla.common.{Clock, model} import no.ndla.common.errors.ValidationException -import no.ndla.common.implicits.{OptionImplicit, TryQuestionMark} +import no.ndla.common.implicits.* import no.ndla.common.model.api.myndla.UpdatedMyNDLAUserDTO import no.ndla.common.model.domain.myndla import no.ndla.common.model.domain.myndla.{ @@ -34,7 +34,7 @@ import scala.util.{Failure, Success, Try} trait FolderConverterService { this: Clock & NodeBBClient => - val folderConverterService: FolderConverterService + lazy val folderConverterService: FolderConverterService class FolderConverterService extends StrictLogging { def toApiFolder( @@ -273,7 +273,7 @@ trait FolderConverterService { updatedUser: UpdatedMyNDLAUserDTO, updaterToken: Option[TokenUser], feideToken: Option[FeideAccessToken] - ): Try[DomainMyNDLAUser] = { + ): Try[DomainMyNDLAUser] = permitTry { val favoriteSubjects = updatedUser.favoriteSubjects.getOrElse(domainUserData.favoriteSubjects) val arenaEnabled = { if (updaterToken.hasPermission(LEARNINGPATH_API_ADMIN)) diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/service/FolderReadService.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/service/FolderReadService.scala index e8dd9fb374..3181af5ce0 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/service/FolderReadService.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/service/FolderReadService.scala @@ -11,7 +11,7 @@ package no.ndla.myndlaapi.service import cats.implicits.* import no.ndla.common.Clock import no.ndla.common.errors.NotFoundException -import no.ndla.common.implicits.TryQuestionMark +import no.ndla.common.implicits.* import no.ndla.common.model.api.SingleResourceStatsDTO import no.ndla.common.model.api.myndla.MyNDLAUserDTO import no.ndla.common.model.domain.TryMaybe.* @@ -35,7 +35,7 @@ trait FolderReadService { this: FolderConverterService & FolderRepository & UserRepository & FeideApiClient & Clock & ConfigService & UserService & DBUtility & LearningPathApiClient & RobotRepository => - val folderReadService: FolderReadService + lazy val folderReadService: FolderReadService class FolderReadService { private def getSubFoldersAndResources( @@ -345,7 +345,7 @@ trait FolderReadService { def getFavouriteStatsForResource( resourceIds: List[String], resourceTypes: List[String] - ): Try[List[SingleResourceStatsDTO]] = { + ): Try[List[SingleResourceStatsDTO]] = permitTry { implicit val session: DBSession = folderRepository.getSession(true) val result = diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/service/FolderWriteService.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/service/FolderWriteService.scala index 750e256a8d..655c68309b 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/service/FolderWriteService.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/service/FolderWriteService.scala @@ -56,7 +56,7 @@ trait FolderWriteService { this: FolderReadService & Clock & FeideApiClient & FolderRepository & FolderConverterService & UserRepository & ConfigService & UserService & SearchApiClient & DBUtility => - val folderWriteService: FolderWriteService + lazy val folderWriteService: FolderWriteService class FolderWriteService extends StrictLogging { val MaxFolderDepth = 5L @@ -323,9 +323,9 @@ trait FolderWriteService { session: DBSession ): Try[UUID] = { folderRepository.folderResourceConnectionCount(resourceId) match { - case Failure(exception) => Failure(exception) - case Success(count) if count == 1 => folderRepository.deleteResource(resourceId) - case Success(_) => folderRepository.deleteFolderResourceConnection(folderId, resourceId) + case Failure(exception) => Failure(exception) + case Success(count) if count == 1L => folderRepository.deleteResource(resourceId) + case Success(_) => folderRepository.deleteFolderResourceConnection(folderId, resourceId) } } @@ -466,7 +466,7 @@ trait FolderWriteService { folderSortObject: domain.FolderSortObject, sortRequest: api.FolderSortRequestDTO, feideAccessToken: Option[FeideAccessToken] - ): Try[Unit] = { + ): Try[Unit] = permitTry { implicit val session: DBSession = folderRepository.getSession(readOnly = false) val feideId = feideApiClient.getFeideID(feideAccessToken).? canWriteDuringMyNDLAWriteRestrictionsOrAccessDenied(feideId, feideAccessToken).?? @@ -577,8 +577,7 @@ trait FolderWriteService { isCloning: Boolean )(implicit session: DBSession - ): Try[domain.Folder] = { - + ): Try[domain.Folder] = permitTry { val parentId = getMaybeParentId(newFolder.parentId).? val maybeSiblings = getFolderWithDirectChildren(parentId, feideId).? val nextRank = getNextRank(maybeSiblings.childrenFolders) diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/service/RobotService.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/service/RobotService.scala index f68faef4b1..0ea8c090d4 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/service/RobotService.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/service/RobotService.scala @@ -10,7 +10,7 @@ package no.ndla.myndlaapi.service import no.ndla.common.Clock import no.ndla.common.errors.NotFoundException -import no.ndla.common.implicits.OptionImplicit +import no.ndla.common.implicits.* import no.ndla.database.DBUtility import no.ndla.myndlaapi.model.api.robot.{CreateRobotDefinitionDTO, ListOfRobotDefinitionsDTO, RobotDefinitionDTO} import no.ndla.myndlaapi.model.domain.{RobotConfiguration, RobotDefinition, RobotStatus} @@ -24,7 +24,7 @@ import scala.util.Try trait RobotService { this: RobotRepository & DBUtility & Clock & FeideApiClient & FolderWriteService => - val robotService: RobotService + lazy val robotService: RobotService class RobotService { diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/service/UserService.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/service/UserService.scala index 48719a5cbb..c31eeb1695 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/service/UserService.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/service/UserService.scala @@ -10,7 +10,7 @@ package no.ndla.myndlaapi.service import no.ndla.common.Clock import no.ndla.common.errors.{AccessDeniedException, NotFoundException} -import no.ndla.common.implicits.TryQuestionMark +import no.ndla.common.implicits.* import no.ndla.common.model.api.myndla import no.ndla.common.model.api.myndla.UpdatedMyNDLAUserDTO import no.ndla.common.model.domain.myndla.{MyNDLAGroup, MyNDLAUser, MyNDLAUserDocument, UserRole} @@ -27,7 +27,7 @@ trait UserService { this: FeideApiClient & FolderConverterService & ConfigService & UserRepository & Clock & FolderWriteService & NodeBBClient & FolderRepository & DBUtility => - val userService: UserService + lazy val userService: UserService class UserService { def getMyNdlaUserDataDomain( @@ -170,7 +170,7 @@ trait UserService { userData: MyNDLAUser )(implicit session: DBSession - ): Try[MyNDLAUser] = { + ): Try[MyNDLAUser] = permitTry { val feideUser = feideApiClient.getFeideExtendedUser(feideAccessToken).? val organization = feideApiClient.getOrganization(feideAccessToken).? val feideGroups = feideApiClient.getFeideGroups(feideAccessToken).? diff --git a/myndla-api/src/test/scala/no/ndla/myndlaapi/TestEnvironment.scala b/myndla-api/src/test/scala/no/ndla/myndlaapi/TestEnvironment.scala index b0ea257992..e3eda2603a 100644 --- a/myndla-api/src/test/scala/no/ndla/myndlaapi/TestEnvironment.scala +++ b/myndla-api/src/test/scala/no/ndla/myndlaapi/TestEnvironment.scala @@ -68,34 +68,34 @@ trait TestEnvironment with SearchApiClient with LearningPathApiClient with NdlaClient { - lazy val props = new MyNdlaApiProperties - lazy val clock: SystemClock = mock[SystemClock] - val dataSource: HikariDataSource = mock[HikariDataSource] - val migrator: DBMigrator = mock[DBMigrator] - val folderRepository: FolderRepository = mock[FolderRepository] - val robotRepository: RobotRepository = mock[RobotRepository] - val folderReadService: FolderReadService = mock[FolderReadService] - val folderWriteService: FolderWriteService = mock[FolderWriteService] - val folderConverterService: FolderConverterService = mock[FolderConverterService] - val robotService: RobotService = mock[RobotService] - val userService: UserService = mock[UserService] - val configService: ConfigService = mock[ConfigService] - val userRepository: UserRepository = mock[UserRepository] - val configRepository: ConfigRepository = mock[ConfigRepository] - val feideApiClient: FeideApiClient = mock[FeideApiClient] - val configController: ConfigController = mock[ConfigController] - val robotController: RobotController = mock[RobotController] - val redisClient: RedisClient = mock[RedisClient] - val folderController: FolderController = mock[FolderController] - val userController: UserController = mock[UserController] - val statsController: StatsController = mock[StatsController] - val healthController: TapirHealthController = mock[TapirHealthController] - val nodebb: NodeBBClient = mock[NodeBBClient] - val searchApiClient: SearchApiClient = mock[SearchApiClient] - val learningPathApiClient: LearningPathApiClient = mock[LearningPathApiClient] - val ndlaClient: NdlaClient = mock[NdlaClient] - val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] - val DBUtil: DBUtility = mock[DBUtility] + override lazy val props = new MyNdlaApiProperties + override lazy val clock: SystemClock = mock[SystemClock] + override lazy val dataSource: HikariDataSource = mock[HikariDataSource] + override lazy val migrator: DBMigrator = mock[DBMigrator] + override lazy val folderRepository: FolderRepository = mock[FolderRepository] + override lazy val robotRepository: RobotRepository = mock[RobotRepository] + override lazy val folderReadService: FolderReadService = mock[FolderReadService] + override lazy val folderWriteService: FolderWriteService = mock[FolderWriteService] + override lazy val folderConverterService: FolderConverterService = mock[FolderConverterService] + override lazy val robotService: RobotService = mock[RobotService] + override lazy val userService: UserService = mock[UserService] + override lazy val configService: ConfigService = mock[ConfigService] + override lazy val userRepository: UserRepository = mock[UserRepository] + override lazy val configRepository: ConfigRepository = mock[ConfigRepository] + override lazy val feideApiClient: FeideApiClient = mock[FeideApiClient] + override lazy val configController: ConfigController = mock[ConfigController] + override lazy val robotController: RobotController = mock[RobotController] + override lazy val redisClient: RedisClient = mock[RedisClient] + override lazy val folderController: FolderController = mock[FolderController] + override lazy val userController: UserController = mock[UserController] + val statsController: StatsController = mock[StatsController] + override lazy val healthController: TapirHealthController = mock[TapirHealthController] + override lazy val nodebb: NodeBBClient = mock[NodeBBClient] + override lazy val searchApiClient: SearchApiClient = mock[SearchApiClient] + override lazy val learningPathApiClient: LearningPathApiClient = mock[LearningPathApiClient] + override lazy val ndlaClient: NdlaClient = mock[NdlaClient] + override lazy val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] + override lazy val DBUtil: DBUtility = mock[DBUtility] def services: List[TapirController] = List.empty val swagger: SwaggerController = mock[SwaggerController] diff --git a/myndla-api/src/test/scala/no/ndla/myndlaapi/repository/ConfigRepositoryTest.scala b/myndla-api/src/test/scala/no/ndla/myndlaapi/repository/ConfigRepositoryTest.scala index 1beb5daae4..c1b6618629 100644 --- a/myndla-api/src/test/scala/no/ndla/myndlaapi/repository/ConfigRepositoryTest.scala +++ b/myndla-api/src/test/scala/no/ndla/myndlaapi/repository/ConfigRepositoryTest.scala @@ -18,9 +18,9 @@ import scalikejdbc.* import scala.util.Success class ConfigRepositoryTest extends DatabaseIntegrationSuite with UnitSuite with TestEnvironment { - override val schemaName: String = "myndlaapi_test" - override val dataSource: HikariDataSource = testDataSource.get - override val migrator = new DBMigrator + override val schemaName: String = "myndlaapi_test" + override lazy val dataSource: HikariDataSource = testDataSource.get + override lazy val migrator = new DBMigrator var repository: ConfigRepository = _ diff --git a/myndla-api/src/test/scala/no/ndla/myndlaapi/repository/FolderRepositoryTest.scala b/myndla-api/src/test/scala/no/ndla/myndlaapi/repository/FolderRepositoryTest.scala index 0e7bd00ab9..eaad6e0236 100644 --- a/myndla-api/src/test/scala/no/ndla/myndlaapi/repository/FolderRepositoryTest.scala +++ b/myndla-api/src/test/scala/no/ndla/myndlaapi/repository/FolderRepositoryTest.scala @@ -25,11 +25,11 @@ import java.util.UUID import scala.util.{Success, Try} class FolderRepositoryTest extends DatabaseIntegrationSuite with UnitSuite with TestEnvironment { - override val dataSource: HikariDataSource = testDataSource.get - override val migrator: DBMigrator = new DBMigrator - var repository: FolderRepository = _ - override val userRepository: UserRepository = new UserRepository - override val DBUtil: DBUtility = new DBUtility + override lazy val dataSource: HikariDataSource = testDataSource.get + override lazy val migrator: DBMigrator = new DBMigrator + var repository: FolderRepository = _ + override lazy val userRepository: UserRepository = new UserRepository + override lazy val DBUtil: DBUtility = new DBUtility def emptyTestDatabase: Boolean = { DB autoCommit (implicit session => { diff --git a/myndla-api/src/test/scala/no/ndla/myndlaapi/repository/RobotRepositoryTest.scala b/myndla-api/src/test/scala/no/ndla/myndlaapi/repository/RobotRepositoryTest.scala index 1aef7d1128..b24de62fc6 100644 --- a/myndla-api/src/test/scala/no/ndla/myndlaapi/repository/RobotRepositoryTest.scala +++ b/myndla-api/src/test/scala/no/ndla/myndlaapi/repository/RobotRepositoryTest.scala @@ -21,10 +21,10 @@ import java.util.UUID import scala.util.{Success, Try} class RobotRepositoryTest extends DatabaseIntegrationSuite with UnitSuite with TestEnvironment { - override val dataSource: HikariDataSource = testDataSource.get - override val migrator: DBMigrator = new DBMigrator - var repository: RobotRepository = _ - override val DBUtil: DBUtility = new DBUtility + override lazy val dataSource: HikariDataSource = testDataSource.get + override lazy val migrator: DBMigrator = new DBMigrator + var repository: RobotRepository = _ + override lazy val DBUtil: DBUtility = new DBUtility def emptyTestDatabase: Boolean = { DB autoCommit (implicit session => { diff --git a/myndla-api/src/test/scala/no/ndla/myndlaapi/service/ConfigServiceTest.scala b/myndla-api/src/test/scala/no/ndla/myndlaapi/service/ConfigServiceTest.scala index 35d44d51c2..8e55629878 100644 --- a/myndla-api/src/test/scala/no/ndla/myndlaapi/service/ConfigServiceTest.scala +++ b/myndla-api/src/test/scala/no/ndla/myndlaapi/service/ConfigServiceTest.scala @@ -44,7 +44,7 @@ class ConfigServiceTest extends UnitTestSuite with TestEnvironment { ConfigKey.LearningpathWriteRestricted, ConfigMetaValueDTO(true), TokenUser("Kari", Set(LEARNINGPATH_API_PUBLISH), None) - ) + ): @unchecked ex.isInstanceOf[AccessDeniedException] should be(true) } @@ -55,7 +55,7 @@ class ConfigServiceTest extends UnitTestSuite with TestEnvironment { ConfigKey.LearningpathWriteRestricted, ConfigMetaValueDTO(true), TokenUser("Kari", Set(LEARNINGPATH_API_ADMIN), None) - ) + ): @unchecked } test("That validation fails if IsWriteRestricted is not a boolean") { @@ -65,7 +65,7 @@ class ConfigServiceTest extends UnitTestSuite with TestEnvironment { ConfigKey.LearningpathWriteRestricted, ConfigMetaValueDTO(List("123")), TokenUser("Kari", Set(LEARNINGPATH_API_ADMIN), None) - ) + ): @unchecked ex.isInstanceOf[ValidationException] should be(true) } diff --git a/myndla-api/src/test/scala/no/ndla/myndlaapi/service/FolderConverterServiceTest.scala b/myndla-api/src/test/scala/no/ndla/myndlaapi/service/FolderConverterServiceTest.scala index d735ab6fe6..00cd7bc624 100644 --- a/myndla-api/src/test/scala/no/ndla/myndlaapi/service/FolderConverterServiceTest.scala +++ b/myndla-api/src/test/scala/no/ndla/myndlaapi/service/FolderConverterServiceTest.scala @@ -236,7 +236,7 @@ class FolderConverterServiceTest extends UnitTestSuite with TestEnvironment { List(api.BreadcrumbDTO(id = mainFolderUUID, name = "mainFolder")), None, true - ) + ): @unchecked result should be(expected) } diff --git a/myndla-api/src/test/scala/no/ndla/myndlaapi/service/FolderReadServiceTest.scala b/myndla-api/src/test/scala/no/ndla/myndlaapi/service/FolderReadServiceTest.scala index 87cead3e53..99dda1ce6b 100644 --- a/myndla-api/src/test/scala/no/ndla/myndlaapi/service/FolderReadServiceTest.scala +++ b/myndla-api/src/test/scala/no/ndla/myndlaapi/service/FolderReadServiceTest.scala @@ -29,8 +29,8 @@ import scala.util.{Failure, Success, Try} class FolderReadServiceTest extends UnitTestSuite with TestEnvironment { - val service = new FolderReadService - override val folderConverterService: FolderConverterService = org.mockito.Mockito.spy(new FolderConverterService) + val service = new FolderReadService + override lazy val folderConverterService: FolderConverterService = org.mockito.Mockito.spy(new FolderConverterService) override def beforeEach(): Unit = { super.beforeEach() @@ -40,7 +40,7 @@ class FolderReadServiceTest extends UnitTestSuite with TestEnvironment { doAnswer((i: InvocationOnMock) => { val func = i.getArgument[DBSession => Try[Nothing]](0) func(mock[DBSession]) - }).when(DBUtil).rollbackOnFailure(any) + }).when(DBUtil).rollbackOnFailure(any()) } test("That getSingleFolder returns folder and its data when user is the owner") { @@ -403,23 +403,23 @@ class FolderReadServiceTest extends UnitTestSuite with TestEnvironment { ) .thenReturn(Success(Some(folderWithId))) - val Failure(result: NotFoundException) = service.getSharedFolder(folderUUID, None) + val Failure(result: NotFoundException) = service.getSharedFolder(folderUUID, None): @unchecked result.message should be("Folder does not exist") } test("That getting stats fetches stats for my ndla usage") { - when(userRepository.usersGrouped()(any)).thenReturn(Success(Map(UserRole.EMPLOYEE -> 2, UserRole.STUDENT -> 3))) - when(folderRepository.numberOfFolders()(any)).thenReturn(Success(Some(10))) - when(folderRepository.numberOfResources()(any)).thenReturn(Success(Some(20))) - when(folderRepository.numberOfTags()(any)).thenReturn(Success(Some(10))) - when(userRepository.numberOfFavouritedSubjects()(any)).thenReturn(Success(Some(15))) - when(folderRepository.numberOfSharedFolders()(any)).thenReturn(Success(Some(5))) - when(learningPathApiClient.getStats).thenReturn(Success(LearningPathStatsDTO(25))) + when(userRepository.usersGrouped()(any)).thenReturn(Success(Map(UserRole.EMPLOYEE -> 2L, UserRole.STUDENT -> 3L))) + when(folderRepository.numberOfFolders()(any)).thenReturn(Success(Some(10L))) + when(folderRepository.numberOfResources()(any)).thenReturn(Success(Some(20L))) + when(folderRepository.numberOfTags()(any)).thenReturn(Success(Some(10L))) + when(userRepository.numberOfFavouritedSubjects()(any)).thenReturn(Success(Some(15L))) + when(folderRepository.numberOfSharedFolders()(any)).thenReturn(Success(Some(5L))) + when(learningPathApiClient.getStats).thenReturn(Success(LearningPathStatsDTO(25L))) when(folderRepository.numberOfResourcesGrouped()(any)) - .thenReturn(Success(List((1, "article"), (2, "learningpath"), (3, "video")))) - when(folderRepository.numberOfUsersWithFavourites(any)).thenReturn(Success(Some(3))) - when(folderRepository.numberOfUsersWithoutFavourites(any)).thenReturn(Success(Some(2))) - when(userRepository.numberOfUsersInArena(any)).thenReturn(Success(Some(4))) + .thenReturn(Success(List((1L, "article"), (2L, "learningpath"), (3L, "video")))) + when(folderRepository.numberOfUsersWithFavourites(any)).thenReturn(Success(Some(3L))) + when(folderRepository.numberOfUsersWithoutFavourites(any)).thenReturn(Success(Some(2L))) + when(userRepository.numberOfUsersInArena(any)).thenReturn(Success(Some(4L))) service.getStats.unsafeGet should be( api.StatsDTO( diff --git a/myndla-api/src/test/scala/no/ndla/myndlaapi/service/FolderWriteServiceTest.scala b/myndla-api/src/test/scala/no/ndla/myndlaapi/service/FolderWriteServiceTest.scala index 10931f3c32..a8b002329e 100644 --- a/myndla-api/src/test/scala/no/ndla/myndlaapi/service/FolderWriteServiceTest.scala +++ b/myndla-api/src/test/scala/no/ndla/myndlaapi/service/FolderWriteServiceTest.scala @@ -32,8 +32,8 @@ class FolderWriteServiceTest extends UnitTestSuite with TestEnvironment { val MaxFolderDepth = 5L - val service = new FolderWriteService - override val folderConverterService: FolderConverterService = spy(new FolderConverterService) + val service = new FolderWriteService + override lazy val folderConverterService: FolderConverterService = spy(new FolderConverterService) override def beforeEach(): Unit = { super.beforeEach() @@ -42,7 +42,7 @@ class FolderWriteServiceTest extends UnitTestSuite with TestEnvironment { doAnswer((i: InvocationOnMock) => { val func = i.getArgument[DBSession => Try[Nothing]](0) func(mock[DBSession]) - }).when(DBUtil).rollbackOnFailure(any) + }).when(DBUtil).rollbackOnFailure(any()) } test("that a user without access cannot delete a folder") { @@ -59,7 +59,7 @@ class FolderWriteServiceTest extends UnitTestSuite with TestEnvironment { when(folderRepository.foldersWithFeideAndParentID(any, any)(any)).thenReturn(Success(List.empty)) when(feideApiClient.getFeideID(any)).thenReturn(Success(wrongFeideId)) when(userService.getOrCreateMyNDLAUserIfNotExist(any, any)(any)).thenReturn(Success(emptyMyNDLAUser)) - when(folderRepository.folderResourceConnectionCount(any)(any)).thenReturn(Success(0)) + when(folderRepository.folderResourceConnectionCount(any)(any)).thenReturn(Success(0L)) when(folderRepository.folderWithId(eqTo(id))(any)).thenReturn(Success(folderWithChildren)) val x = service.deleteFolder(id, Some("token")) @@ -90,14 +90,14 @@ class FolderWriteServiceTest extends UnitTestSuite with TestEnvironment { ) val correctFeideId = "FEIDE" - when(folderRepository.withTx(any[DBSession => Try[Unit]])).thenAnswer((i: InvocationOnMock) => { + when(folderRepository.withTx(any[DBSession => Try[Unit]]())).thenAnswer((i: InvocationOnMock) => { val func = i.getArgument[DBSession => Try[Unit]](0) func(mock[DBSession]) }) when(folderRepository.foldersWithFeideAndParentID(any, any)(any)).thenReturn(Success(List.empty)) when(feideApiClient.getFeideID(any)).thenReturn(Success(correctFeideId)) when(userService.getOrCreateMyNDLAUserIfNotExist(any, any)(any)).thenReturn(Success(emptyMyNDLAUser)) - when(folderRepository.folderResourceConnectionCount(any)(any[DBSession])).thenReturn(Success(1)) + when(folderRepository.folderResourceConnectionCount(any)(any[DBSession])).thenReturn(Success(1L)) when(folderRepository.folderWithId(eqTo(mainFolderId))(any)).thenReturn(Success(folder)) when(folderReadService.getSingleFolderWithContent(eqTo(folder.id), any, eqTo(true))(any)) .thenReturn(Success(folderWithChildren)) @@ -140,14 +140,14 @@ class FolderWriteServiceTest extends UnitTestSuite with TestEnvironment { ) val correctFeideId = "FEIDE" - when(folderRepository.withTx(any[DBSession => Try[Unit]])).thenAnswer((i: InvocationOnMock) => { + when(folderRepository.withTx(any[DBSession => Try[Unit]]())).thenAnswer((i: InvocationOnMock) => { val func = i.getArgument[DBSession => Try[Unit]](0) func(mock[DBSession]) }) when(folderRepository.foldersWithFeideAndParentID(any, any)(any)).thenReturn(Success(List.empty)) when(feideApiClient.getFeideID(any)).thenReturn(Success(correctFeideId)) when(userService.getOrCreateMyNDLAUserIfNotExist(any, any)(any)).thenReturn(Success(emptyMyNDLAUser)) - when(folderRepository.folderResourceConnectionCount(eqTo(resourceId))(any)).thenReturn(Success(5)) + when(folderRepository.folderResourceConnectionCount(eqTo(resourceId))(any)).thenReturn(Success(5L)) when(folderRepository.folderWithId(eqTo(mainFolderId))(any)).thenReturn(Success(folder)) when(folderReadService.getSingleFolderWithContent(eqTo(folder.id), any, eqTo(true))(any)) .thenReturn(Success(folderWithChildren)) @@ -184,8 +184,8 @@ class FolderWriteServiceTest extends UnitTestSuite with TestEnvironment { when(userService.getOrCreateMyNDLAUserIfNotExist(any, any)(any)).thenReturn(Success(emptyMyNDLAUser)) when(folderRepository.folderWithId(eqTo(folderId))(any)).thenReturn(Success(folder)) when(folderRepository.resourceWithId(eqTo(resourceId))(any)).thenReturn(Success(resource)) - when(folderRepository.folderResourceConnectionCount(eqTo(resourceId))(any)).thenReturn(Success(2)) - when(folderRepository.withTx(any[DBSession => Try[Unit]])).thenAnswer((i: InvocationOnMock) => { + when(folderRepository.folderResourceConnectionCount(eqTo(resourceId))(any)).thenReturn(Success(2L)) + when(folderRepository.withTx(any[DBSession => Try[Unit]]())).thenAnswer((i: InvocationOnMock) => { val func = i.getArgument[DBSession => Try[Unit]](0) func(mock[DBSession]) }) @@ -217,11 +217,11 @@ class FolderWriteServiceTest extends UnitTestSuite with TestEnvironment { when(userService.getOrCreateMyNDLAUserIfNotExist(any, any)(any)).thenReturn(Success(emptyMyNDLAUser)) when(folderRepository.folderWithId(eqTo(folderId))(any)).thenReturn(Success(folder)) when(folderRepository.resourceWithId(eqTo(resourceId))(any)).thenReturn(Success(resource)) - when(folderRepository.folderResourceConnectionCount(eqTo(resourceId))(any)).thenReturn(Success(1)) + when(folderRepository.folderResourceConnectionCount(eqTo(resourceId))(any)).thenReturn(Success(1L)) when(folderRepository.deleteFolderResourceConnection(eqTo(folderId), eqTo(resourceId))(any)) .thenReturn(Success(resourceId)) when(folderRepository.deleteResource(eqTo(resourceId))(any)).thenReturn(Success(resourceId)) - when(folderRepository.withTx(any[DBSession => Try[Unit]])).thenAnswer((i: InvocationOnMock) => { + when(folderRepository.withTx(any[DBSession => Try[Unit]]())).thenAnswer((i: InvocationOnMock) => { val func = i.getArgument[DBSession => Try[Unit]](0) func(mock[DBSession]) }) @@ -446,16 +446,16 @@ class FolderWriteServiceTest extends UnitTestSuite with TestEnvironment { when(folderReadService.getSingleFolderWithContent(eqTo(folder1Id), eqTo(true), eqTo(true))(any[DBSession])) .thenReturn(Success(folder1)) when(folderRepository.folderResourceConnectionCount(eqTo(resource1Id))(any[DBSession])) - .thenReturn(Success(2), Success(1)) + .thenReturn(Success(2L), Success(1L)) when(folderRepository.deleteFolderResourceConnection(eqTo(folder1Id), eqTo(resource1Id))(any[DBSession])) .thenReturn(Success(resource1Id)) when(folderRepository.deleteResource(eqTo(resource1Id))(any[DBSession])).thenReturn(Success(resource1Id)) when(folderRepository.folderResourceConnectionCount(eqTo(resource2Id))(any[DBSession])) - .thenReturn(Success(2), Success(1)) + .thenReturn(Success(2L), Success(1L)) when(folderRepository.deleteFolderResourceConnection(eqTo(folder2Id), eqTo(resource2Id))(any[DBSession])) .thenReturn(Success(resource2Id)) when(folderRepository.deleteResource(eqTo(resource2Id))(any[DBSession])).thenReturn(Success(resource2Id)) - when(folderRepository.folderResourceConnectionCount(eqTo(resource3Id))(any[DBSession])).thenReturn(Success(1)) + when(folderRepository.folderResourceConnectionCount(eqTo(resource3Id))(any[DBSession])).thenReturn(Success(1L)) when(folderRepository.deleteResource(eqTo(resource3Id))(any[DBSession])).thenReturn(Success(resource3Id)) when(folderRepository.deleteFolder(eqTo(folder3Id))(any[DBSession])).thenReturn(Success(folder3Id)) when(folderRepository.deleteFolder(eqTo(folder2Id))(any[DBSession])).thenReturn(Success(folder2Id)) @@ -463,13 +463,14 @@ class FolderWriteServiceTest extends UnitTestSuite with TestEnvironment { when(folderRepository.deleteFolderUserConnection(eqTo(Some(folder3Id)), eqTo(None))(any)).thenReturn(Success(0)) when(folderRepository.deleteFolderUserConnection(eqTo(Some(folder2Id)), eqTo(None))(any)).thenReturn(Success(0)) when(folderRepository.deleteFolderUserConnection(eqTo(Some(folder1Id)), eqTo(None))(any)).thenReturn(Success(0)) - when(folderRepository.withTx(any[DBSession => Try[Unit]])).thenAnswer((i: InvocationOnMock) => { + when(folderRepository.withTx(any[DBSession => Try[Unit]]())).thenAnswer((i: InvocationOnMock) => { val func = i.getArgument[DBSession => Try[Unit]](0) func(mock[DBSession]) }) when(folderRepository.foldersWithFeideAndParentID(any, any)(any)).thenReturn(Success(List.empty)) - service.deleteFolder(folder1Id, Some("FEIDEF")) should be(Success(folder1Id)) + val result = service.deleteFolder(folder1Id, Some("FEIDEF")) + result should be(Success(folder1Id)) verify(folderReadService, times(1)).getSingleFolderWithContent(eqTo(folder1Id), eqTo(true), eqTo(true))(any) verify(folderRepository, times(5)).folderResourceConnectionCount(any)(any) @@ -513,7 +514,7 @@ class FolderWriteServiceTest extends UnitTestSuite with TestEnvironment { when(folderRepository.getFoldersDepth(eqTo(parentId))(any[DBSession])).thenReturn(Success(MaxFolderDepth)) when(folderRepository.getConnections(any)(any)).thenReturn(Success(List.empty)) - val Failure(result: ValidationException) = service.newFolder(newFolder, Some(feideId)) + val Failure(result: ValidationException) = service.newFolder(newFolder, Some(feideId)): @unchecked result.errors.head.message should be( s"Folder can not be created, max folder depth limit of $MaxFolderDepth reached." ) @@ -825,7 +826,7 @@ class FolderWriteServiceTest extends UnitTestSuite with TestEnvironment { ) ) - when(folderRepository.withTx(any[DBSession => Try[Unit]])).thenAnswer((i: InvocationOnMock) => { + when(folderRepository.withTx(any[DBSession => Try[Unit]]())).thenAnswer((i: InvocationOnMock) => { val func = i.getArgument[DBSession => Try[Unit]](0) func(mock[DBSession]) }) @@ -967,7 +968,8 @@ class FolderWriteServiceTest extends UnitTestSuite with TestEnvironment { when(userService.getOrCreateMyNDLAUserIfNotExist(any, any)(any)).thenReturn(Success(myNDLAUser)) val updatedFolder = api.UpdatedFolderDTO(name = None, status = Some("shared"), description = None) - val Failure(result) = service.isOperationAllowedOrAccessDenied("feideid", Some("accesstoken"), updatedFolder) + val Failure(result) = + service.isOperationAllowedOrAccessDenied("feideid", Some("accesstoken"), updatedFolder): @unchecked result.getMessage should be("You do not have necessary permissions to share folders.") } @@ -979,7 +981,8 @@ class FolderWriteServiceTest extends UnitTestSuite with TestEnvironment { when(configService.isMyNDLAWriteRestricted).thenReturn(Success(true)) val updatedFolder = api.UpdatedFolderDTO(name = Some("asd"), status = None, description = None) - val Failure(result) = service.isOperationAllowedOrAccessDenied("feideid", Some("accesstoken"), updatedFolder) + val Failure(result) = + service.isOperationAllowedOrAccessDenied("feideid", Some("accesstoken"), updatedFolder): @unchecked result.getMessage should be("You do not have write access while write restriction is active.") } diff --git a/myndla-api/src/test/scala/no/ndla/myndlaapi/service/UserServiceTest.scala b/myndla-api/src/test/scala/no/ndla/myndlaapi/service/UserServiceTest.scala index f8432db592..32e905db78 100644 --- a/myndla-api/src/test/scala/no/ndla/myndlaapi/service/UserServiceTest.scala +++ b/myndla-api/src/test/scala/no/ndla/myndlaapi/service/UserServiceTest.scala @@ -25,8 +25,8 @@ import scala.util.{Failure, Success, Try} class UserServiceTest extends UnitTestSuite with TestEnvironment { - val service: UserService = spy(new UserService) - override val folderConverterService: FolderConverterService = spy(new FolderConverterService) + val service: UserService = spy(new UserService) + override lazy val folderConverterService: FolderConverterService = spy(new FolderConverterService) override def beforeEach(): Unit = { super.beforeEach() @@ -145,7 +145,7 @@ class UserServiceTest extends UnitTestSuite with TestEnvironment { doAnswer((i: InvocationOnMock) => { val func = i.getArgument[DBSession => Try[Nothing]](0) func(mock[DBSession]) - }).when(DBUtil).rollbackOnFailure(any) + }).when(DBUtil).rollbackOnFailure(any()) when(userRepository.reserveFeideIdIfNotExists(any)(any)).thenReturn(Success(false)) val feideId = "feide" @@ -227,7 +227,7 @@ class UserServiceTest extends UnitTestSuite with TestEnvironment { doAnswer((i: InvocationOnMock) => { val func = i.getArgument[DBSession => Try[Nothing]](0) func(mock[DBSession]) - }).when(DBUtil).rollbackOnFailure(any) + }).when(DBUtil).rollbackOnFailure(any()) when(userRepository.reserveFeideIdIfNotExists(any)(any)).thenReturn(Success(true)) val feideId = "feide" @@ -285,7 +285,7 @@ class UserServiceTest extends UnitTestSuite with TestEnvironment { doAnswer((i: InvocationOnMock) => { val func = i.getArgument[DBSession => Try[Nothing]](0) func(mock[DBSession]) - }).when(DBUtil).rollbackOnFailure(any) + }).when(DBUtil).rollbackOnFailure(any()) when(userRepository.reserveFeideIdIfNotExists(any)(any)).thenReturn(Success(true)) val feideId = "feide" diff --git a/network/src/main/scala/no/ndla/network/NdlaClient.scala b/network/src/main/scala/no/ndla/network/NdlaClient.scala index 84f1003fff..71816e0df9 100644 --- a/network/src/main/scala/no/ndla/network/NdlaClient.scala +++ b/network/src/main/scala/no/ndla/network/NdlaClient.scala @@ -19,13 +19,13 @@ import sttp.client3.quick.* import scala.util.{Failure, Success, Try} trait NdlaClient { - val ndlaClient: NdlaClient + lazy val ndlaClient: NdlaClient class NdlaClient { val client: SimpleHttpClient = simpleHttpClient private val ResponseErrorBodyCharacterCutoff = 1000 - def fetch[A: Decoder](request: NdlaRequest): Try[A] = { + def fetch[A](request: NdlaRequest)(implicit decoder: Decoder[A]): Try[A] = { doFetch(addCorrelationId(request)) } diff --git a/network/src/main/scala/no/ndla/network/clients/FeideApiClient.scala b/network/src/main/scala/no/ndla/network/clients/FeideApiClient.scala index 466245626c..7d932b5892 100644 --- a/network/src/main/scala/no/ndla/network/clients/FeideApiClient.scala +++ b/network/src/main/scala/no/ndla/network/clients/FeideApiClient.scala @@ -86,7 +86,7 @@ object FeideExtendedUserInfo { trait FeideApiClient { this: RedisClient => - val feideApiClient: FeideApiClient + lazy val feideApiClient: FeideApiClient class FeideApiClient extends StrictLogging { diff --git a/network/src/main/scala/no/ndla/network/clients/FrontpageApiClient.scala b/network/src/main/scala/no/ndla/network/clients/FrontpageApiClient.scala index 4dde218701..9e6af8d6c7 100644 --- a/network/src/main/scala/no/ndla/network/clients/FrontpageApiClient.scala +++ b/network/src/main/scala/no/ndla/network/clients/FrontpageApiClient.scala @@ -18,7 +18,7 @@ import scala.util.Try trait FrontpageApiClient { this: HasBaseProps & NdlaClient => - val frontpageApiClient: FrontpageApiClient + lazy val frontpageApiClient: FrontpageApiClient class FrontpageApiClient { val timeout: FiniteDuration = 15.seconds diff --git a/network/src/main/scala/no/ndla/network/clients/MyNDLAApiClient.scala b/network/src/main/scala/no/ndla/network/clients/MyNDLAApiClient.scala index fa1fb562db..e232211ec6 100644 --- a/network/src/main/scala/no/ndla/network/clients/MyNDLAApiClient.scala +++ b/network/src/main/scala/no/ndla/network/clients/MyNDLAApiClient.scala @@ -23,7 +23,7 @@ import scala.util.{Failure, Success, Try} trait MyNDLAApiClient { this: HasBaseProps & NdlaClient => - val myndlaApiClient: MyNDLAApiClient + lazy val myndlaApiClient: MyNDLAApiClient class MyNDLAApiClient { private val statsEndpoint = s"http://${props.MyNDLAApiHost}/myndla-api/v1/stats" diff --git a/network/src/main/scala/no/ndla/network/clients/RedisClient.scala b/network/src/main/scala/no/ndla/network/clients/RedisClient.scala index 26f109aef6..0abd0b8c10 100644 --- a/network/src/main/scala/no/ndla/network/clients/RedisClient.scala +++ b/network/src/main/scala/no/ndla/network/clients/RedisClient.scala @@ -18,7 +18,7 @@ import scala.util.{Failure, Success, Try} trait RedisClient { this: HasBaseProps => - val redisClient: RedisClient + lazy val redisClient: RedisClient class RedisClient( host: String, port: Int, @@ -37,7 +37,7 @@ trait RedisClient { Success(newExpireTime) } - private def updateCache(accessToken: FeideAccessToken, field: String, data: String): Try[_] = { + private def updateCache(accessToken: FeideAccessToken, field: String, data: String): Try[?] = { for { newExpireTime <- getKeyExpireTime(accessToken) _ <- jedis.hset(accessToken, field, data) diff --git a/network/src/main/scala/no/ndla/network/clients/SearchApiClient.scala b/network/src/main/scala/no/ndla/network/clients/SearchApiClient.scala index a3b143f76d..89b53f6d83 100644 --- a/network/src/main/scala/no/ndla/network/clients/SearchApiClient.scala +++ b/network/src/main/scala/no/ndla/network/clients/SearchApiClient.scala @@ -26,7 +26,7 @@ import scala.util.{Failure, Success, Try} trait SearchApiClient { this: HasBaseProps & NdlaClient => - val searchApiClient: SearchApiClient + lazy val searchApiClient: SearchApiClient class SearchApiClient(SearchApiBaseUrl: String = s"http://${props.SearchApiHost}") extends StrictLogging { private val InternalEndpoint = s"$SearchApiBaseUrl/intern" diff --git a/network/src/main/scala/no/ndla/network/tapir/NdlaTapirMain.scala b/network/src/main/scala/no/ndla/network/tapir/NdlaTapirMain.scala index 263633df6c..ad52533207 100644 --- a/network/src/main/scala/no/ndla/network/tapir/NdlaTapirMain.scala +++ b/network/src/main/scala/no/ndla/network/tapir/NdlaTapirMain.scala @@ -19,7 +19,7 @@ import sttp.tapir.server.jdkhttp.HttpServer import scala.concurrent.Future import scala.concurrent.duration.{Duration, DurationInt} import scala.io.Source -import scala.util.Try +import scala.util.{Try, boundary} trait NdlaTapirMain[T <: TapirApplication] { val logger: Logger = getLogger @@ -45,7 +45,7 @@ trait NdlaTapirMain[T <: TapirApplication] { private def performWarmup(): Unit = if (!props.disableWarmup) { import scala.concurrent.ExecutionContext.Implicits.global - Future { + val fut = Future { // Since we don't really have a good way to check whether server is ready lets just wait a bit Thread.sleep(500) val warmupStart = System.currentTimeMillis() @@ -55,19 +55,21 @@ trait NdlaTapirMain[T <: TapirApplication] { val warmupTime = System.currentTimeMillis() - warmupStart logger.info(s"Warmup procedure finished in ${warmupTime}ms.") - }: Unit + } + + fut: Unit } else { componentRegistry.healthController.setWarmedUp() } - private def waitForActiveRequests(timeout: Duration): Unit = { + private def waitForActiveRequests(timeout: Duration): Unit = boundary { val startTime = System.currentTimeMillis() val activeRequests = componentRegistry.Routes.activeRequests.get() logTaskTime(s"Waiting for $activeRequests active requests to finish...", timeout, logTaskStart = true) { while (componentRegistry.Routes.activeRequests.get() > 0) { if (System.currentTimeMillis() - startTime > timeout.toMillis) { logger.warn(s"Timeout of $timeout reached while waiting for active requests to finish.") - return + boundary.break() } Thread.sleep(100) } @@ -96,10 +98,7 @@ trait NdlaTapirMain[T <: TapirApplication] { * than the default logger shutdown handler will allow */ private def shutdownLogger(): Unit = { - LoggerContext.getContext(false) match { - case context: LoggerContext => context.stop() - case _ => println("No LoggerContext found, cannot stop logging context.") - } + LoggerContext.getContext(false).stop() } private def runServer(): Try[Unit] = { diff --git a/network/src/main/scala/no/ndla/network/tapir/NonEmptyString.scala b/network/src/main/scala/no/ndla/network/tapir/NonEmptyString.scala index ec24b2a09e..ea307dfe94 100644 --- a/network/src/main/scala/no/ndla/network/tapir/NonEmptyString.scala +++ b/network/src/main/scala/no/ndla/network/tapir/NonEmptyString.scala @@ -28,6 +28,9 @@ class NonEmptyString private (val underlying: String) { object NonEmptyString { def apply(underlying: String): Option[NonEmptyString] = fromString(underlying) + given CanEqual[NonEmptyString, String] = CanEqual.derived + given CanEqual[String, NonEmptyString] = CanEqual.derived + private def validateString(underlying: String): Boolean = underlying.trim.nonEmpty def fromOptString(s: Option[String]): Option[NonEmptyString] = diff --git a/network/src/main/scala/no/ndla/network/tapir/Routes.scala b/network/src/main/scala/no/ndla/network/tapir/Routes.scala index 1d5390ffd9..0826e7f556 100644 --- a/network/src/main/scala/no/ndla/network/tapir/Routes.scala +++ b/network/src/main/scala/no/ndla/network/tapir/Routes.scala @@ -169,7 +169,7 @@ trait Routes { req.attribute(requestBody).foreach { body => if (body.nonEmpty) { val requestBodyStr = new String(body, UTF_8) - MDC.put("requestBody", requestBodyStr) + MDC.put("requestBody", requestBodyStr): Unit } } @@ -203,7 +203,7 @@ trait Routes { if (code >= 500) logger.error(s) else logger.info(s) - activeRequests.decrementAndGet() + activeRequests.decrementAndGet(): Unit } RequestInfo.clear() @@ -247,6 +247,8 @@ trait Routes { val endpoints = services.flatMap(_.builtEndpoints) + logger.info(s"Starting $name on port $port") + val server = JdkHttpServer() .options(options) .executor(executor) @@ -255,8 +257,6 @@ trait Routes { .port(port) .start() - logger.info(s"Starting $name on port $port") - warmupFunc server diff --git a/network/src/main/scala/no/ndla/network/tapir/SwaggerControllerConfig.scala b/network/src/main/scala/no/ndla/network/tapir/SwaggerControllerConfig.scala index 4605ff2609..f1e577110c 100644 --- a/network/src/main/scala/no/ndla/network/tapir/SwaggerControllerConfig.scala +++ b/network/src/main/scala/no/ndla/network/tapir/SwaggerControllerConfig.scala @@ -23,16 +23,15 @@ trait SwaggerControllerConfig { this: HasBaseProps & TapirController => class SwaggerController(services: List[TapirController], swaggerInfo: SwaggerInfo) extends TapirController { - import props.* - def getServices(): List[TapirController] = services :+ this val info: Info = Info( title = props.ApplicationName, version = "1.0", description = swaggerInfo.description.some, - termsOfService = TermsUrl.some, - contact = Contact(name = ContactName.some, url = ContactUrl.some, email = ContactEmail.some).some, + termsOfService = props.TermsUrl.some, + contact = + Contact(name = props.ContactName.some, url = props.ContactUrl.some, email = props.ContactEmail.some).some, license = License("GPL v3.0", "https://www.gnu.org/licenses/gpl-3.0.en.html".some).some ) diff --git a/network/src/main/scala/no/ndla/network/tapir/TapirHealthController.scala b/network/src/main/scala/no/ndla/network/tapir/TapirHealthController.scala index 1886802e26..06b1aef77a 100644 --- a/network/src/main/scala/no/ndla/network/tapir/TapirHealthController.scala +++ b/network/src/main/scala/no/ndla/network/tapir/TapirHealthController.scala @@ -16,7 +16,7 @@ import sttp.tapir.* trait TapirHealthController { this: TapirController => - val healthController: TapirHealthController + lazy val healthController: TapirHealthController class TapirHealthController extends Warmup with TapirController { @volatile private var isShuttingDown: Boolean = false override val enableSwagger: Boolean = false diff --git a/network/src/main/scala/no/ndla/network/tapir/TapirUtil.scala b/network/src/main/scala/no/ndla/network/tapir/TapirUtil.scala index 0036e0e33a..5954b2e37e 100644 --- a/network/src/main/scala/no/ndla/network/tapir/TapirUtil.scala +++ b/network/src/main/scala/no/ndla/network/tapir/TapirUtil.scala @@ -42,6 +42,6 @@ object TapirUtil { val variants = variantsForCodes(codesToGetVariantFor) val err = variants :+ internalServerErrorDefaultVariant - oneOf[AllErrors](err.head, err.tail: _*) + oneOf[AllErrors](err.head, err.tail*) } } diff --git a/network/src/main/scala/no/ndla/network/tapir/auth/TokenUser.scala b/network/src/main/scala/no/ndla/network/tapir/auth/TokenUser.scala index 101a9aec56..07138ed452 100644 --- a/network/src/main/scala/no/ndla/network/tapir/auth/TokenUser.scala +++ b/network/src/main/scala/no/ndla/network/tapir/auth/TokenUser.scala @@ -113,7 +113,7 @@ object TokenUser { .requiredScopes(permissions.map(_.entryName)) EndpointInput.Auth( - input = sttp.tapir.header(HeaderNames.Authorization)(authCodec), + input = sttp.tapir.header(HeaderNames.Authorization)(using authCodec), challenge = WWWAuthenticateChallenge.bearer, authType = authType, info = AuthInfo.Empty.securitySchemeName("oauth2") diff --git a/network/src/test/scala/no/ndla/network/NdlaClientTest.scala b/network/src/test/scala/no/ndla/network/NdlaClientTest.scala index 88fcbe7270..7ed79360fe 100644 --- a/network/src/test/scala/no/ndla/network/NdlaClientTest.scala +++ b/network/src/test/scala/no/ndla/network/NdlaClientTest.scala @@ -29,8 +29,8 @@ class NdlaClientTest extends UnitSuite with NdlaClient { |} """.stripMargin - val httpClientMock: SimpleHttpClient = mock[SimpleHttpClient] - val ndlaClient: NdlaClient = new NdlaClient { + val httpClientMock: SimpleHttpClient = mock[SimpleHttpClient] + override lazy val ndlaClient: NdlaClient = new NdlaClient { override val client = httpClientMock } diff --git a/network/src/test/scala/no/ndla/network/tapir/NonEmptyStringTest.scala b/network/src/test/scala/no/ndla/network/tapir/NonEmptyStringTest.scala index da28bbc2bb..6b76016ee8 100644 --- a/network/src/test/scala/no/ndla/network/tapir/NonEmptyStringTest.scala +++ b/network/src/test/scala/no/ndla/network/tapir/NonEmptyStringTest.scala @@ -9,6 +9,7 @@ package no.ndla.network.tapir import no.ndla.network.UnitSuite +import org.scalatest.EitherValues.convertEitherToValuable class NonEmptyStringTest extends UnitSuite { @@ -42,27 +43,27 @@ class NonEmptyStringTest extends UnitSuite { import io.circe.generic.auto._ case class SomeObject(cantbeempty: Option[NonEmptyString]) - val jsonString = """{"cantbeempty": ""}""" - val Right(Right(result)) = io.circe.parser.parse(jsonString).map(_.as[SomeObject]) - result.cantbeempty should be(None) + val jsonString = """{"cantbeempty": ""}""" + val result = io.circe.parser.parse(jsonString).map(_.as[SomeObject]) + result should be(Right(Right(SomeObject(None)))) } test("That decoding a json-string returns Some(str) if valid string is passed") { import io.circe.generic.auto._ case class SomeObject(cantbeempty: Option[NonEmptyString]) - val jsonString = """{"cantbeempty": "spirrevipp"}""" - val Right(Right(result)) = io.circe.parser.parse(jsonString).map(_.as[SomeObject]) - result.cantbeempty.get.underlying should be("spirrevipp") + val jsonString = """{"cantbeempty": "spirrevipp"}""" + val result = io.circe.parser.parse(jsonString).map(_.as[SomeObject]) + result.value.value.cantbeempty.get.underlying should be("spirrevipp") } test("That decoding missing field returns None for optionals") { import io.circe.generic.auto._ case class SomeObject(cantbeempty: Option[NonEmptyString]) - val jsonString = """{}""" - val Right(Right(result)) = io.circe.parser.parse(jsonString).map(_.as[SomeObject]) - result.cantbeempty should be(None) + val jsonString = """{}""" + val result = io.circe.parser.parse(jsonString).map(_.as[SomeObject]) + result.value.value.cantbeempty should be(None) } test("That encoding json simply makes a normal string :^)") { @@ -85,13 +86,13 @@ class NonEmptyStringTest extends UnitSuite { import io.circe.generic.auto._ case class SomeObject(cantbeempty: NonEmptyString) - val jsonString1 = """{"cantbeempty": "spirrevipp"}""" - val Right(Right(result1)) = io.circe.parser.parse(jsonString1).map(_.as[SomeObject]) - result1.cantbeempty.underlying should be("spirrevipp") + val jsonString1 = """{"cantbeempty": "spirrevipp"}""" + val result1 = io.circe.parser.parse(jsonString1).map(_.as[SomeObject]) + result1.value.value.cantbeempty.underlying should be("spirrevipp") - val jsonString = """{"cantbeempty": ""}""" - val Right(Left(failure)) = io.circe.parser.parse(jsonString).map(_.as[SomeObject]) - failure.message should be(NonEmptyString.parseErrorMessage) + val jsonString = """{"cantbeempty": ""}""" + val failure = io.circe.parser.parse(jsonString).map(_.as[SomeObject]) + failure.value.swap.value.message should be(NonEmptyString.parseErrorMessage) } test("That comparisons works as expected") { diff --git a/oembed-proxy/src/main/scala/no/ndla/oembedproxy/ComponentRegistry.scala b/oembed-proxy/src/main/scala/no/ndla/oembedproxy/ComponentRegistry.scala index 71cc484ea6..5e78f889b4 100644 --- a/oembed-proxy/src/main/scala/no/ndla/oembedproxy/ComponentRegistry.scala +++ b/oembed-proxy/src/main/scala/no/ndla/oembedproxy/ComponentRegistry.scala @@ -29,7 +29,7 @@ class ComponentRegistry(properties: OEmbedProxyProperties) with ErrorHandling with Clock with SwaggerDocControllerConfig { - override val props: OEmbedProxyProperties = properties + override lazy val props: OEmbedProxyProperties = properties lazy val providerService = new ProviderService lazy val oEmbedService = new OEmbedService diff --git a/oembed-proxy/src/main/scala/no/ndla/oembedproxy/OEmbedProxyProperties.scala b/oembed-proxy/src/main/scala/no/ndla/oembedproxy/OEmbedProxyProperties.scala index 0b28c12984..d2ec3d3c1f 100644 --- a/oembed-proxy/src/main/scala/no/ndla/oembedproxy/OEmbedProxyProperties.scala +++ b/oembed-proxy/src/main/scala/no/ndla/oembedproxy/OEmbedProxyProperties.scala @@ -14,7 +14,7 @@ import no.ndla.network.{AuthUser, Domains} import scala.util.Properties.propOrElse trait Props extends HasBaseProps { - val props: OEmbedProxyProperties + lazy val props: OEmbedProxyProperties } class OEmbedProxyProperties extends BaseProps { diff --git a/oembed-proxy/src/main/scala/no/ndla/oembedproxy/caching/Memoize1.scala b/oembed-proxy/src/main/scala/no/ndla/oembedproxy/caching/Memoize1.scala index 948c9d78a9..9816cda7c9 100644 --- a/oembed-proxy/src/main/scala/no/ndla/oembedproxy/caching/Memoize1.scala +++ b/oembed-proxy/src/main/scala/no/ndla/oembedproxy/caching/Memoize1.scala @@ -23,7 +23,7 @@ class Memoize[R](maxCacheAgeMs: Long, retryTimeInMs: Long, f: () => R, autoRefre lastUpdated + maxCacheAgeMs <= System.currentTimeMillis() } - private[this] var cache: Option[CacheValue] = None + private var cache: Option[CacheValue] = None private def renewCache(): Unit = { try { @@ -44,7 +44,7 @@ class Memoize[R](maxCacheAgeMs: Long, retryTimeInMs: Long, f: () => R, autoRefre val task = new Runnable { def run(): Unit = renewCache() } - ex.scheduleAtFixedRate(task, 20, maxCacheAgeMs, TimeUnit.MILLISECONDS) + ex.scheduleAtFixedRate(task, 20, maxCacheAgeMs, TimeUnit.MILLISECONDS): Unit } def apply(): R = { @@ -59,17 +59,14 @@ class Memoize[R](maxCacheAgeMs: Long, retryTimeInMs: Long, f: () => R, autoRefre } trait MemoizeHelpers { this: Props => - import props.{ProviderListRetryTimeInMs, ProviderListCacheAgeInMs} - object Memoize { - def apply[R](f: () => R) = - new Memoize(ProviderListCacheAgeInMs, ProviderListRetryTimeInMs, f, autoRefreshCache = false) + new Memoize(props.ProviderListCacheAgeInMs, props.ProviderListRetryTimeInMs, f, autoRefreshCache = false) } object MemoizeAutoRenew { def apply[R](f: () => R) = - new Memoize(ProviderListCacheAgeInMs, ProviderListRetryTimeInMs, f, autoRefreshCache = true) + new Memoize(props.ProviderListCacheAgeInMs, props.ProviderListRetryTimeInMs, f, autoRefreshCache = true) } } diff --git a/oembed-proxy/src/main/scala/no/ndla/oembedproxy/controller/OEmbedProxyController.scala b/oembed-proxy/src/main/scala/no/ndla/oembedproxy/controller/OEmbedProxyController.scala index 23b10d5a6a..f34a4bfce0 100644 --- a/oembed-proxy/src/main/scala/no/ndla/oembedproxy/controller/OEmbedProxyController.scala +++ b/oembed-proxy/src/main/scala/no/ndla/oembedproxy/controller/OEmbedProxyController.scala @@ -9,7 +9,6 @@ package no.ndla.oembedproxy.controller import cats.implicits.* -import io.circe.generic.auto.* import no.ndla.network.tapir.NoNullJsonPrinter.jsonBody import no.ndla.network.tapir.TapirController import no.ndla.network.tapir.TapirUtil.errorOutputsFor @@ -23,7 +22,7 @@ import scala.util.{Failure, Success} trait OEmbedProxyController { this: OEmbedServiceComponent & ErrorHandling & TapirController => - val oEmbedProxyController: OEmbedProxyController + lazy val oEmbedProxyController: OEmbedProxyController class OEmbedProxyController extends TapirController { override val serviceName: String = "oembed" diff --git a/oembed-proxy/src/main/scala/no/ndla/oembedproxy/service/OEmbedServiceComponent.scala b/oembed-proxy/src/main/scala/no/ndla/oembedproxy/service/OEmbedServiceComponent.scala index 812133af2a..11afb78022 100644 --- a/oembed-proxy/src/main/scala/no/ndla/oembedproxy/service/OEmbedServiceComponent.scala +++ b/oembed-proxy/src/main/scala/no/ndla/oembedproxy/service/OEmbedServiceComponent.scala @@ -23,7 +23,7 @@ import scala.concurrent.duration.FiniteDuration trait OEmbedServiceComponent { this: NdlaClient & ProviderService => - val oEmbedService: OEmbedService + lazy val oEmbedService: OEmbedService class OEmbedService(optionalProviders: Option[List[OEmbedProvider]] = None) extends StrictLogging { private val remoteTimeout: FiniteDuration = 10.seconds diff --git a/oembed-proxy/src/main/scala/no/ndla/oembedproxy/service/ProviderService.scala b/oembed-proxy/src/main/scala/no/ndla/oembedproxy/service/ProviderService.scala index 6bbd17debc..526e596082 100644 --- a/oembed-proxy/src/main/scala/no/ndla/oembedproxy/service/ProviderService.scala +++ b/oembed-proxy/src/main/scala/no/ndla/oembedproxy/service/ProviderService.scala @@ -26,7 +26,7 @@ import sttp.client3.quick.* trait ProviderService { this: NdlaClient & Props & MemoizeHelpers => - val providerService: ProviderService + lazy val providerService: ProviderService class ProviderService extends StrictLogging { val NdlaFrontendEndpoint: OEmbedEndpoint = diff --git a/oembed-proxy/src/test/scala/no/ndla/oembedproxy/TestEnvironment.scala b/oembed-proxy/src/test/scala/no/ndla/oembedproxy/TestEnvironment.scala index 4206b4b51a..bfe2b56bde 100644 --- a/oembed-proxy/src/test/scala/no/ndla/oembedproxy/TestEnvironment.scala +++ b/oembed-proxy/src/test/scala/no/ndla/oembedproxy/TestEnvironment.scala @@ -31,13 +31,13 @@ trait TestEnvironment with Clock { override lazy val props = new OEmbedProxyProperties - val oEmbedService: OEmbedService = mock[OEmbedService] - val oEmbedProxyController: OEmbedProxyController = mock[OEmbedProxyController] - val ndlaClient: NdlaClient = mock[NdlaClient] - val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] - val providerService: ProviderService = mock[ProviderService] - val healthController: TapirHealthController = mock[TapirHealthController] - val clock: SystemClock = mock[SystemClock] + lazy val oEmbedService: OEmbedService = mock[OEmbedService] + lazy val oEmbedProxyController: OEmbedProxyController = mock[OEmbedProxyController] + lazy val ndlaClient: NdlaClient = mock[NdlaClient] + lazy val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] + lazy val providerService: ProviderService = mock[ProviderService] + lazy val healthController: TapirHealthController = mock[TapirHealthController] + lazy val clock: SystemClock = mock[SystemClock] def services: List[TapirController] = List.empty val swagger: SwaggerController = mock[SwaggerController] diff --git a/oembed-proxy/src/test/scala/no/ndla/oembedproxy/caching/MemoizeTest.scala b/oembed-proxy/src/test/scala/no/ndla/oembedproxy/caching/MemoizeTest.scala index 3b6117b22e..8d7c3bf1a7 100644 --- a/oembed-proxy/src/test/scala/no/ndla/oembedproxy/caching/MemoizeTest.scala +++ b/oembed-proxy/src/test/scala/no/ndla/oembedproxy/caching/MemoizeTest.scala @@ -20,7 +20,7 @@ class MemoizeTest extends UnitSuite with TestEnvironment { test("That an uncached value will do an actual call") { val targetMock = mock[Target] - val memoizedTarget = new Memoize[String](Long.MaxValue, Long.MaxValue, targetMock.targetMethod _, false) + val memoizedTarget = new Memoize[String](Long.MaxValue, Long.MaxValue, targetMock.targetMethod, false) when(targetMock.targetMethod()).thenReturn("Hello from mock") memoizedTarget() should equal("Hello from mock") @@ -29,7 +29,7 @@ class MemoizeTest extends UnitSuite with TestEnvironment { test("That a cached value will not forward the call to the target") { val targetMock = mock[Target] - val memoizedTarget = new Memoize[String](Long.MaxValue, Long.MaxValue, targetMock.targetMethod _, false) + val memoizedTarget = new Memoize[String](Long.MaxValue, Long.MaxValue, targetMock.targetMethod, false) when(targetMock.targetMethod()).thenReturn("Hello from mock") Seq(1 to 10).foreach(_ => { @@ -42,7 +42,7 @@ class MemoizeTest extends UnitSuite with TestEnvironment { val cacheMaxAgeInMs = 20L val cacheRetryInMs = 20L val targetMock = mock[Target] - val memoizedTarget = new Memoize[String](cacheMaxAgeInMs, cacheRetryInMs, targetMock.targetMethod _, false) + val memoizedTarget = new Memoize[String](cacheMaxAgeInMs, cacheRetryInMs, targetMock.targetMethod, false) when(targetMock.targetMethod()).thenReturn("Hello from mock") @@ -59,7 +59,7 @@ class MemoizeTest extends UnitSuite with TestEnvironment { val cacheMaxAgeInMs = 20L val cacheRetryInMs = 20L val targetMock = mock[Target] - val memoizedTarget = new Memoize[String](cacheMaxAgeInMs, cacheRetryInMs, targetMock.targetMethod _, false) + val memoizedTarget = new Memoize[String](cacheMaxAgeInMs, cacheRetryInMs, targetMock.targetMethod, false) when(targetMock.targetMethod()) .thenReturn("Hello from mock") diff --git a/oembed-proxy/src/test/scala/no/ndla/oembedproxy/controller/HealthControllerTest.scala b/oembed-proxy/src/test/scala/no/ndla/oembedproxy/controller/HealthControllerTest.scala index 011f9b9a71..6909f78649 100644 --- a/oembed-proxy/src/test/scala/no/ndla/oembedproxy/controller/HealthControllerTest.scala +++ b/oembed-proxy/src/test/scala/no/ndla/oembedproxy/controller/HealthControllerTest.scala @@ -13,7 +13,7 @@ import no.ndla.tapirtesting.TapirControllerTest import sttp.client3.quick._ class HealthControllerTest extends UnitSuite with TestEnvironment with TapirControllerTest { - lazy val controller: TapirHealthController = new TapirHealthController + val controller: TapirHealthController = new TapirHealthController controller.setWarmedUp() test("That /health returns 200 ok") { diff --git a/oembed-proxy/src/test/scala/no/ndla/oembedproxy/service/OEmbedServiceTest.scala b/oembed-proxy/src/test/scala/no/ndla/oembedproxy/service/OEmbedServiceTest.scala index 4ef96b67ce..994be31081 100644 --- a/oembed-proxy/src/test/scala/no/ndla/oembedproxy/service/OEmbedServiceTest.scala +++ b/oembed-proxy/src/test/scala/no/ndla/oembedproxy/service/OEmbedServiceTest.scala @@ -59,21 +59,19 @@ class OEmbedServiceTest extends UnitSuite with TestEnvironment { Some("") ) - override val oEmbedService = new OEmbedService(Some(List(ndlaProvider, youtubeProvider))) - val providerMemoize = new Memoize(0, 0, () => List[OEmbedProvider](), false) - override val providerService: ProviderService = new ProviderService { + override lazy val oEmbedService = new OEmbedService(Some(List(ndlaProvider, youtubeProvider))) + val providerMemoize = new Memoize(0, 0, () => List[OEmbedProvider](), false) + override lazy val providerService: ProviderService = new ProviderService { override val loadProviders: Memoize[List[OEmbedProvider]] = providerMemoize } test("That get returns Failure(ProviderNotSupportedException) when no providers support the url") { - val Failure(ex: ProviderNotSupportedException) = - oEmbedService.get(url = "ABC", None, None) - - ex.getMessage should equal("Could not find an oembed-provider for the url 'ABC'") + val ex = oEmbedService.get(url = "ABC", None, None) + ex should be(Failure(ProviderNotSupportedException("Could not find an oembed-provider for the url 'ABC'"))) } test("That get returns a failure with HttpRequestException when receiving http error") { - when(ndlaClient.fetch[OEmbedDTO](any[NdlaRequest])(any)) + when(ndlaClient.fetch[OEmbedDTO](any[NdlaRequest])(using any)) .thenReturn(Failure(new HttpRequestException("An error occured"))) val oembedTry = oEmbedService.get("https://www.youtube.com/abc", None, None) oembedTry.isFailure should be(true) @@ -81,7 +79,7 @@ class OEmbedServiceTest extends UnitSuite with TestEnvironment { } test("That get returns a Success with an oEmbed when http call is successful") { - when(ndlaClient.fetch[OEmbedDTO](any[NdlaRequest])(any)) + when(ndlaClient.fetch[OEmbedDTO](any[NdlaRequest])(using any)) .thenReturn(Success(OEmbedResponse)) val oembedTry = oEmbedService.get("https://ndla.no/abc", None, None) oembedTry.isSuccess should be(true) diff --git a/oembed-proxy/src/test/scala/no/ndla/oembedproxy/service/ProviderServiceTest.scala b/oembed-proxy/src/test/scala/no/ndla/oembedproxy/service/ProviderServiceTest.scala index 0b1059d257..af8882a965 100644 --- a/oembed-proxy/src/test/scala/no/ndla/oembedproxy/service/ProviderServiceTest.scala +++ b/oembed-proxy/src/test/scala/no/ndla/oembedproxy/service/ProviderServiceTest.scala @@ -39,11 +39,11 @@ class ProviderServiceTest extends UnitSuite with TestEnvironment { ) ) - override val providerService = new ProviderService + override lazy val providerService = new ProviderService test("That loadProvidersFromRequest fails on invalid url/bad response") { val invalidUrl = "invalidUrl123" - when(ndlaClient.fetch[OEmbedDTO](any[NdlaRequest])(any)) + when(ndlaClient.fetch[OEmbedDTO](any[NdlaRequest])(using any)) .thenReturn(Failure(new HttpRequestException("An error occured"))) intercept[DoNotUpdateMemoizeException] { providerService.loadProvidersFromRequest(quickRequest.get(uri"$invalidUrl")) @@ -51,7 +51,7 @@ class ProviderServiceTest extends UnitSuite with TestEnvironment { } test("That loadProvidersFromRequest does not return an incomplete provider") { - when(ndlaClient.fetch[List[OEmbedProvider]](any[NdlaRequest])(any)) + when(ndlaClient.fetch[List[OEmbedProvider]](any[NdlaRequest])(using any)) .thenReturn(Success(List(IncompleteProvider))) val providers = providerService.loadProvidersFromRequest(mock[NdlaRequest]) @@ -59,7 +59,7 @@ class ProviderServiceTest extends UnitSuite with TestEnvironment { } test("That loadProvidersFromRequest works for a single provider") { - when(ndlaClient.fetch[List[OEmbedProvider]](any[NdlaRequest])(any)) + when(ndlaClient.fetch[List[OEmbedProvider]](any[NdlaRequest])(using any)) .thenReturn(Success(List(CompleteProvider))) val providers = providerService.loadProvidersFromRequest(mock[NdlaRequest]) @@ -67,7 +67,7 @@ class ProviderServiceTest extends UnitSuite with TestEnvironment { } test("That loadProvidersFromRequest only returns the complete provider") { - when(ndlaClient.fetch[List[OEmbedProvider]](any[NdlaRequest])(any)) + when(ndlaClient.fetch[List[OEmbedProvider]](any[NdlaRequest])(using any)) .thenReturn(Success(List(IncompleteProvider, CompleteProvider))) val providers = providerService.loadProvidersFromRequest(mock[NdlaRequest]) diff --git a/search-api/src/main/scala/no/ndla/searchapi/ComponentRegistry.scala b/search-api/src/main/scala/no/ndla/searchapi/ComponentRegistry.scala index dbefa97931..f5c27c7681 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/ComponentRegistry.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/ComponentRegistry.scala @@ -61,41 +61,40 @@ class ComponentRegistry(properties: SearchApiProperties) with Props with SwaggerDocControllerConfig with GrepSearchService { - override val props: SearchApiProperties = properties - import props._ + override lazy val props: SearchApiProperties = properties - lazy val ndlaClient = new NdlaClient - var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(SearchServer) + override lazy val ndlaClient = new NdlaClient + var e4sClient: NdlaE4sClient = Elastic4sClientFactory.getClient(props.SearchServer) - lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient + override lazy val myndlaApiClient: MyNDLAApiClient = new MyNDLAApiClient - lazy val taxonomyApiClient = new TaxonomyApiClient - lazy val grepApiClient = new GrepApiClient + override lazy val taxonomyApiClient = new TaxonomyApiClient + override lazy val grepApiClient = new GrepApiClient - lazy val draftApiClient = new DraftApiClient(DraftApiUrl) - lazy val draftConceptApiClient = new DraftConceptApiClient(ConceptApiUrl) - lazy val learningPathApiClient = new LearningPathApiClient(LearningpathApiUrl) - lazy val articleApiClient = new ArticleApiClient(ArticleApiUrl) - lazy val feideApiClient = new FeideApiClient - lazy val redisClient = new RedisClient(props.RedisHost, props.RedisPort) - lazy val frontpageApiClient = new FrontpageApiClient + override lazy val draftApiClient = new DraftApiClient(props.DraftApiUrl) + override lazy val draftConceptApiClient = new DraftConceptApiClient(props.ConceptApiUrl) + override lazy val learningPathApiClient = new LearningPathApiClient(props.LearningpathApiUrl) + override lazy val articleApiClient = new ArticleApiClient(props.ArticleApiUrl) + override lazy val feideApiClient = new FeideApiClient + override lazy val redisClient = new RedisClient(props.RedisHost, props.RedisPort) + override lazy val frontpageApiClient = new FrontpageApiClient - lazy val converterService = new ConverterService - lazy val searchConverterService = new SearchConverterService - lazy val multiSearchService = new MultiSearchService - lazy val articleIndexService = new ArticleIndexService - lazy val draftConceptIndexService = new DraftConceptIndexService - lazy val learningPathIndexService = new LearningPathIndexService - lazy val draftIndexService = new DraftIndexService - lazy val multiDraftSearchService = new MultiDraftSearchService - lazy val grepIndexService = new GrepIndexService - lazy val grepSearchService = new GrepSearchService - lazy val nodeIndexService = new NodeIndexService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService + override lazy val multiSearchService = new MultiSearchService + override lazy val articleIndexService = new ArticleIndexService + override lazy val draftConceptIndexService = new DraftConceptIndexService + override lazy val learningPathIndexService = new LearningPathIndexService + override lazy val draftIndexService = new DraftIndexService + override lazy val multiDraftSearchService = new MultiDraftSearchService + override lazy val grepIndexService = new GrepIndexService + override lazy val grepSearchService = new GrepSearchService + override lazy val nodeIndexService = new NodeIndexService - lazy val searchController = new SearchController - lazy val healthController: TapirHealthController = new TapirHealthController - lazy val internController = new InternController - lazy val clock: SystemClock = new SystemClock + override lazy val searchController = new SearchController + override lazy val healthController: TapirHealthController = new TapirHealthController + override lazy val internController = new InternController + override lazy val clock: SystemClock = new SystemClock val swagger = new SwaggerController( List[TapirController]( diff --git a/search-api/src/main/scala/no/ndla/searchapi/SearchApiProperties.scala b/search-api/src/main/scala/no/ndla/searchapi/SearchApiProperties.scala index 1f187bf33b..3175e342fb 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/SearchApiProperties.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/SearchApiProperties.scala @@ -17,7 +17,7 @@ import scala.util.Properties.* import scala.util.{Failure, Success, Try} trait Props extends HasBaseProps { - val props: SearchApiProperties + lazy val props: SearchApiProperties } class SearchApiProperties extends BaseProps with StrictLogging { diff --git a/search-api/src/main/scala/no/ndla/searchapi/controller/InternController.scala b/search-api/src/main/scala/no/ndla/searchapi/controller/InternController.scala index 4c43b22074..4bb3afeea8 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/controller/InternController.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/controller/InternController.scala @@ -52,7 +52,7 @@ trait InternController { this: IndexService & ArticleIndexService & LearningPathIndexService & DraftIndexService & DraftConceptIndexService & NodeIndexService & TaxonomyApiClient & GrepApiClient & GrepIndexService & Props & ErrorHandling & MyNDLAApiClient & TapirController => - val internController: InternController + lazy val internController: InternController class InternController extends TapirController with StrictLogging { import ErrorHelpers._ diff --git a/search-api/src/main/scala/no/ndla/searchapi/controller/SearchController.scala b/search-api/src/main/scala/no/ndla/searchapi/controller/SearchController.scala index 8c04ba311c..aa09167537 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/controller/SearchController.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/controller/SearchController.scala @@ -60,11 +60,9 @@ import no.ndla.common.model.domain.Priority trait SearchController { this: SearchApiClient & MultiSearchService & SearchConverterService & SearchService & MultiDraftSearchService & FeideApiClient & Props & ErrorHandling & TapirController & GrepSearchService & GetSearchQueryParams => - val searchController: SearchController + lazy val searchController: SearchController class SearchController extends TapirController { - import props.* - override val serviceName: String = "search" override val prefix: EndpointInput[Unit] = "search-api" / "v1" / serviceName @@ -199,7 +197,7 @@ trait SearchController { orFunction: => Try[(MultiSearchResultDTO, DynamicHeaders)] ): Try[(MultiSearchResultDTO, DynamicHeaders)] = { scrollId match { - case Some(scroll) if !InitialScrollContextKeywords.contains(scroll) => + case Some(scroll) if !props.InitialScrollContextKeywords.contains(scroll) => for { scrollResult <- scroller.scroll(scroll, language.code) body = searchConverterService.toApiMultiSearchResult(scrollResult) @@ -461,7 +459,7 @@ trait SearchController { p match { case None => SearchSettings.default case Some(params) => - val shouldScroll = params.scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = params.scrollId.exists(props.InitialScrollContextKeywords.contains) SearchSettings( query = params.query, fallback = params.fallback.getOrElse(false), @@ -498,7 +496,7 @@ trait SearchController { p match { case None => MultiDraftSearchSettings.default(user) case Some(params) => - val shouldScroll = params.scrollId.exists(InitialScrollContextKeywords.contains) + val shouldScroll = params.scrollId.exists(props.InitialScrollContextKeywords.contains) MultiDraftSearchSettings( user = user, query = params.query, diff --git a/search-api/src/main/scala/no/ndla/searchapi/integration/ArticleApiClient.scala b/search-api/src/main/scala/no/ndla/searchapi/integration/ArticleApiClient.scala index 83ba74f6af..849f82ea17 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/integration/ArticleApiClient.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/integration/ArticleApiClient.scala @@ -13,7 +13,7 @@ import no.ndla.network.NdlaClient trait ArticleApiClient { this: NdlaClient & SearchApiClient => - val articleApiClient: ArticleApiClient + lazy val articleApiClient: ArticleApiClient class ArticleApiClient(val baseUrl: String) extends SearchApiClient[Article] { override val searchPath = "article-api/v2/articles" diff --git a/search-api/src/main/scala/no/ndla/searchapi/integration/DraftApiClient.scala b/search-api/src/main/scala/no/ndla/searchapi/integration/DraftApiClient.scala index a7d2f8acf7..bf109f8128 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/integration/DraftApiClient.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/integration/DraftApiClient.scala @@ -14,7 +14,7 @@ import no.ndla.searchapi.Props trait DraftApiClient { this: NdlaClient & SearchApiClient & Props => - val draftApiClient: DraftApiClient + lazy val draftApiClient: DraftApiClient class DraftApiClient(val baseUrl: String) extends SearchApiClient[Draft] { override val searchPath = "draft-api/v1/drafts" diff --git a/search-api/src/main/scala/no/ndla/searchapi/integration/DraftConceptApiClient.scala b/search-api/src/main/scala/no/ndla/searchapi/integration/DraftConceptApiClient.scala index 19cfda6622..4144c2c9cc 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/integration/DraftConceptApiClient.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/integration/DraftConceptApiClient.scala @@ -13,7 +13,7 @@ import no.ndla.network.NdlaClient trait DraftConceptApiClient { this: NdlaClient & SearchApiClient => - val draftConceptApiClient: DraftConceptApiClient + lazy val draftConceptApiClient: DraftConceptApiClient class DraftConceptApiClient(val baseUrl: String) extends SearchApiClient[Concept] { override val searchPath = "concept-api/v1/drafts" diff --git a/search-api/src/main/scala/no/ndla/searchapi/integration/GrepApiClient.scala b/search-api/src/main/scala/no/ndla/searchapi/integration/GrepApiClient.scala index cd9792e2ee..2e729bb60c 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/integration/GrepApiClient.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/integration/GrepApiClient.scala @@ -12,7 +12,7 @@ import cats.implicits.* import com.typesafe.scalalogging.StrictLogging import io.circe.Decoder import no.ndla.common.CirceUtil -import no.ndla.common.implicits.TryQuestionMark +import no.ndla.common.implicits.* import no.ndla.common.logging.logTaskTime import no.ndla.common.model.NDLADate import no.ndla.network.NdlaClient @@ -30,11 +30,10 @@ import scala.util.{Failure, Success, Try, Using} trait GrepApiClient { this: NdlaClient & Props => - val grepApiClient: GrepApiClient + lazy val grepApiClient: GrepApiClient class GrepApiClient extends StrictLogging { - import props.GrepApiUrl - private val grepDumpUrl = s"$GrepApiUrl/kl06/v201906/dump/json" + private val grepDumpUrl = s"${props.GrepApiUrl}/kl06/v201906/dump/json" private def readFile(file: File): Try[String] = Try { Using.resource(scala.io.Source.fromFile(file)) { source => @@ -58,12 +57,16 @@ trait GrepApiClient { private def getKjerneelementerLK20(dump: File): Try[List[GrepKjerneelement]] = readGrepJsonFiles[GrepKjerneelement](dump, "kjerneelementer-lk20") + private def getKompetansemaalLK20(dump: File): Try[List[GrepKompetansemaal]] = readGrepJsonFiles[GrepKompetansemaal](dump, "kompetansemaal-lk20") + private def getKompetansemaalsettLK20(dump: File): Try[List[GrepKompetansemaalSett]] = readGrepJsonFiles[GrepKompetansemaalSett](dump, "kompetansemaalsett-lk20") + private def getTverrfagligeTemaerLK20(dump: File): Try[List[GrepTverrfagligTema]] = readGrepJsonFiles[GrepTverrfagligTema](dump, "tverrfaglige-temaer-lk20") + private def getLaereplanerLK20(dump: File): Try[List[GrepLaererplan]] = readGrepJsonFiles[GrepLaererplan](dump, "laereplaner-lk20") @@ -91,18 +94,22 @@ trait GrepApiClient { } f.delete(): Unit } + def release(resource: File): Unit = deleteDirectory(resource) } - private def getGrepBundleUncached: Try[GrepBundle] = logTaskTime("Fetching grep bundle", 30.seconds) { - val date = NDLADate.now().toUTCEpochSecond - val tempDirPath = Try(Files.createTempDirectory(s"grep-dump-$date")).? - Using(tempDirPath.toFile) { tempDir => - val zippedDump = fetchDump(tempDir).? - val unzippedDump = ZipUtil.unzip(zippedDump, tempDir, deleteArchive = true).? - getBundleFromDump(unzippedDump).? + private def getGrepBundleUncached: Try[GrepBundle] = + logTaskTime("Fetching grep bundle", 30.seconds) { + permitTry { + val date = NDLADate.now().toUTCEpochSecond + val tempDirPath = Try(Files.createTempDirectory(s"grep-dump-$date")).? + Using(tempDirPath.toFile) { tempDir => + val zippedDump = fetchDump(tempDir).? + val unzippedDump = ZipUtil.unzip(zippedDump, tempDir, deleteArchive = true).? + getBundleFromDump(unzippedDump).? + } + } } - } case class GrepDumpDownloadException(message: String) extends RuntimeException(message) { def withCause(cause: Throwable): GrepDumpDownloadException = { diff --git a/search-api/src/main/scala/no/ndla/searchapi/integration/LearningPathApiClient.scala b/search-api/src/main/scala/no/ndla/searchapi/integration/LearningPathApiClient.scala index a1416e7fed..2cc949012f 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/integration/LearningPathApiClient.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/integration/LearningPathApiClient.scala @@ -22,7 +22,7 @@ import scala.util.{Failure, Success, Try} trait LearningPathApiClient { this: NdlaClient & StrictLogging & SearchApiClient => - val learningPathApiClient: LearningPathApiClient + lazy val learningPathApiClient: LearningPathApiClient class LearningPathApiClient(val baseUrl: String) extends SearchApiClient[LearningPath] { override val searchPath = "learningpath-api/v2/learningpaths" diff --git a/search-api/src/main/scala/no/ndla/searchapi/integration/TaxonomyApiClient.scala b/search-api/src/main/scala/no/ndla/searchapi/integration/TaxonomyApiClient.scala index 9e07ac4e6a..458839e214 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/integration/TaxonomyApiClient.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/integration/TaxonomyApiClient.scala @@ -30,12 +30,10 @@ import scala.util.{Failure, Success, Try} trait TaxonomyApiClient { this: NdlaClient & Props => - val taxonomyApiClient: TaxonomyApiClient + lazy val taxonomyApiClient: TaxonomyApiClient class TaxonomyApiClient extends StrictLogging { - import props.TaxonomyUrl - - private val TaxonomyApiEndpoint = s"$TaxonomyUrl/v1" + private val TaxonomyApiEndpoint = s"${props.TaxonomyUrl}/v1" private val timeoutSeconds = 600.seconds private def getNodes(shouldUsePublishedTax: Boolean): Try[ListBuffer[Node]] = get[ListBuffer[Node]]( diff --git a/search-api/src/main/scala/no/ndla/searchapi/integration/ZipUtil.scala b/search-api/src/main/scala/no/ndla/searchapi/integration/ZipUtil.scala index fe39cf165a..bc029bb65f 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/integration/ZipUtil.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/integration/ZipUtil.scala @@ -32,7 +32,9 @@ object ZipUtil { zis.close() fis.close() - if (deleteArchive) zipFile.delete() + if (deleteArchive) { + val _ = zipFile.delete() + } targetDir } diff --git a/search-api/src/main/scala/no/ndla/searchapi/model/api/grep/GrepStatusDTO.scala b/search-api/src/main/scala/no/ndla/searchapi/model/api/grep/GrepStatusDTO.scala index 626da96ec5..3463ddf4bc 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/model/api/grep/GrepStatusDTO.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/model/api/grep/GrepStatusDTO.scala @@ -11,7 +11,7 @@ package no.ndla.searchapi.model.api.grep import enumeratum.* import io.circe.syntax.EncoderOps import io.circe.{Decoder, Encoder} -import no.ndla.common.implicits.OptionImplicit +import no.ndla.common.implicits.* import sttp.tapir.Schema import sttp.tapir.codec.enumeratum.* diff --git a/search-api/src/main/scala/no/ndla/searchapi/service/ConverterService.scala b/search-api/src/main/scala/no/ndla/searchapi/service/ConverterService.scala index 3c2070021b..9942a62a00 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/service/ConverterService.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/service/ConverterService.scala @@ -31,11 +31,9 @@ import no.ndla.searchapi.model.domain.* trait ConverterService { this: Props => - val converterService: ConverterService + lazy val converterService: ConverterService class ConverterService { - import props.Domain - def searchResultToApiModel(searchResults: ApiSearchResults): SearchResultsDTO = { searchResults match { case a: ArticleApiSearchResults => articleSearchResultsToApi(a) @@ -101,7 +99,7 @@ trait ConverterService { private def imageSearchResultToApi(image: ImageApiSearchResult): ImageResultDTO = { val scheme = ApplicationUrl.get.schemeOption.getOrElse("https://") - val host = ApplicationUrl.get.hostOption.map(_.toString).getOrElse(Domain) + val host = ApplicationUrl.get.hostOption.map(_.toString).getOrElse(props.Domain) val previewUrl = image.previewUrl.withHost(host).withScheme(scheme) val metaUrl = image.metaUrl.withHost(host).withScheme(scheme) @@ -129,7 +127,7 @@ trait ConverterService { private def audioSearchResultToApi(audio: AudioApiSearchResult): AudioResultDTO = { val scheme = ApplicationUrl.get.schemeOption.getOrElse("https://") - val host = ApplicationUrl.get.hostOption.map(_.toString).getOrElse(Domain) + val host = ApplicationUrl.get.hostOption.map(_.toString).getOrElse(props.Domain) val url = audio.url.withHost(host).withScheme(scheme).toString AudioResultDTO( diff --git a/search-api/src/main/scala/no/ndla/searchapi/service/search/ArticleIndexService.scala b/search-api/src/main/scala/no/ndla/searchapi/service/search/ArticleIndexService.scala index 046a577196..46b194f63c 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/service/search/ArticleIndexService.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/service/search/ArticleIndexService.scala @@ -18,20 +18,19 @@ import no.ndla.common.CirceUtil import no.ndla.common.model.api.search.SearchType import no.ndla.common.model.domain.article.Article import no.ndla.searchapi.Props -import no.ndla.searchapi.integration.ArticleApiClient +import no.ndla.searchapi.integration.{ArticleApiClient, SearchApiClient} import no.ndla.searchapi.model.domain.IndexingBundle import scala.util.Try trait ArticleIndexService { - this: SearchConverterService & IndexService & ArticleApiClient & Props => - val articleIndexService: ArticleIndexService + this: SearchConverterService & IndexService & ArticleApiClient & Props & SearchApiClient => + lazy val articleIndexService: ArticleIndexService class ArticleIndexService extends StrictLogging with IndexService[Article] { - import props.SearchIndex - override val documentType: String = "article" - override val searchIndex: String = SearchIndex(SearchType.Articles) - override val apiClient: ArticleApiClient = articleApiClient + override val documentType: String = "article" + override val searchIndex: String = props.SearchIndex(SearchType.Articles) + override val apiClient: SearchApiClient[Article] = articleApiClient override def createIndexRequest( domainModel: Article, diff --git a/search-api/src/main/scala/no/ndla/searchapi/service/search/DraftConceptIndexService.scala b/search-api/src/main/scala/no/ndla/searchapi/service/search/DraftConceptIndexService.scala index 6fbd8df76d..42ad99eb09 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/service/search/DraftConceptIndexService.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/service/search/DraftConceptIndexService.scala @@ -17,21 +17,20 @@ import com.typesafe.scalalogging.StrictLogging import no.ndla.common.CirceUtil import no.ndla.common.model.api.search.SearchType import no.ndla.searchapi.Props -import no.ndla.searchapi.integration.DraftConceptApiClient +import no.ndla.searchapi.integration.{DraftConceptApiClient, SearchApiClient} import no.ndla.searchapi.model.domain.IndexingBundle import no.ndla.common.model.domain.concept.Concept import scala.util.Try trait DraftConceptIndexService { - this: SearchConverterService & IndexService & DraftConceptApiClient & Props => - val draftConceptIndexService: DraftConceptIndexService + this: SearchConverterService & IndexService & DraftConceptApiClient & Props & SearchApiClient => + lazy val draftConceptIndexService: DraftConceptIndexService - class DraftConceptIndexService extends StrictLogging with IndexService[Concept] { - import props.SearchIndex - override val documentType: String = "concept" - override val searchIndex: String = SearchIndex(SearchType.Concepts) - override val apiClient: DraftConceptApiClient = draftConceptApiClient + class DraftConceptIndexService extends IndexService[Concept] with StrictLogging { + override val documentType: String = "concept" + override val searchIndex: String = props.SearchIndex(SearchType.Concepts) + override val apiClient: SearchApiClient[Concept] = draftConceptApiClient override def createIndexRequest( domainModel: Concept, diff --git a/search-api/src/main/scala/no/ndla/searchapi/service/search/DraftIndexService.scala b/search-api/src/main/scala/no/ndla/searchapi/service/search/DraftIndexService.scala index 0adbb155da..32ef025feb 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/service/search/DraftIndexService.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/service/search/DraftIndexService.scala @@ -18,20 +18,19 @@ import no.ndla.common.CirceUtil import no.ndla.common.model.api.search.SearchType import no.ndla.common.model.domain.draft.Draft import no.ndla.searchapi.Props -import no.ndla.searchapi.integration.DraftApiClient +import no.ndla.searchapi.integration.{DraftApiClient, SearchApiClient} import no.ndla.searchapi.model.domain.IndexingBundle import scala.util.Try trait DraftIndexService { - this: SearchConverterService & IndexService & DraftApiClient & Props => - import props.SearchIndex - val draftIndexService: DraftIndexService + this: SearchConverterService & IndexService & DraftApiClient & Props & SearchApiClient => + lazy val draftIndexService: DraftIndexService class DraftIndexService extends StrictLogging with IndexService[Draft] { - override val documentType: String = "draft" - override val searchIndex: String = SearchIndex(SearchType.Drafts) - override val apiClient: DraftApiClient = draftApiClient + override val documentType: String = "draft" + override val searchIndex: String = props.SearchIndex(SearchType.Drafts) + override val apiClient: SearchApiClient[Draft] = draftApiClient override def createIndexRequest( domainModel: Draft, diff --git a/search-api/src/main/scala/no/ndla/searchapi/service/search/GrepIndexService.scala b/search-api/src/main/scala/no/ndla/searchapi/service/search/GrepIndexService.scala index 5327b43f51..68c4134bcb 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/service/search/GrepIndexService.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/service/search/GrepIndexService.scala @@ -9,7 +9,7 @@ package no.ndla.searchapi.service.search import cats.implicits.toTraverseOps -import no.ndla.common.implicits.TryQuestionMark +import no.ndla.common.implicits.* import com.sksamuel.elastic4s.ElasticDsl.* import com.sksamuel.elastic4s.fields.ObjectField import com.sksamuel.elastic4s.requests.indexes.IndexRequest @@ -26,12 +26,11 @@ import scala.util.{Success, Try} trait GrepIndexService { this: SearchConverterService & IndexService & Props & GrepApiClient => - val grepIndexService: GrepIndexService + lazy val grepIndexService: GrepIndexService class GrepIndexService extends BulkIndexingService with StrictLogging { - import props.SearchIndex override val documentType: String = "grep" - override val searchIndex: String = SearchIndex(SearchType.Grep) + override val searchIndex: String = props.SearchIndex(SearchType.Grep) override val MaxResultWindowOption: Int = props.ElasticSearchIndexMaxResultWindow override def getMapping: MappingDefinition = { @@ -55,7 +54,7 @@ trait GrepIndexService { } } - def createIndexRequest(grepElement: GrepElement, indexName: String): Try[IndexRequest] = { + def createIndexRequest(grepElement: GrepElement, indexName: String): Try[IndexRequest] = permitTry { val searchable = searchConverterService.asSearchableGrep(grepElement).? val source = CirceUtil.toJsonString(searchable) Success(indexInto(indexName).doc(source).id(grepElement.kode)) @@ -68,7 +67,7 @@ trait GrepIndexService { .flatten } - def sendToElastic(grepBundle: Option[GrepBundle], indexName: String): Try[BulkIndexResult] = { + def sendToElastic(grepBundle: Option[GrepBundle], indexName: String): Try[BulkIndexResult] = permitTry { val bundle = (grepBundle match { case Some(value) => Success(value) case None => grepApiClient.getGrepBundle() diff --git a/search-api/src/main/scala/no/ndla/searchapi/service/search/GrepSearchService.scala b/search-api/src/main/scala/no/ndla/searchapi/service/search/GrepSearchService.scala index fd30f13341..7b3da32260 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/service/search/GrepSearchService.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/service/search/GrepSearchService.scala @@ -17,7 +17,7 @@ import com.sksamuel.elastic4s.requests.searches.sort.SortOrder.{Asc, Desc} import com.sksamuel.elastic4s.requests.searches.{SearchHit, SearchRequest, SearchResponse} import com.typesafe.scalalogging.StrictLogging import no.ndla.common.CirceUtil -import no.ndla.common.implicits.TryQuestionMark +import no.ndla.common.implicits.* import no.ndla.common.model.api.search.SearchType import no.ndla.language.Language.AllLanguages import no.ndla.language.model.Iso639 @@ -35,15 +35,14 @@ import no.ndla.searchapi.model.grep.{ } import no.ndla.searchapi.model.search.SearchableGrepElement -import scala.util.{Failure, Success, Try} +import scala.util.{Failure, Success, Try, boundary} trait GrepSearchService { this: Props & SearchService & GrepIndexService & BaseIndexService & Elastic4sClient & SearchConverterService => - val grepSearchService: GrepSearchService + lazy val grepSearchService: GrepSearchService class GrepSearchService extends SearchService with StrictLogging { - import props.SearchIndex - override val searchIndex: List[String] = List(SearchType.Grep).map(SearchIndex) + override val searchIndex: List[String] = List(SearchType.Grep).map(props.SearchIndex) override val indexServices: List[BaseIndexService] = List(grepIndexService) def grepSortDefinition(maybeSort: Option[GrepSortDTO], language: String): FieldSort = maybeSort match { @@ -158,7 +157,7 @@ trait GrepSearchService { executeAsSearchableGreps(searchToExecute) } - def searchGreps(input: GrepSearchInputDTO): Try[GrepSearchResultsDTO] = { + def searchGreps(input: GrepSearchInputDTO): Try[GrepSearchResultsDTO] = permitTry { val searchLanguage = input.language match { case Some(lang) if Iso639.get(lang).isSuccess => lang case _ => AllLanguages @@ -204,7 +203,7 @@ trait GrepSearchService { CirceUtil.tryParseAs[SearchableGrepElement](jsonString) } - private def hitToResult(hit: SearchHit, language: String): Try[GrepResultDTO] = { + private def hitToResult(hit: SearchHit, language: String): Try[GrepResultDTO] = permitTry { val searchable = hitToSearchable(hit).? GrepResultDTO.fromSearchable(searchable, language) } @@ -213,32 +212,35 @@ trait GrepSearchService { response.result.hits.hits.toList.traverse { hit => f(hit) } } - private def getCoreElementReplacement(core: GrepKjerneelement): Try[String] = { - val lpCode = core.`tilhoerer-laereplan`.kode - val foundLp = getSingleCodeById(lpCode) match { - case Success(Some(lp)) => lp - case Failure(ex) => return Failure(ex) - case Success(None) => - logger.warn(s"Could not find læreplan for core element: ${core.kode} (LP: $lpCode)") - return Success(core.kode) - } + private def getCoreElementReplacement(core: GrepKjerneelement): Try[String] = permitTry { + boundary { + val lpCode = core.`tilhoerer-laereplan`.kode + val foundLp = getSingleCodeById(lpCode) match { + case Success(Some(lp)) => lp + case Failure(ex) => boundary.break(Failure(ex)) + case Success(None) => + logger.warn(s"Could not find læreplan for core element: ${core.kode} (LP: $lpCode)") + boundary.break(Success(core.kode)) + } - val domainObject = foundLp.domainObject match { - case lp: GrepLaererplan => lp - case _ => - val msg = - s"Got unexpected domain object when looking up læreplan (${foundLp.code}) for replacement for core element (${core.kode})" - logger.error(msg) - return Failure(new RuntimeException(msg)) - } - getLaererplanReplacement(domainObject) match { - case None => Success(core.kode) - case Some(replacementPlan) => - val elementsInPlan = elementsWithLpCode(replacementPlan).? - val foundReplacement = elementsInPlan.find { x => - core.tittel.tekst == x.domainObject.getTitle - } - Success(foundReplacement.map(_.code).getOrElse(core.kode)) + val domainObject = foundLp.domainObject match { + case lp: GrepLaererplan => lp + case _ => + val msg = + s"Got unexpected domain object when looking up læreplan (${foundLp.code}) for replacement for core element (${core.kode})" + logger.error(msg) + boundary.break(Failure(new RuntimeException(msg))) + } + + getLaererplanReplacement(domainObject) match { + case None => Success(core.kode) + case Some(replacementPlan) => + val elementsInPlan = elementsWithLpCode(replacementPlan).? + val foundReplacement = elementsInPlan.find { x => + core.tittel.tekst == x.domainObject.getTitle + } + Success(foundReplacement.map(_.code).getOrElse(core.kode)) + } } } @@ -252,7 +254,7 @@ trait GrepSearchService { executeAsSearchableGreps(searchToExecute) } - def getKompetansemaalReplacement(goal: GrepKompetansemaal): Try[String] = { + def getKompetansemaalReplacement(goal: GrepKompetansemaal): Try[String] = permitTry { val reuseOf = getReuseOf(goal.kode).? reuseOf match { case head :: Nil => @@ -289,7 +291,7 @@ trait GrepSearchService { result.map(r => code.code -> r) } - def getReplacements(codes: List[String]): Try[Map[String, String]] = { + def getReplacements(codes: List[String]): Try[Map[String, String]] = permitTry { val foundOldCodes = getCodesById(codes).? val foundAllCodes = foundOldCodes.map(_.code).toSet val missingCodes = codes.toSet.diff(foundAllCodes) diff --git a/search-api/src/main/scala/no/ndla/searchapi/service/search/IndexService.scala b/search-api/src/main/scala/no/ndla/searchapi/service/search/IndexService.scala index 7a931cbc48..b0248c6e9e 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/service/search/IndexService.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/service/search/IndexService.scala @@ -28,7 +28,7 @@ trait IndexService { this: Elastic4sClient & SearchApiClient & BaseIndexService & TaxonomyApiClient & GrepApiClient & Props & MyNDLAApiClient & SearchLanguage => - trait BulkIndexingService extends BaseIndexService { + abstract class BulkIndexingService extends BaseIndexService { protected def languageValuesMapping(name: String, keepRaw: Boolean = false): Seq[ElasticField] = { val subfields = List( diff --git a/search-api/src/main/scala/no/ndla/searchapi/service/search/LearningPathIndexService.scala b/search-api/src/main/scala/no/ndla/searchapi/service/search/LearningPathIndexService.scala index 9e5f17b735..0807e37f2e 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/service/search/LearningPathIndexService.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/service/search/LearningPathIndexService.scala @@ -18,20 +18,19 @@ import no.ndla.common.CirceUtil import no.ndla.common.model.api.search.SearchType import no.ndla.common.model.domain.learningpath.LearningPath import no.ndla.searchapi.Props -import no.ndla.searchapi.integration.LearningPathApiClient +import no.ndla.searchapi.integration.{LearningPathApiClient, SearchApiClient} import no.ndla.searchapi.model.domain.IndexingBundle import scala.util.Try trait LearningPathIndexService { - this: SearchConverterService & IndexService & LearningPathApiClient & Props => - import props.SearchIndex - val learningPathIndexService: LearningPathIndexService + this: SearchConverterService & IndexService & LearningPathApiClient & Props & SearchApiClient => + lazy val learningPathIndexService: LearningPathIndexService class LearningPathIndexService extends StrictLogging with IndexService[LearningPath] { - override val documentType: String = "learningpath" - override val searchIndex: String = SearchIndex(SearchType.LearningPaths) - override val apiClient: LearningPathApiClient = learningPathApiClient + override val documentType: String = "learningpath" + override val searchIndex: String = props.SearchIndex(SearchType.LearningPaths) + override val apiClient: SearchApiClient[LearningPath] = learningPathApiClient override def createIndexRequest( domainModel: LearningPath, diff --git a/search-api/src/main/scala/no/ndla/searchapi/service/search/MultiDraftSearchService.scala b/search-api/src/main/scala/no/ndla/searchapi/service/search/MultiDraftSearchService.scala index f1f122565b..e13b04f652 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/service/search/MultiDraftSearchService.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/service/search/MultiDraftSearchService.scala @@ -16,17 +16,16 @@ import com.sksamuel.elastic4s.requests.searches.queries.{Query, RangeQuery} import com.sksamuel.elastic4s.requests.searches.term.TermsQuery import com.typesafe.scalalogging.StrictLogging import no.ndla.common.errors.{ValidationException, ValidationMessage} -import no.ndla.common.implicits.TryQuestionMark +import no.ndla.common.implicits.* import no.ndla.common.model.NDLADate import no.ndla.common.model.api.search.{LearningResourceType, SearchType} -import no.ndla.common.model.domain.Content import no.ndla.common.model.domain.draft.DraftStatus import no.ndla.common.model.domain.learningpath.LearningPathStatus import no.ndla.language.Language.AllLanguages import no.ndla.language.model.Iso639 import no.ndla.network.tapir.auth.TokenUser import no.ndla.search.AggregationBuilder.{buildTermsAggregation, getAggregationsFromResult} -import no.ndla.search.Elastic4sClient +import no.ndla.search.{BaseIndexService, Elastic4sClient} import no.ndla.searchapi.Props import no.ndla.searchapi.model.api.{ErrorHandling, SubjectAggregationDTO, SubjectAggregationsDTO} import no.ndla.searchapi.model.domain.SearchResult @@ -36,18 +35,17 @@ import scala.util.{Failure, Success, Try} trait MultiDraftSearchService { this: Elastic4sClient & SearchConverterService & IndexService & SearchService & DraftIndexService & - LearningPathIndexService & Props & ErrorHandling & DraftConceptIndexService => - val multiDraftSearchService: MultiDraftSearchService + LearningPathIndexService & Props & ErrorHandling & DraftConceptIndexService & BaseIndexService => + lazy val multiDraftSearchService: MultiDraftSearchService class MultiDraftSearchService extends StrictLogging with SearchService with TaxonomyFiltering { - import props.{ElasticSearchScrollKeepAlive, SearchIndex} override val searchIndex: List[String] = List( SearchType.Drafts, SearchType.LearningPaths, SearchType.Concepts - ).map(SearchIndex) + ).map(props.SearchIndex) - override val indexServices: List[IndexService[? <: Content]] = List( + override val indexServices: List[BaseIndexService] = List( draftIndexService, learningPathIndexService, draftConceptIndexService @@ -130,7 +128,7 @@ trait MultiDraftSearchService { settings.resultTypes match { case Some(list) if list.nonEmpty => val idxs = list.map { st => - val index = SearchIndex(st) + val index = props.SearchIndex(st) val isValidIndex = searchIndex.contains(index) if (isValidIndex) Right(index) @@ -150,7 +148,7 @@ trait MultiDraftSearchService { if (errors.nonEmpty) Failure(new ValidationException(s"Got invalid `resultTypes` for endpoint", errors)) else Success(idxs.collect { case Right(i) => i }) - case _ => Success(List(SearchType.Drafts, SearchType.LearningPaths).map(SearchIndex)) + case _ => Success(List(SearchType.Drafts, SearchType.LearningPaths).map(props.SearchIndex)) } } @@ -217,7 +215,7 @@ trait MultiDraftSearchService { e4sClient.execute(searchToExecute).map(_.result.totalHits) } - def executeSearch(settings: MultiDraftSearchSettings, baseQuery: BoolQuery): Try[SearchResult] = { + def executeSearch(settings: MultiDraftSearchSettings, baseQuery: BoolQuery): Try[SearchResult] = permitTry { val searchLanguage = settings.language match { case lang if Iso639.get(lang).isSuccess && !settings.fallback => lang case _ => AllLanguages @@ -240,7 +238,7 @@ trait MultiDraftSearchService { // Only add scroll param if it is first page val searchWithScroll = if (pagination.startAt == 0 && settings.shouldScroll) { - searchToExecute.scroll(ElasticSearchScrollKeepAlive) + searchToExecute.scroll(props.ElasticSearchScrollKeepAlive) } else { searchToExecute } e4sClient.execute(searchWithScroll) match { diff --git a/search-api/src/main/scala/no/ndla/searchapi/service/search/MultiSearchService.scala b/search-api/src/main/scala/no/ndla/searchapi/service/search/MultiSearchService.scala index 8b4bf8ebf4..5475efe357 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/service/search/MultiSearchService.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/service/search/MultiSearchService.scala @@ -17,7 +17,7 @@ import com.sksamuel.elastic4s.requests.searches.queries.compound.BoolQuery import com.typesafe.scalalogging.StrictLogging import no.ndla.common.CirceUtil import no.ndla.common.errors.{ValidationException, ValidationMessage} -import no.ndla.common.implicits.TryQuestionMark +import no.ndla.common.implicits.* import no.ndla.common.model.api.search.SearchType import no.ndla.common.model.domain.Availability import no.ndla.language.Language.AllLanguages @@ -25,7 +25,7 @@ import no.ndla.language.model.Iso639 import no.ndla.mapping.License import no.ndla.network.tapir.NonEmptyString import no.ndla.search.AggregationBuilder.{buildTermsAggregation, getAggregationsFromResult} -import no.ndla.search.Elastic4sClient +import no.ndla.search.{BaseIndexService, Elastic4sClient} import no.ndla.searchapi.Props import no.ndla.searchapi.model.api.ErrorHandling import no.ndla.searchapi.model.domain.SearchResult @@ -36,23 +36,21 @@ import scala.util.{Failure, Success, Try} trait MultiSearchService { this: Elastic4sClient & SearchConverterService & SearchService & IndexService & ArticleIndexService & - LearningPathIndexService & Props & ErrorHandling & NodeIndexService => + LearningPathIndexService & Props & ErrorHandling & NodeIndexService & BaseIndexService => - val multiSearchService: MultiSearchService - - class MultiSearchService extends StrictLogging with SearchService with TaxonomyFiltering { - import props.{ElasticSearchScrollKeepAlive, SearchIndex} + lazy val multiSearchService: MultiSearchService + class MultiSearchService extends SearchService with StrictLogging with TaxonomyFiltering { override val searchIndex: List[String] = - List(SearchType.Articles, SearchType.LearningPaths, SearchType.Nodes).map(SearchIndex) - override val indexServices: List[BulkIndexingService] = List( + List(SearchType.Articles, SearchType.LearningPaths, SearchType.Nodes).map(props.SearchIndex) + override val indexServices: List[BaseIndexService] = List( articleIndexService, learningPathIndexService, nodeIndexService ) private def getIndexFilter(indexes: List[SearchType]): Query = { - val indexNames = indexes.map(SearchIndex) + val indexNames = indexes.map(props.SearchIndex) termsQuery("_index", indexNames) } @@ -137,7 +135,7 @@ trait MultiSearchService { settings.resultTypes match { case Some(list) if list.nonEmpty => val idxs = list.map { st => - val index = SearchIndex(st) + val index = props.SearchIndex(st) val isValidIndex = searchIndex.contains(index) if (isValidIndex) Right(index) @@ -157,13 +155,13 @@ trait MultiSearchService { if (errors.nonEmpty) Failure(new ValidationException(s"Got invalid `resultTypes` for endpoint", errors)) else Success(idxs.collect { case Right(i) => i }) - case _ => Success(List(SearchType.Articles, SearchType.LearningPaths).map(SearchIndex)) + case _ => Success(List(SearchType.Articles, SearchType.LearningPaths).map(props.SearchIndex)) } } - private def logShardErrors(response: RequestSuccess[SearchResponse]) = { + private def logShardErrors(response: RequestSuccess[SearchResponse]): Unit = { if (response.result.shards.failed > 0) { - response.body.map { body => + val _ = response.body.map { body => CirceUtil.tryParse(body) match { case Failure(ex) => logger.error(s"Got error parsing search response: $body", ex) @@ -180,7 +178,7 @@ trait MultiSearchService { } } - def executeSearch(settings: SearchSettings, filteredSearch: BoolQuery): Try[SearchResult] = { + def executeSearch(settings: SearchSettings, filteredSearch: BoolQuery): Try[SearchResult] = permitTry { val searchLanguage = settings.language match { case lang if Iso639.get(lang).isSuccess && !settings.fallback => lang case _ => AllLanguages @@ -203,7 +201,7 @@ trait MultiSearchService { // Only add scroll param if it is first page val searchWithScroll = if (pagination.startAt == 0 && settings.shouldScroll) { - searchToExecute.scroll(ElasticSearchScrollKeepAlive) + searchToExecute.scroll(props.ElasticSearchScrollKeepAlive) } else { searchToExecute } e4sClient.execute(searchWithScroll) match { diff --git a/search-api/src/main/scala/no/ndla/searchapi/service/search/NodeIndexService.scala b/search-api/src/main/scala/no/ndla/searchapi/service/search/NodeIndexService.scala index 824b933085..3c0409be33 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/service/search/NodeIndexService.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/service/search/NodeIndexService.scala @@ -31,11 +31,11 @@ import scala.util.{Failure, Success, Try} trait NodeIndexService { this: SearchConverterService & IndexService & Props & TaxonomyApiClient & ArticleApiClient & FrontpageApiClient & GrepApiClient => - val nodeIndexService: NodeIndexService - class NodeIndexService extends StrictLogging with BulkIndexingService { - import props.SearchIndex + lazy val nodeIndexService: NodeIndexService + + class NodeIndexService extends BulkIndexingService with StrictLogging { override val documentType: String = "nodes" - override val searchIndex: String = SearchIndex(SearchType.Nodes) + override val searchIndex: String = props.SearchIndex(SearchType.Nodes) override val MaxResultWindowOption: Int = props.ElasticSearchIndexMaxResultWindow override def getMapping: MappingDefinition = { @@ -118,7 +118,7 @@ trait NodeIndexService { .flatten } - def sendToElastic(indexingBundle: IndexingBundle, indexName: String): Try[BulkIndexResult] = { + def sendToElastic(indexingBundle: IndexingBundle, indexName: String): Try[BulkIndexResult] = permitTry { val taxBundle = indexingBundle.taxonomyBundle match { case None => taxonomyApiClient.getTaxonomyBundle(true).? case Some(value) => value diff --git a/search-api/src/main/scala/no/ndla/searchapi/service/search/SearchConverterService.scala b/search-api/src/main/scala/no/ndla/searchapi/service/search/SearchConverterService.scala index b4b60c42ab..16e5da8958 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/service/search/SearchConverterService.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/service/search/SearchConverterService.scala @@ -77,7 +77,7 @@ import scala.util.{Failure, Success, Try} trait SearchConverterService { this: DraftApiClient & TaxonomyApiClient & ConverterService & Props & MyNDLAApiClient & SearchLanguage => - val searchConverterService: SearchConverterService + lazy val searchConverterService: SearchConverterService class SearchConverterService extends StrictLogging { @@ -119,7 +119,7 @@ trait SearchConverterService { }) } - def nodeHitAsMultiSummary(hit: SearchHit, language: String): Try[NodeHitDTO] = { + def nodeHitAsMultiSummary(hit: SearchHit, language: String): Try[NodeHitDTO] = permitTry { val searchableNode = CirceUtil.tryParseAs[SearchableNode](hit.sourceAsString).? val title = searchableNode.title.getLanguageOrDefault(language).getOrElse("") val url = searchableNode.url.map(urlPath => s"${props.ndlaFrontendUrl}$urlPath") @@ -368,63 +368,68 @@ trait SearchConverterService { ) } - def asSearchableLearningPath(lp: LearningPath, indexingBundle: IndexingBundle): Try[SearchableLearningPath] = { - val taxonomyContexts = indexingBundle.taxonomyBundle match { - case Some(bundle) => - Success(getTaxonomyContexts(lp.id.get, "learningpath", bundle, filterVisibles = true, filterContexts = false)) - case None => - taxonomyApiClient.getTaxonomyContext( - s"urn:learningpath:${lp.id.get}", - filterVisibles = true, - filterContexts = false, - shouldUsePublishedTax = true - ) - } - - val favorited = getFavoritedCountFor(indexingBundle, lp.id.get.toString, List(MyNDLAResourceType.Learningpath)).? + def asSearchableLearningPath(lp: LearningPath, indexingBundle: IndexingBundle): Try[SearchableLearningPath] = + permitTry { + val taxonomyContexts = indexingBundle.taxonomyBundle match { + case Some(bundle) => + Success( + getTaxonomyContexts(lp.id.get, "learningpath", bundle, filterVisibles = true, filterContexts = false) + ) + case None => + taxonomyApiClient.getTaxonomyContext( + s"urn:learningpath:${lp.id.get}", + filterVisibles = true, + filterContexts = false, + shouldUsePublishedTax = true + ) + } - val supportedLanguages = getSupportedLanguages(lp.title, lp.description).toList - val defaultTitle = lp.title.sortBy(title => ISO639.languagePriority.reverse.indexOf(title.language)).lastOption - val license = api.learningpath.CopyrightDTO( - asLearningPathApiLicense(lp.copyright.license), - lp.copyright.contributors.map(c => AuthorDTO(c.`type`, c.name)) - ) - val contexts = asSearchableTaxonomyContexts(taxonomyContexts.getOrElse(List.empty)) + val favorited = + getFavoritedCountFor(indexingBundle, lp.id.get.toString, List(MyNDLAResourceType.Learningpath)).? - Success( - SearchableLearningPath( - id = lp.id.get, - title = model.SearchableLanguageValues(lp.title.map(t => LanguageValue(t.language, t.title))), - content = model.SearchableLanguageValues( - lp.title.map(t => LanguageValue(t.language, "*")) - ), - description = - model.SearchableLanguageValues(lp.description.map(d => LanguageValue(d.language, d.description))), - coverPhotoId = lp.coverPhotoId, - duration = lp.duration, - status = lp.status.toString, - owner = lp.owner, - verificationStatus = lp.verificationStatus.toString, - lastUpdated = lp.lastUpdated, - defaultTitle = defaultTitle.map(_.title), - tags = SearchableLanguageList(lp.tags.map(tag => LanguageValue(tag.language, tag.tags))), - learningsteps = lp.learningsteps.getOrElse(Seq.empty).map(asSearchableLearningStep).toList, - license = lp.copyright.license, - copyright = license, - isBasedOn = lp.isBasedOn, - supportedLanguages = supportedLanguages, - authors = lp.copyright.contributors.map(_.name).toList, - context = contexts.find(_.isPrimary), - contexts = contexts, - contextids = - indexingBundle.taxonomyBundle.map(getTaxonomyContexids(lp.id.get, "learningpath", _)).getOrElse(List.empty), - favorited = favorited, - learningResourceType = LearningResourceType.LearningPath, - typeName = getTypeNames(LearningResourceType.LearningPath), - priority = lp.priority + val supportedLanguages = getSupportedLanguages(lp.title, lp.description).toList + val defaultTitle = lp.title.sortBy(title => ISO639.languagePriority.reverse.indexOf(title.language)).lastOption + val license = api.learningpath.CopyrightDTO( + asLearningPathApiLicense(lp.copyright.license), + lp.copyright.contributors.map(c => AuthorDTO(c.`type`, c.name)) ) - ) - } + val contexts = asSearchableTaxonomyContexts(taxonomyContexts.getOrElse(List.empty)) + + Success( + SearchableLearningPath( + id = lp.id.get, + title = model.SearchableLanguageValues(lp.title.map(t => LanguageValue(t.language, t.title))), + content = model.SearchableLanguageValues( + lp.title.map(t => LanguageValue(t.language, "*")) + ), + description = + model.SearchableLanguageValues(lp.description.map(d => LanguageValue(d.language, d.description))), + coverPhotoId = lp.coverPhotoId, + duration = lp.duration, + status = lp.status.toString, + owner = lp.owner, + verificationStatus = lp.verificationStatus.toString, + lastUpdated = lp.lastUpdated, + defaultTitle = defaultTitle.map(_.title), + tags = SearchableLanguageList(lp.tags.map(tag => LanguageValue(tag.language, tag.tags))), + learningsteps = lp.learningsteps.getOrElse(Seq.empty).map(asSearchableLearningStep).toList, + license = lp.copyright.license, + copyright = license, + isBasedOn = lp.isBasedOn, + supportedLanguages = supportedLanguages, + authors = lp.copyright.contributors.map(_.name).toList, + context = contexts.find(_.isPrimary), + contexts = contexts, + contextids = indexingBundle.taxonomyBundle + .map(getTaxonomyContexids(lp.id.get, "learningpath", _)) + .getOrElse(List.empty), + favorited = favorited, + learningResourceType = LearningResourceType.LearningPath, + typeName = getTypeNames(LearningResourceType.LearningPath), + priority = lp.priority + ) + ) + } private def getFavoritedCountFor( indexingBundle: IndexingBundle, @@ -440,7 +445,7 @@ trait SearchConverterService { } } - def asSearchableConcept(c: Concept, indexingBundle: IndexingBundle): Try[SearchableConcept] = { + def asSearchableConcept(c: Concept, indexingBundle: IndexingBundle): Try[SearchableConcept] = permitTry { val title = model.SearchableLanguageValues(c.title.map(t => LanguageValue(t.language, toPlaintext(t.title)))) val content = SearchableLanguageValues.fromFieldsMap(c.content)(toPlaintext) val tags = SearchableLanguageList.fromFields(c.tags) @@ -483,7 +488,7 @@ trait SearchConverterService { ) } - def asSearchableDraft(draft: Draft, indexingBundle: IndexingBundle): Try[SearchableDraft] = { + def asSearchableDraft(draft: Draft, indexingBundle: IndexingBundle): Try[SearchableDraft] = permitTry { val taxonomyContexts = { val draftId = draft.id.get indexingBundle.taxonomyBundle match { @@ -727,7 +732,7 @@ trait SearchConverterService { hit: SearchHit, language: String, filterInactive: Boolean - ): Try[MultiSearchSummaryDTO] = { + ): Try[MultiSearchSummaryDTO] = permitTry { val searchableArticle = CirceUtil.tryParseAs[SearchableArticle](hit.sourceAsString).? val context = searchableArticle.context.map(c => searchableContextToApiContext(c, language)) @@ -801,7 +806,7 @@ trait SearchConverterService { hit: SearchHit, language: String, filterInactive: Boolean - ): Try[MultiSearchSummaryDTO] = { + ): Try[MultiSearchSummaryDTO] = permitTry { val searchableDraft = CirceUtil.tryParseAs[SearchableDraft](hit.sourceAsString).? val context = searchableDraft.context.map(c => searchableContextToApiContext(c, language)) @@ -889,7 +894,7 @@ trait SearchConverterService { hit: SearchHit, language: String, filterInactive: Boolean - ): Try[MultiSearchSummaryDTO] = { + ): Try[MultiSearchSummaryDTO] = permitTry { val searchableLearningPath = CirceUtil.tryParseAs[SearchableLearningPath](hit.sourceAsString).? val context = searchableLearningPath.context.map(c => searchableContextToApiContext(c, language)) @@ -958,7 +963,7 @@ trait SearchConverterService { ) } - def conceptHitAsMultiSummary(hit: SearchHit, language: String): Try[MultiSearchSummaryDTO] = { + def conceptHitAsMultiSummary(hit: SearchHit, language: String): Try[MultiSearchSummaryDTO] = permitTry { val searchableConcept = CirceUtil.tryParseAs[SearchableConcept](hit.sourceAsString).? val titles = diff --git a/search-api/src/main/scala/no/ndla/searchapi/service/search/SearchService.scala b/search-api/src/main/scala/no/ndla/searchapi/service/search/SearchService.scala index 8241c35cf6..f397aee3da 100644 --- a/search-api/src/main/scala/no/ndla/searchapi/service/search/SearchService.scala +++ b/search-api/src/main/scala/no/ndla/searchapi/service/search/SearchService.scala @@ -46,7 +46,6 @@ trait SearchService { ErrorHandling & SearchLanguage => trait SearchService { - import props.{DefaultLanguage, ElasticSearchScrollKeepAlive, MaxPageSize} val searchIndex: List[String] val indexServices: List[BaseIndexService] @@ -232,7 +231,7 @@ trait SearchService { query .map(q => { val searchLanguage = - if (language == Language.AllLanguages || fallback) DefaultLanguage else language + if (language == Language.AllLanguages || fallback) props.DefaultLanguage else language Seq( suggestion(q, "title", searchLanguage), suggestion(q, "content", searchLanguage) @@ -278,7 +277,7 @@ trait SearchService { def scroll(scrollId: String, language: String): Try[SearchResult] = { e4sClient .execute { - searchScroll(scrollId, ElasticSearchScrollKeepAlive) + searchScroll(scrollId, props.ElasticSearchScrollKeepAlive) } .flatMap(response => { getHits(response.result, language, filterInactive = false).map(hits => { @@ -305,7 +304,7 @@ trait SearchService { protected def defaultSort(default: String, withLanguage: String, order: SortOrder, language: String): FieldSort = { val sortLanguage = language match { - case Language.NoLanguage => DefaultLanguage + case Language.NoLanguage => props.DefaultLanguage case _ => language } @@ -355,7 +354,7 @@ trait SearchService { private val maxResultWindow = props.ElasticSearchIndexMaxResultWindow def getStartAtAndNumResults(page: Int, pageSize: Int): Try[SearchPagination] = { - val safePageSize = max(pageSize.min(MaxPageSize), 0) + val safePageSize = max(pageSize.min(props.MaxPageSize), 0) val safePage = page.max(1) val startAt = (safePage - 1) * safePageSize val resultWindow = startAt + safePageSize diff --git a/search-api/src/test/scala/no/ndla/searchapi/TestEnvironment.scala b/search-api/src/test/scala/no/ndla/searchapi/TestEnvironment.scala index e10b0a0aef..241fe7efeb 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/TestEnvironment.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/TestEnvironment.scala @@ -70,42 +70,42 @@ trait TestEnvironment with Props { override lazy val props: TestProps = new TestProps - val searchController: SearchController = mock[SearchController] - val internController: InternController = mock[InternController] - val healthController: TapirHealthController = mock[TapirHealthController] + override lazy val searchController: SearchController = mock[SearchController] + override lazy val internController: InternController = mock[InternController] + override lazy val healthController: TapirHealthController = mock[TapirHealthController] - val ndlaClient: NdlaClient = mock[NdlaClient] - var e4sClient: NdlaE4sClient = mock[NdlaE4sClient] + override lazy val ndlaClient: NdlaClient = mock[NdlaClient] + var e4sClient: NdlaE4sClient = mock[NdlaE4sClient] - val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] + override lazy val myndlaApiClient: MyNDLAApiClient = mock[MyNDLAApiClient] - val taxonomyApiClient: TaxonomyApiClient = mock[TaxonomyApiClient] - val grepApiClient: GrepApiClient = mock[GrepApiClient] + override lazy val taxonomyApiClient: TaxonomyApiClient = mock[TaxonomyApiClient] + override lazy val grepApiClient: GrepApiClient = mock[GrepApiClient] - val draftApiClient: DraftApiClient = mock[DraftApiClient] - val learningPathApiClient: LearningPathApiClient = mock[LearningPathApiClient] - val articleApiClient: ArticleApiClient = mock[ArticleApiClient] - val draftConceptApiClient: DraftConceptApiClient = mock[DraftConceptApiClient] - val feideApiClient: FeideApiClient = mock[FeideApiClient] - val redisClient: RedisClient = mock[RedisClient] - val frontpageApiClient: FrontpageApiClient = mock[FrontpageApiClient] - val DBUtil: DBUtility = mock[DBUtility] + override lazy val draftApiClient: DraftApiClient = mock[DraftApiClient] + override lazy val learningPathApiClient: LearningPathApiClient = mock[LearningPathApiClient] + override lazy val articleApiClient: ArticleApiClient = mock[ArticleApiClient] + override lazy val draftConceptApiClient: DraftConceptApiClient = mock[DraftConceptApiClient] + override lazy val feideApiClient: FeideApiClient = mock[FeideApiClient] + override lazy val redisClient: RedisClient = mock[RedisClient] + override lazy val frontpageApiClient: FrontpageApiClient = mock[FrontpageApiClient] + override lazy val DBUtil: DBUtility = mock[DBUtility] - val clock: SystemClock = mock[SystemClock] + override lazy val clock: SystemClock = mock[SystemClock] - val converterService: ConverterService = mock[ConverterService] - val searchConverterService: SearchConverterService = mock[SearchConverterService] - val multiSearchService: MultiSearchService = mock[MultiSearchService] - val grepSearchService: GrepSearchService = mock[GrepSearchService] + override lazy val converterService: ConverterService = mock[ConverterService] + override lazy val searchConverterService: SearchConverterService = mock[SearchConverterService] + override lazy val multiSearchService: MultiSearchService = mock[MultiSearchService] + override lazy val grepSearchService: GrepSearchService = mock[GrepSearchService] - val articleIndexService: ArticleIndexService = mock[ArticleIndexService] - val learningPathIndexService: LearningPathIndexService = mock[LearningPathIndexService] - val draftIndexService: DraftIndexService = mock[DraftIndexService] - val draftConceptIndexService: DraftConceptIndexService = mock[DraftConceptIndexService] - val grepIndexService: GrepIndexService = mock[GrepIndexService] - val nodeIndexService: NodeIndexService = mock[NodeIndexService] + override lazy val articleIndexService: ArticleIndexService = mock[ArticleIndexService] + override lazy val learningPathIndexService: LearningPathIndexService = mock[LearningPathIndexService] + override lazy val draftIndexService: DraftIndexService = mock[DraftIndexService] + override lazy val draftConceptIndexService: DraftConceptIndexService = mock[DraftConceptIndexService] + override lazy val grepIndexService: GrepIndexService = mock[GrepIndexService] + override lazy val nodeIndexService: NodeIndexService = mock[NodeIndexService] - val multiDraftSearchService: MultiDraftSearchService = mock[MultiDraftSearchService] + override lazy val multiDraftSearchService: MultiDraftSearchService = mock[MultiDraftSearchService] override def services: List[TapirController] = List() val swagger: SwaggerController = mock[SwaggerController] diff --git a/search-api/src/test/scala/no/ndla/searchapi/controller/InternControllerTest.scala b/search-api/src/test/scala/no/ndla/searchapi/controller/InternControllerTest.scala index a96fa5ee54..6bb96a9dc0 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/controller/InternControllerTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/controller/InternControllerTest.scala @@ -12,6 +12,6 @@ import no.ndla.searchapi.{TestEnvironment, UnitSuite} import no.ndla.tapirtesting.TapirControllerTest class InternControllerTest extends UnitSuite with TestEnvironment with TapirControllerTest { - override val converterService = new ConverterService - val controller: InternController = new InternController + override lazy val converterService = new ConverterService + override val controller: InternController = new InternController } diff --git a/search-api/src/test/scala/no/ndla/searchapi/controller/SearchControllerTest.scala b/search-api/src/test/scala/no/ndla/searchapi/controller/SearchControllerTest.scala index fce284e19e..b926567d8c 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/controller/SearchControllerTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/controller/SearchControllerTest.scala @@ -25,8 +25,8 @@ import java.time.Month import scala.util.Success class SearchControllerTest extends UnitSuite with TestEnvironment with TapirControllerTest { - override val converterService = new ConverterService - val controller: SearchController = new SearchController() + override lazy val converterService = new ConverterService + override val controller: SearchController = new SearchController() override def beforeEach(): Unit = { reset(clock) diff --git a/search-api/src/test/scala/no/ndla/searchapi/service/ConverterServiceTest.scala b/search-api/src/test/scala/no/ndla/searchapi/service/ConverterServiceTest.scala index 698d519bcf..a07a9e614f 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/service/ConverterServiceTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/service/ConverterServiceTest.scala @@ -14,7 +14,7 @@ import no.ndla.searchapi.model.api class ConverterServiceTest extends UnitSuite with TestEnvironment { ApplicationUrl.applicationUrl.set("https://unit-test") - override val converterService = new ConverterService + override lazy val converterService = new ConverterService test("searchResultToApiModel should return the api model of the corresponding input domain model") { converterService.searchResultToApiModel(TestData.sampleArticleSearch).isInstanceOf[api.ArticleResultsDTO] diff --git a/search-api/src/test/scala/no/ndla/searchapi/service/search/ArticleIndexServiceTest.scala b/search-api/src/test/scala/no/ndla/searchapi/service/search/ArticleIndexServiceTest.scala index d1dc4ece50..90f488777d 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/service/search/ArticleIndexServiceTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/service/search/ArticleIndexServiceTest.scala @@ -25,7 +25,7 @@ import scala.util.Success class ArticleIndexServiceTest extends ElasticsearchIntegrationSuite with UnitSuite with TestEnvironment { e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("")) - override val articleIndexService: ArticleIndexService = new ArticleIndexService { + override lazy val articleIndexService: ArticleIndexService = new ArticleIndexService { override val indexShards = 1 } @@ -35,8 +35,8 @@ class ArticleIndexServiceTest extends ElasticsearchIntegrationSuite with UnitSui articleIndexService.createIndexWithGeneratedName } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService test("That articles are indexed correctly") { articleIndexService @@ -74,7 +74,7 @@ class ArticleIndexServiceTest extends ElasticsearchIntegrationSuite with UnitSui val Success(response) = e4sClient.execute { search(articleIndexService.searchIndex) - } + }: @unchecked val sources = response.result.hits.hits.map(_.sourceAsString) val articles = sources.map(source => CirceUtil.unsafeParseAs[SearchableArticle](source)) @@ -87,7 +87,7 @@ class ArticleIndexServiceTest extends ElasticsearchIntegrationSuite with UnitSui Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked val Success(expectedArticle6) = searchConverterService.asSearchableArticle( article6, @@ -96,7 +96,7 @@ class ArticleIndexServiceTest extends ElasticsearchIntegrationSuite with UnitSui Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked val Success(expectedArticle7) = searchConverterService.asSearchableArticle( article7, @@ -105,11 +105,11 @@ class ArticleIndexServiceTest extends ElasticsearchIntegrationSuite with UnitSui Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked - val Some(actualArticle5) = articles.find(p => p.id == article5.id.get) - val Some(actualArticle6) = articles.find(p => p.id == article6.id.get) - val Some(actualArticle7) = articles.find(p => p.id == article7.id.get) + val Some(actualArticle5) = articles.find(p => p.id == article5.id.get): @unchecked + val Some(actualArticle6) = articles.find(p => p.id == article6.id.get): @unchecked + val Some(actualArticle7) = articles.find(p => p.id == article7.id.get): @unchecked actualArticle5 should be(expectedArticle5) actualArticle6 should be(expectedArticle6) diff --git a/search-api/src/test/scala/no/ndla/searchapi/service/search/DraftConceptIndexServiceTest.scala b/search-api/src/test/scala/no/ndla/searchapi/service/search/DraftConceptIndexServiceTest.scala index 9d94a9641c..75bc37c557 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/service/search/DraftConceptIndexServiceTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/service/search/DraftConceptIndexServiceTest.scala @@ -21,7 +21,7 @@ import no.ndla.searchapi.{TestData, TestEnvironment, UnitSuite} class DraftConceptIndexServiceTest extends ElasticsearchIntegrationSuite with UnitSuite with TestEnvironment { e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("")) - override val draftConceptIndexService: DraftConceptIndexService = new DraftConceptIndexService { + override lazy val draftConceptIndexService: DraftConceptIndexService = new DraftConceptIndexService { override val indexShards = 1 } @@ -31,8 +31,8 @@ class DraftConceptIndexServiceTest extends ElasticsearchIntegrationSuite with Un articleIndexService.createIndexWithGeneratedName } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService test("That mapping contains every field after serialization") { val languageValues = SearchableLanguageValues(Seq(LanguageValue("nb", "hei"), LanguageValue("en", "hå"))) diff --git a/search-api/src/test/scala/no/ndla/searchapi/service/search/DraftIndexServiceTest.scala b/search-api/src/test/scala/no/ndla/searchapi/service/search/DraftIndexServiceTest.scala index e38a1b94d0..0f79e3d4fd 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/service/search/DraftIndexServiceTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/service/search/DraftIndexServiceTest.scala @@ -26,7 +26,7 @@ class DraftIndexServiceTest extends ElasticsearchIntegrationSuite with UnitSuite e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("")) - override val draftIndexService: DraftIndexService = new DraftIndexService { + override lazy val draftIndexService: DraftIndexService = new DraftIndexService { override val indexShards = 1 } @@ -41,8 +41,8 @@ class DraftIndexServiceTest extends ElasticsearchIntegrationSuite with UnitSuite when(myndlaApiClient.getStatsFor(any, any)).thenReturn(Success(List.empty)) } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService test("That mapping contains every field after serialization") { val now = NDLADate.now() diff --git a/search-api/src/test/scala/no/ndla/searchapi/service/search/GrepSearchServiceTest.scala b/search-api/src/test/scala/no/ndla/searchapi/service/search/GrepSearchServiceTest.scala index 1959f770f4..809f08b3a4 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/service/search/GrepSearchServiceTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/service/search/GrepSearchServiceTest.scala @@ -20,12 +20,12 @@ import no.ndla.searchapi.model.grep.* class GrepSearchServiceTest extends ElasticsearchIntegrationSuite with TestEnvironment { e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("")) - override val grepIndexService: GrepIndexService = new GrepIndexService { + override lazy val grepIndexService: GrepIndexService = new GrepIndexService { override val indexShards = 1 } - override val grepSearchService = new GrepSearchService - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val grepSearchService = new GrepSearchService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService override def beforeEach(): Unit = { if (elasticSearchContainer.isSuccess) { diff --git a/search-api/src/test/scala/no/ndla/searchapi/service/search/IndexServiceTest.scala b/search-api/src/test/scala/no/ndla/searchapi/service/search/IndexServiceTest.scala index 699d66307f..fa17a690b9 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/service/search/IndexServiceTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/service/search/IndexServiceTest.scala @@ -37,7 +37,7 @@ class IndexServiceTest extends ElasticsearchIntegrationSuite with TestEnvironmen searchIndexService.cleanupIndexes(s"${testIndexPrefix}_article") - val Success(response) = e4sClient.execute(getAliases()) + val Success(response) = e4sClient.execute(getAliases()): @unchecked val result = response.result.mappings val indexNames = result.map(_._1.name) diff --git a/search-api/src/test/scala/no/ndla/searchapi/service/search/LearningPathIndexServiceTest.scala b/search-api/src/test/scala/no/ndla/searchapi/service/search/LearningPathIndexServiceTest.scala index 7d553b3c3c..8b260712ec 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/service/search/LearningPathIndexServiceTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/service/search/LearningPathIndexServiceTest.scala @@ -31,7 +31,7 @@ class LearningPathIndexServiceTest extends ElasticsearchIntegrationSuite with Un e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("")) - override val learningPathIndexService: LearningPathIndexService = new LearningPathIndexService { + override lazy val learningPathIndexService: LearningPathIndexService = new LearningPathIndexService { override val indexShards = 1 } @@ -46,8 +46,8 @@ class LearningPathIndexServiceTest extends ElasticsearchIntegrationSuite with Un when(myndlaApiClient.getStatsFor(any, any)).thenReturn(Success(List.empty)) } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService test("That mapping contains every field after serialization") { val domainLearningPath = TestData.learningPath1.copy( diff --git a/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiDraftSearchServiceAtomicTest.scala b/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiDraftSearchServiceAtomicTest.scala index 5fe4633ae8..32648b86bc 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiDraftSearchServiceAtomicTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiDraftSearchServiceAtomicTest.scala @@ -17,7 +17,6 @@ import no.ndla.common.model.api.search.{ApiTaxonomyContextDTO, LearningResourceT import no.ndla.common.model.domain.* import no.ndla.common.model.domain.concept.{ConceptContent, ConceptType} import no.ndla.common.model.domain.draft.{Draft, DraftStatus} -import no.ndla.common.model.domain.{EditorNote, Priority, Responsible, RevisionMeta, RevisionStatus} import no.ndla.network.tapir.NonEmptyString import no.ndla.scalatestsuite.ElasticsearchIntegrationSuite import no.ndla.search.model.{LanguageValue, SearchableLanguageList, SearchableLanguageValues} @@ -33,23 +32,23 @@ import scala.util.{Success, Try} class MultiDraftSearchServiceAtomicTest extends ElasticsearchIntegrationSuite with TestEnvironment { e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("")) - override val articleIndexService: ArticleIndexService = new ArticleIndexService { + override lazy val articleIndexService: ArticleIndexService = new ArticleIndexService { override val indexShards = 1 } - override val draftIndexService: DraftIndexService = new DraftIndexService { + override lazy val draftIndexService: DraftIndexService = new DraftIndexService { override val indexShards = 1 } - override val learningPathIndexService: LearningPathIndexService = new LearningPathIndexService { + override lazy val learningPathIndexService: LearningPathIndexService = new LearningPathIndexService { override val indexShards = 1 } - override val draftConceptIndexService: DraftConceptIndexService = new DraftConceptIndexService { + override lazy val draftConceptIndexService: DraftConceptIndexService = new DraftConceptIndexService { override val indexShards = 1 } - override val multiDraftSearchService: MultiDraftSearchService = new MultiDraftSearchService { + override lazy val multiDraftSearchService: MultiDraftSearchService = new MultiDraftSearchService { override val enableExplanations = true } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService override def beforeEach(): Unit = { if (elasticSearchContainer.isSuccess) { @@ -104,7 +103,7 @@ class MultiDraftSearchServiceAtomicTest extends ElasticsearchIntegrationSuite wi val Success(search1) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(embedId = Some("3"), embedResource = List("content-link")) - ) + ): @unchecked search1.totalCount should be(1) search1.summaryResults.map(_.id) should be(List(2)) @@ -112,7 +111,7 @@ class MultiDraftSearchServiceAtomicTest extends ElasticsearchIntegrationSuite wi val Success(search2) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(embedId = Some("3"), embedResource = List("content-link", "related-content")) - ) + ): @unchecked search2.totalCount should be(2) search2.summaryResults.map(_.id) should be(List(1, 2)) @@ -182,7 +181,7 @@ class MultiDraftSearchServiceAtomicTest extends ElasticsearchIntegrationSuite wi val Success(search1) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(sort = Sort.ByRevisionDateAsc) - ) + ): @unchecked search1.totalCount should be(4) search1.summaryResults.map(_.id) should be(List(3, 1, 2, 4)) @@ -190,7 +189,7 @@ class MultiDraftSearchServiceAtomicTest extends ElasticsearchIntegrationSuite wi val Success(search2) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(sort = Sort.ByRevisionDateDesc) - ) + ): @unchecked search2.totalCount should be(4) search2.summaryResults.map(_.id) should be(List(1, 3, 2, 4)) @@ -260,7 +259,7 @@ class MultiDraftSearchServiceAtomicTest extends ElasticsearchIntegrationSuite wi val Success(search1) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(query = Some(NonEmptyString.fromString("trylleformel").get)) - ) + ): @unchecked search1.totalCount should be(1) search1.summaryResults.map(_.id) should be(List(3)) @@ -917,7 +916,7 @@ class MultiDraftSearchServiceAtomicTest extends ElasticsearchIntegrationSuite wi val Success(search1) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(embedId = Some(videoId), embedResource = List("video")) - ) + ): @unchecked search1.totalCount should be(1) search1.summaryResults.map(_.id) should be(List(1)) @@ -982,17 +981,17 @@ class MultiDraftSearchServiceAtomicTest extends ElasticsearchIntegrationSuite wi val Success(search1) = searchService.matchingQuery( multiDraftSearchSettings.copy(sort = Sort.ByParentTopicNameAsc) - ) + ): @unchecked search1.summaryResults.map(_.id) should be(List(1, 2, 3)) val Success(search2) = searchService.matchingQuery( multiDraftSearchSettings.copy(sort = Sort.ByPrimaryRootAsc) - ) + ): @unchecked search2.summaryResults.map(_.id) should be(List(2, 3, 1)) val Success(search3) = searchService.matchingQuery( multiDraftSearchSettings.copy(sort = Sort.ByResourceTypeAsc) - ) + ): @unchecked search3.summaryResults.map(_.id) should be(List(3, 1, 2)) } diff --git a/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiDraftSearchServiceTest.scala b/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiDraftSearchServiceTest.scala index 8c5b4a42b9..98681e2e07 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiDraftSearchServiceTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiDraftSearchServiceTest.scala @@ -28,26 +28,24 @@ import no.ndla.searchapi.SearchTestUtility.* import scala.util.Success class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with TestEnvironment { - import props.DefaultPageSize - e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("")) - override val articleIndexService: ArticleIndexService = new ArticleIndexService { + override lazy val articleIndexService: ArticleIndexService = new ArticleIndexService { override val indexShards = 1 } - override val draftIndexService: DraftIndexService = new DraftIndexService { + override lazy val draftIndexService: DraftIndexService = new DraftIndexService { override val indexShards = 1 } - override val learningPathIndexService: LearningPathIndexService = new LearningPathIndexService { + override lazy val learningPathIndexService: LearningPathIndexService = new LearningPathIndexService { override val indexShards = 1 } - override val draftConceptIndexService: DraftConceptIndexService = new DraftConceptIndexService { + override lazy val draftConceptIndexService: DraftConceptIndexService = new DraftConceptIndexService { override val indexShards = 1 } - override val multiDraftSearchService: MultiDraftSearchService = new MultiDraftSearchService { + override lazy val multiDraftSearchService: MultiDraftSearchService = new MultiDraftSearchService { override val enableExplanations = true } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService val indexingBundle: IndexingBundle = IndexingBundle(Some(emptyGrepBundle), Some(taxonomyTestBundle), Some(TestData.myndlaTestBundle)) @@ -103,43 +101,47 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That getStartAtAndNumResults returns the correct calculated start at for page and page-size") { val page = 74 - val expectedStartAt = (page - 1) * DefaultPageSize - multiDraftSearchService.getStartAtAndNumResults(page, DefaultPageSize) should equal( - Success(SearchPagination(page, DefaultPageSize, expectedStartAt)) + val expectedStartAt = (page - 1) * props.DefaultPageSize + multiDraftSearchService.getStartAtAndNumResults(page, props.DefaultPageSize) should equal( + Success(SearchPagination(page, props.DefaultPageSize, expectedStartAt)) ) } test("That all returns all documents ordered by id ascending") { - val Success(results) = multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(sort = Sort.ByIdAsc)) - val expected = idsForLang("nb").sorted + val Success(results) = + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(sort = Sort.ByIdAsc)): @unchecked + val expected = idsForLang("nb").sorted results.totalCount should be(expected.size) results.summaryResults.map(_.id) should be(expected) } test("That all returns all documents ordered by id descending") { - val Success(results) = multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(sort = Sort.ByIdDesc)) - val expected = idsForLang("nb").sorted.reverse + val Success(results) = + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(sort = Sort.ByIdDesc)): @unchecked + val expected = idsForLang("nb").sorted.reverse results.totalCount should be(expected.size) results.summaryResults.map(_.id) should be(expected) } test("That all returns all documents ordered by title ascending") { - val Success(results) = multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(sort = Sort.ByTitleAsc)) - val expected = titlesForLang("nb").sorted + val Success(results) = + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(sort = Sort.ByTitleAsc)): @unchecked + val expected = titlesForLang("nb").sorted results.totalCount should be(expected.size) results.summaryResults.map(_.title.title) should be(expected) } test("That all returns all documents ordered by title descending") { - val Success(results) = multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(sort = Sort.ByTitleDesc)) - val expected = titlesForLang("nb").sorted.reverse + val Success(results) = + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(sort = Sort.ByTitleDesc)): @unchecked + val expected = titlesForLang("nb").sorted.reverse results.totalCount should be(expected.size) results.summaryResults.map(_.title.title) should be(expected) } test("That all returns all documents ordered by lastUpdated descending") { val Success(results) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(sort = Sort.ByLastUpdatedDesc)) + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(sort = Sort.ByLastUpdatedDesc)): @unchecked val expected = idsForLang("nb") results.totalCount should be(expected.size) results.summaryResults.head.id should be(4) @@ -148,7 +150,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That all returns all documents ordered by lastUpdated ascending") { val Success(results) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(sort = Sort.ByLastUpdatedAsc)) + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(sort = Sort.ByLastUpdatedAsc)): @unchecked val expected = idsForLang("nb") results.totalCount should be(expected.size) results.summaryResults.head.id should be(5) @@ -158,9 +160,13 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That paging returns only hits on current page and not more than page-size") { val Success(page1) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(page = 1, pageSize = 2, sort = Sort.ByIdAsc)) + multiDraftSearchService.matchingQuery( + multiDraftSearchSettings.copy(page = 1, pageSize = 2, sort = Sort.ByIdAsc) + ): @unchecked val Success(page2) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(page = 2, pageSize = 2, sort = Sort.ByIdAsc)) + multiDraftSearchService.matchingQuery( + multiDraftSearchSettings.copy(page = 2, pageSize = 2, sort = Sort.ByIdAsc) + ): @unchecked val expected = idsForLang("nb") val hits1 = page1.summaryResults val hits2 = page2.summaryResults @@ -180,7 +186,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(results) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(query = Some(NonEmptyString.fromString("bil").get), sort = Sort.ByRelevanceDesc) - ) + ): @unchecked results.totalCount should be(3) results.summaryResults.map(_.id) should be(Seq(1, 5, 3)) } @@ -193,7 +199,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes sort = Sort.ByRelevanceDesc, withIdIn = List(3) ) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(3) @@ -204,7 +210,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(results) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(query = Some(NonEmptyString.fromString("Pingvinen").get), sort = Sort.ByTitleAsc) - ) + ): @unchecked results.summaryResults.map(_.contexts.head.contextType) should be(Seq("learningpath", "standard")) results.summaryResults.map(_.id) should be(Seq(1, 2)) @@ -215,7 +221,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(results) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(sort = Sort.ByIdAsc, userFilter = List("ndalId54321")) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(13) hits.head.id should be(1) @@ -227,7 +233,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(results) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(sort = Sort.ByIdAsc, userFilter = List("ndalId12345")) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(5) @@ -238,7 +244,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(results) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(query = Some(NonEmptyString.fromString("and").get), sort = Sort.ByTitleAsc) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(2) hits.head.id should be(3) @@ -254,7 +260,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes license = Some(License.Copyrighted.toString), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(4) @@ -267,7 +273,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes query = Some(NonEmptyString.fromString("bilde + bil").get), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits1 = search1.summaryResults hits1.map(_.id) should equal(Seq(1, 3, 5)) @@ -277,7 +283,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes query = Some(NonEmptyString.fromString("batmen + bil").get), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits2 = search2.summaryResults hits2.map(_.id) should equal(Seq(1)) } @@ -288,7 +294,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes query = Some(NonEmptyString.fromString("-flaggermusmann + (bil + bilde)").get), sort = Sort.ByTitleAsc ) - ) + ): @unchecked search1.summaryResults.map(_.id) should equal(Seq(3, 5)) val Success(search2) = @@ -297,7 +303,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes query = Some(NonEmptyString.fromString("bil + -hulken").get), sort = Sort.ByTitleAsc ) - ) + ): @unchecked search2.summaryResults.map(_.id) should equal(Seq(1, 3)) } @@ -308,7 +314,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes query = Some(NonEmptyString.fromString("mareritt+ragnarok").get), sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked val hits = search.summaryResults hits.map(_.id) should equal(Seq(9, 8)) } @@ -316,14 +322,16 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("Search for all languages should return all articles in different languages") { val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = AllLanguages, pageSize = 100, sort = Sort.ByTitleAsc) - ) + ): @unchecked search.totalCount should equal(titlesForLang("*").size) } test("Search for all languages should return all articles in correct language") { val Success(search) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(language = AllLanguages, pageSize = 100)) + multiDraftSearchService.matchingQuery( + multiDraftSearchSettings.copy(language = AllLanguages, pageSize = 100) + ): @unchecked val hits = search.summaryResults search.totalCount should equal(idsForLang("*").size) @@ -358,7 +366,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes pageSize = 100, sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits = search.summaryResults search.totalCount should equal(1) @@ -369,7 +377,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(searchEn) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings .copy(query = Some(NonEmptyString.fromString("Cats").get), language = AllLanguages, sort = Sort.ByRelevanceDesc) - ) + ): @unchecked val Success(searchNb) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings .copy( @@ -377,7 +385,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes language = AllLanguages, sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked searchEn.totalCount should equal(1) searchEn.summaryResults.head.id should equal(11) @@ -393,7 +401,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("Searching with query for unknown language should return nothing") { val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = "mix", sort = Sort.ByRelevanceDesc) - ) + ): @unchecked search.totalCount should equal(0) } @@ -406,7 +414,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes language = AllLanguages, sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked search.totalCount should equal(1) search.summaryResults.head.id should equal(11) @@ -418,7 +426,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(fallback = true, language = "en", withIdIn = List(9, 10, 11)) - ) + ): @unchecked search.totalCount should equal(3) search.summaryResults.head.id should equal(9) @@ -430,21 +438,25 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes } test("That private learningpaths are only returned if user is owner") { - val Success(search) = multiDraftSearchService.matchingQuery( - multiDraftSearchSettings.copy( - resultTypes = Some(List(SearchType.LearningPaths)), - fallback = true + val search = multiDraftSearchService + .matchingQuery( + multiDraftSearchSettings.copy( + resultTypes = Some(List(SearchType.LearningPaths)), + fallback = true + ) ) - ) + .get search.totalCount should equal(6) - val Success(search2) = multiDraftSearchService.matchingQuery( - multiDraftSearchSettings.copy( - resultTypes = Some(List(SearchType.LearningPaths)), - fallback = true, - user = TokenUser("private", Set(LEARNINGPATH_API_WRITE), None) + val search2 = multiDraftSearchService + .matchingQuery( + multiDraftSearchSettings.copy( + resultTypes = Some(List(SearchType.LearningPaths)), + fallback = true, + user = TokenUser("private", Set(LEARNINGPATH_API_WRITE), None) + ) ) - ) + .get search2.totalCount should equal(7) } @@ -452,7 +464,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = "*", subjects = List("urn:subject:2")) - ) + ): @unchecked search.totalCount should be(7) search.summaryResults.map(_.id) should be(Seq(1, 5, 5, 6, 7, 11, 12)) } @@ -461,7 +473,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = "*", subjects = List("urn:subject:2"), filterInactive = true) - ) + ): @unchecked search.totalCount should be(4) search.summaryResults.flatMap(_.contexts).toList.length should be(5) search.summaryResults.map(_.id) should be(Seq(5, 6, 11, 12)) @@ -471,14 +483,14 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(subjects = List("urn:subject:2", "urn:subject:1")) - ) + ): @unchecked search.totalCount should be(15) search.summaryResults.map(_.id) should be(Seq(1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 11, 12)) } test("That filtering for invisible subjects returns all drafts with any of listed subjects") { val Success(search) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(subjects = List("urn:subject:3"))) + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(subjects = List("urn:subject:3"))): @unchecked search.totalCount should be(2) search.summaryResults.map(_.id) should be(Seq(1, 15)) } @@ -487,21 +499,21 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(resourceTypes = List("urn:resourcetype:academicArticle")) - ) + ): @unchecked search.totalCount should be(2) search.summaryResults.map(_.id) should be(Seq(2, 5)) val Success(search2) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(resourceTypes = List("urn:resourcetype:subjectMaterial")) - ) + ): @unchecked search2.totalCount should be(8) search2.summaryResults.map(_.id) should be(Seq(1, 2, 3, 4, 5, 6, 7, 12)) val Success(search3) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(resourceTypes = List("urn:resourcetype:learningpath")) - ) + ): @unchecked search3.totalCount should be(4) search3.summaryResults.map(_.id) should be(Seq(1, 2, 3, 4)) } @@ -512,7 +524,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes language = "*", learningResourceTypes = List(LearningResourceType.Article, LearningResourceType.TopicArticle) ) - ) + ): @unchecked search.summaryResults.map(_.id) should be(Seq(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15)) search.totalCount should be(14) @@ -524,7 +536,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes language = "*", filterInactive = false ) - ) + ): @unchecked val totalCount = search.totalCount val ids = search.summaryResults.map(_.id).length @@ -535,7 +547,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes language = "*", filterInactive = true ) - ) + ): @unchecked totalCount should be > search2.totalCount ids should be > search2.summaryResults.map(_.id).length @@ -545,10 +557,10 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That filtering on learning-resource-type works") { val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = "*", learningResourceTypes = List(LearningResourceType.Article)) - ) + ): @unchecked val Success(search2) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = "*", learningResourceTypes = List(LearningResourceType.TopicArticle)) - ) + ): @unchecked search.totalCount should be(8) search.summaryResults.map(_.id) should be(Seq(1, 2, 3, 4, 5, 6, 7, 12)) @@ -560,13 +572,13 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That filtering on article-type works") { val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = "*", articleTypes = List(ArticleType.Standard.entryName)) - ) + ): @unchecked val Success(search2) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = "*", articleTypes = List(ArticleType.TopicArticle.entryName)) - ) + ): @unchecked val Success(search3) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = "*", articleTypes = List(ArticleType.FrontpageArticle.entryName)) - ) + ): @unchecked search.totalCount should be(8) search.summaryResults.map(_.id) should be(Seq(1, 2, 3, 4, 5, 6, 7, 12)) @@ -581,7 +593,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That filtering on learningpath learningresourcetype returns learningpaths") { val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = "*", learningResourceTypes = List(LearningResourceType.LearningPath)) - ) + ): @unchecked search.totalCount should be(6) search.summaryResults.map(_.id) should be(Seq(1, 2, 3, 4, 5, 6)) @@ -592,21 +604,21 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = "*", supportedLanguages = List("en")) - ) + ): @unchecked search.totalCount should be(9) search.summaryResults.map(_.id) should be(Seq(2, 3, 4, 5, 6, 10, 11, 13, 15)) val Success(search2) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = "*", supportedLanguages = List("en", "nb"), pageSize = 100) - ) + ): @unchecked search2.totalCount should be(21) search2.summaryResults.map(_.id) should be(Seq(1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16)) val Success(search3) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = "*", supportedLanguages = List("nb")) - ) + ): @unchecked search3.totalCount should be(18) search3.summaryResults.map(_.id) should be(Seq(1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16)) } @@ -615,7 +627,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = "nb", supportedLanguages = List("en")) - ) + ): @unchecked search.totalCount should be(6) search.summaryResults.map(_.id) should be(Seq(2, 3, 4, 11, 13, 15)) @@ -624,7 +636,9 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That meta image are returned when searching") { val Success(search) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(language = "en", withIdIn = List(10))) + multiDraftSearchService.matchingQuery( + multiDraftSearchSettings.copy(language = "en", withIdIn = List(10)) + ): @unchecked search.totalCount should be(1) search.summaryResults.head.id should be(10) @@ -637,7 +651,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(noteQuery = Some(NonEmptyString.fromString("kakemonster").get)) - ) + ): @unchecked search.totalCount should be(1) search.summaryResults.head.id should be(5) @@ -645,7 +659,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search2) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(noteQuery = Some(NonEmptyString.fromString("Katter").get)) - ) + ): @unchecked search2.totalCount should be(0) } @@ -654,7 +668,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(query = Some(NonEmptyString.fromString("kakemonster").get)) - ) + ): @unchecked search.totalCount should be(1) search.summaryResults.head.id should be(5) @@ -662,7 +676,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That filtering for topics returns every child learningResource") { val Success(search) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(topics = List("urn:topic:1"))) + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(topics = List("urn:topic:1"))): @unchecked search.totalCount should be(8) @@ -675,13 +689,13 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes query = Some(NonEmptyString.fromString("Kjekspolitiet").get), language = AllLanguages ) - ) + ): @unchecked search1.totalCount should be(1) search1.summaryResults.map(_.id) should be(Seq(1)) val Success(search2) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(query = Some(NonEmptyString.fromString("Svims").get), language = AllLanguages) - ) + ): @unchecked search2.totalCount should be(2) search2.summaryResults.map(_.id) should be(Seq(2, 5)) } @@ -689,12 +703,12 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That filtering by relevance id works when no subject is specified") { val Success(search1) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = AllLanguages, relevanceIds = List("urn:relevance:core")) - ) + ): @unchecked search1.summaryResults.map(_.id) should be(Seq(1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12)) val Success(search2) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = AllLanguages, relevanceIds = List("urn:relevance:supplementary")) - ) + ): @unchecked search2.summaryResults.map(_.id) should be(Seq(1, 2, 3, 4, 4, 5, 12, 15)) val Success(search3) = multiDraftSearchService.matchingQuery( @@ -702,7 +716,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes language = AllLanguages, relevanceIds = List("urn:relevance:supplementary", "urn:relevance:core") ) - ) + ): @unchecked search3.summaryResults.map(_.id) should be(Seq(1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 15)) } @@ -710,7 +724,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search1) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings .copy(language = AllLanguages, subjects = List("urn:subject:2"), relevanceIds = List("urn:relevance:core")) - ) + ): @unchecked search1.summaryResults.map(_.id) should be(Seq(1, 5, 6, 7, 11)) } @@ -721,20 +735,20 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(initialSearch) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = AllLanguages, pageSize = pageSize, shouldScroll = true) - ) - - val Success(scroll1) = multiDraftSearchService.scroll(initialSearch.scrollId.get, "*") - val Success(scroll2) = multiDraftSearchService.scroll(scroll1.scrollId.get, "*") - val Success(scroll3) = multiDraftSearchService.scroll(scroll2.scrollId.get, "*") - val Success(scroll4) = multiDraftSearchService.scroll(scroll3.scrollId.get, "*") - val Success(scroll5) = multiDraftSearchService.scroll(scroll4.scrollId.get, "*") - val Success(scroll6) = multiDraftSearchService.scroll(scroll5.scrollId.get, "*") - val Success(scroll7) = multiDraftSearchService.scroll(scroll6.scrollId.get, "*") - val Success(scroll8) = multiDraftSearchService.scroll(scroll7.scrollId.get, "*") - val Success(scroll9) = multiDraftSearchService.scroll(scroll8.scrollId.get, "*") - val Success(scroll10) = multiDraftSearchService.scroll(scroll9.scrollId.get, "*") - val Success(scroll11) = multiDraftSearchService.scroll(scroll10.scrollId.get, "*") - val Success(scroll12) = multiDraftSearchService.scroll(scroll11.scrollId.get, "*") + ): @unchecked + + val Success(scroll1) = multiDraftSearchService.scroll(initialSearch.scrollId.get, "*"): @unchecked + val Success(scroll2) = multiDraftSearchService.scroll(scroll1.scrollId.get, "*"): @unchecked + val Success(scroll3) = multiDraftSearchService.scroll(scroll2.scrollId.get, "*"): @unchecked + val Success(scroll4) = multiDraftSearchService.scroll(scroll3.scrollId.get, "*"): @unchecked + val Success(scroll5) = multiDraftSearchService.scroll(scroll4.scrollId.get, "*"): @unchecked + val Success(scroll6) = multiDraftSearchService.scroll(scroll5.scrollId.get, "*"): @unchecked + val Success(scroll7) = multiDraftSearchService.scroll(scroll6.scrollId.get, "*"): @unchecked + val Success(scroll8) = multiDraftSearchService.scroll(scroll7.scrollId.get, "*"): @unchecked + val Success(scroll9) = multiDraftSearchService.scroll(scroll8.scrollId.get, "*"): @unchecked + val Success(scroll10) = multiDraftSearchService.scroll(scroll9.scrollId.get, "*"): @unchecked + val Success(scroll11) = multiDraftSearchService.scroll(scroll10.scrollId.get, "*"): @unchecked + val Success(scroll12) = multiDraftSearchService.scroll(scroll11.scrollId.get, "*"): @unchecked initialSearch.summaryResults.map(_.id) should be(ids.head) scroll1.summaryResults.map(_.id) should be(ids(1)) @@ -758,7 +772,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes learningResourceTypes = List(LearningResourceType.Article, LearningResourceType.TopicArticle), statusFilter = List(DraftStatus.IN_PROGRESS) ) - ) + ): @unchecked search1.summaryResults.map(_.id) should be(Seq(10, 11)) val Success(search2) = multiDraftSearchService.matchingQuery( @@ -767,7 +781,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes learningResourceTypes = List(LearningResourceType.Article, LearningResourceType.TopicArticle), statusFilter = List(DraftStatus.IMPORTED) ) - ) + ): @unchecked search2.summaryResults.map(_.id) should be(Seq()) val Success(search3) = multiDraftSearchService.matchingQuery( @@ -777,7 +791,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes statusFilter = List(DraftStatus.IMPORTED), includeOtherStatuses = true ) - ) + ): @unchecked search3.summaryResults.map(_.id) should be(Seq(12)) } @@ -787,7 +801,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search1) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(language = AllLanguages, statusFilter = List(DraftStatus.IN_PROGRESS)) - ) + ): @unchecked search1.summaryResults.map(_.id) should be(expectedIds) } @@ -796,11 +810,11 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search1) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(query = Some(NonEmptyString.fromString("kultgammeltnotat").get)) - ) + ): @unchecked val Success(search2) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(noteQuery = Some(NonEmptyString.fromString("kultgammeltnotat").get)) - ) + ): @unchecked search1.totalCount should be(1) search1.summaryResults.head.id should be(5) @@ -811,11 +825,11 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That filtering on grepCodes returns articles which has grepCodes") { val Success(search1) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(grepCodes = List("K123"))) + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(grepCodes = List("K123"))): @unchecked val Success(search2) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(grepCodes = List("K456"))) + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(grepCodes = List("K456"))): @unchecked val Success(search3) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(grepCodes = List("K123", "K456"))) + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(grepCodes = List("K123", "K456"))): @unchecked search1.summaryResults.map(_.id) should be(Seq(1, 2, 3)) search2.summaryResults.map(_.id) should be(Seq(1, 2, 5)) @@ -826,18 +840,18 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search1) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(statusFilter = List(DraftStatus.ARCHIVED)) - ) + ): @unchecked val Success(search2) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(statusFilter = List(DraftStatus.UNPUBLISHED)) - ) + ): @unchecked val Success(search3) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy( withIdIn = List(14, 17), // 14 is archived, 17 is unpublished statusFilter = List.empty ) - ) + ): @unchecked search1.summaryResults.map(_.id) should be(Seq(14)) search2.summaryResults.map(_.id) should be(Seq(17)) @@ -848,7 +862,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings .copy(query = Some(NonEmptyString.fromString("bil").get), language = AllLanguages, sort = Sort.ByRelevanceDesc) - ) + ): @unchecked search.totalCount should equal(3) search.suggestions.length should equal(2) @@ -866,7 +880,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes language = AllLanguages, searchDecompounded = true ) - ) + ): @unchecked search1.totalCount should be(1) search1.summaryResults.map(_.id) should be(Seq(13)) @@ -877,7 +891,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes language = "nb", searchDecompounded = true ) - ) + ): @unchecked search2.totalCount should be(1) search2.summaryResults.map(_.id) should be(Seq(13)) @@ -891,7 +905,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes language = AllLanguages, searchDecompounded = false ) - ) + ): @unchecked search1.totalCount should be(0) search1.summaryResults.map(_.id) should be(Seq.empty) @@ -902,7 +916,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes language = "nb", searchDecompounded = false ) - ) + ): @unchecked search2.totalCount should be(0) search2.summaryResults.map(_.id) should be(Seq.empty) @@ -911,7 +925,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("Search query should not be decompounded (only indexed documents)") { val Success(search1) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(query = Some(NonEmptyString.fromString("Bilsøster").get), language = AllLanguages) - ) + ): @unchecked search1.totalCount should be(0) } @@ -919,7 +933,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That searches for embed attributes matches") { val Success(search) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(query = Some(NonEmptyString.fromString("Flubber").get), language = AllLanguages) - ) + ): @unchecked search.summaryResults.map(_.id) should be(Seq(12)) } @@ -927,13 +941,13 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(results) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(embedResource = List("conc"), embedId = Some("55")) - ) + ): @unchecked results.totalCount should be(0) } test("That searches for data-resource_id matches") { val Success(results) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(embedId = Some("222"))) + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(embedId = Some("222"))): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -947,7 +961,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes embedResource = List("image"), embedId = Some("55") ) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -962,13 +976,13 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes embedResource = List("concept"), embedId = Some("77") ) - ) + ): @unchecked results.totalCount should be(0) } test("That search on embed data-resource matches") { val Success(results) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(embedResource = List("video"))) + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(embedResource = List("video"))): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -976,7 +990,9 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That search on embed data-url matches") { val Success(results) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(embedId = Some("http://test.test"))) + multiDraftSearchService.matchingQuery( + multiDraftSearchSettings.copy(embedId = Some("http://test.test")) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -986,7 +1002,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(results) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(query = Some(NonEmptyString.fromString("77").get)) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -996,7 +1012,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(results) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(query = Some(NonEmptyString.fromString("video").get)) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -1006,7 +1022,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(results) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(query = Some(NonEmptyString.fromString("11").get)) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(11) @@ -1014,7 +1030,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That search on embed data-content-id matches") { val Success(results) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(embedId = Some("111"))) + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(embedId = Some("111"))): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -1022,7 +1038,9 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That search on embed id with language filter does only return correct language") { val Success(results) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(language = "en", embedId = Some("222"))) + multiDraftSearchService.matchingQuery( + multiDraftSearchSettings.copy(language = "en", embedId = Some("222")) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(13) @@ -1030,7 +1048,9 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That search on embed id with language filter=all matches") { val Success(results) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(language = "*", embedId = Some("222"))) + multiDraftSearchService.matchingQuery( + multiDraftSearchSettings.copy(language = "*", embedId = Some("222")) + ): @unchecked val hits = results.summaryResults results.totalCount should be(2) hits.map(_.id) should be(Seq(12, 13)) @@ -1038,7 +1058,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That search on visual element id matches") { val Success(results) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(embedId = Some("333"))) + multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(embedId = Some("333"))): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(12)) @@ -1046,7 +1066,9 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes test("That search on meta image url matches ") { val Success(results) = - multiDraftSearchService.matchingQuery(multiDraftSearchSettings.copy(language = "*", embedId = Some("123"))) + multiDraftSearchService.matchingQuery( + multiDraftSearchSettings.copy(language = "*", embedId = Some("123")) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(10)) @@ -1056,7 +1078,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(results) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(query = Some(NonEmptyString.fromString("\"delt-streng\"").get), language = "*") - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(15)) @@ -1066,7 +1088,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(results) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(query = Some(NonEmptyString.fromString("\"delt\\-streng\"").get), language = "*") - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(15)) @@ -1079,7 +1101,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes query = Some(NonEmptyString.fromString("\"delt!streng\" \"delt?streng\"").get), language = "*" ) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(13)) @@ -1092,7 +1114,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes query = Some(NonEmptyString.fromString("\"delt!streng\"+\"delt-streng\"").get), language = "*" ) - ) + ): @unchecked results.totalCount should be(0) } @@ -1103,7 +1125,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes query = Some(NonEmptyString.fromString("\"delt!streng\"+-\"delt-streng\"").get), language = "*" ) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(13)) @@ -1116,7 +1138,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes query = Some(NonEmptyString.fromString("\"delt!streng\"+-Helsesøster").get), language = "*" ) - ) + ): @unchecked results.totalCount should be(0) } @@ -1127,7 +1149,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes query = Some(NonEmptyString.fromString("\"delt!streng\" + Helsesøster").get), language = "*" ) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(13)) @@ -1140,7 +1162,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes query = Some(NonEmptyString.fromString("\"artikkeltekst med fire deler\"").get), language = "*" ) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(12)) @@ -1151,7 +1173,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes multiDraftSearchService.matchingQuery( multiDraftSearchSettings .copy(language = AllLanguages, embedResource = List("concept"), embedId = Some("222")) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -1161,11 +1183,11 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes val Success(search1) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(embedId = Some("test-image.id")) - ) + ): @unchecked val Success(search2) = multiDraftSearchService.matchingQuery( multiDraftSearchSettings.copy(embedId = Some("test-image.url")) - ) + ): @unchecked search1.totalCount should be(1) search1.summaryResults.head.id should be(12) @@ -1182,7 +1204,7 @@ class MultiDraftSearchServiceTest extends ElasticsearchIntegrationSuite with Tes sort = Sort.ByRelevanceDesc, withIdIn = List(3) ) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.lastUpdated should be(a[NDLADate]) diff --git a/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiSearchServiceAtomicTest.scala b/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiSearchServiceAtomicTest.scala index d04f0e5c9c..598f9dfd34 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiSearchServiceAtomicTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiSearchServiceAtomicTest.scala @@ -30,23 +30,23 @@ import scala.util.Success class MultiSearchServiceAtomicTest extends ElasticsearchIntegrationSuite with TestEnvironment { e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("")) - override val articleIndexService: ArticleIndexService = new ArticleIndexService { + override lazy val articleIndexService: ArticleIndexService = new ArticleIndexService { override val indexShards = 1 } - override val draftIndexService: DraftIndexService = new DraftIndexService { + override lazy val draftIndexService: DraftIndexService = new DraftIndexService { override val indexShards = 1 } - override val learningPathIndexService: LearningPathIndexService = new LearningPathIndexService { + override lazy val learningPathIndexService: LearningPathIndexService = new LearningPathIndexService { override val indexShards = 1 } - override val nodeIndexService: NodeIndexService = new NodeIndexService { + override lazy val nodeIndexService: NodeIndexService = new NodeIndexService { override val indexShards = 1 } - override val multiSearchService = new MultiSearchService { + override lazy val multiSearchService = new MultiSearchService { override val enableExplanations = true } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService override def beforeEach(): Unit = { if (elasticSearchContainer.isSuccess) { @@ -98,7 +98,7 @@ class MultiSearchServiceAtomicTest extends ElasticsearchIntegrationSuite with Te val Success(search1) = multiSearchService.matchingQuery( TestData.searchSettings.copy(embedId = Some("3"), embedResource = List("content-link")) - ) + ): @unchecked search1.totalCount should be(1) search1.summaryResults.map(_.id) should be(List(2)) @@ -106,7 +106,7 @@ class MultiSearchServiceAtomicTest extends ElasticsearchIntegrationSuite with Te val Success(search2) = multiSearchService.matchingQuery( TestData.searchSettings.copy(embedId = Some("3"), embedResource = List("content-link", "related-content")) - ) + ): @unchecked search2.totalCount should be(2) search2.summaryResults.map(_.id) should be(List(1, 2)) diff --git a/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiSearchServiceTest.scala b/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiSearchServiceTest.scala index d0cf0554ad..2df5d2a91c 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiSearchServiceTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/service/search/MultiSearchServiceTest.scala @@ -27,28 +27,26 @@ import no.ndla.searchapi.SearchTestUtility.* import scala.util.Success class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuite with TestEnvironment { - import props.DefaultPageSize - e4sClient = Elastic4sClientFactory.getClient(elasticSearchHost.getOrElse("")) - override val articleIndexService: ArticleIndexService = new ArticleIndexService { + override lazy val articleIndexService: ArticleIndexService = new ArticleIndexService { override val indexShards = 1 } - override val draftIndexService: DraftIndexService = new DraftIndexService { + override lazy val draftIndexService: DraftIndexService = new DraftIndexService { override val indexShards = 1 } - override val learningPathIndexService: LearningPathIndexService = new LearningPathIndexService { + override lazy val learningPathIndexService: LearningPathIndexService = new LearningPathIndexService { override val indexShards = 1 } - override val nodeIndexService: NodeIndexService = new NodeIndexService { + override lazy val nodeIndexService: NodeIndexService = new NodeIndexService { override val indexShards = 1 } - override val multiSearchService = new MultiSearchService { + override lazy val multiSearchService = new MultiSearchService { override val enableExplanations = true } - override val converterService = new ConverterService - override val searchConverterService = new SearchConverterService + override lazy val converterService = new ConverterService + override lazy val searchConverterService = new SearchConverterService override def beforeAll(): Unit = { super.beforeAll() @@ -127,14 +125,14 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That getStartAtAndNumResults returns the correct calculated start at for page and page-size") { val page = 74 - val expectedStartAt = (page - 1) * DefaultPageSize - multiSearchService.getStartAtAndNumResults(page, DefaultPageSize) should equal( - Success(SearchPagination(page, DefaultPageSize, expectedStartAt)) + val expectedStartAt = (page - 1) * props.DefaultPageSize + multiSearchService.getStartAtAndNumResults(page, props.DefaultPageSize) should equal( + Success(SearchPagination(page, props.DefaultPageSize, expectedStartAt)) ) } test("That all returns all documents ordered by id ascending") { - val Success(results) = multiSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByIdAsc)) + val Success(results) = multiSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByIdAsc)): @unchecked val hits = results.summaryResults results.totalCount should be(idsForLang("nb").size) @@ -142,7 +140,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit } test("That all returns all documents ordered by id descending") { - val Success(results) = multiSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByIdDesc)) + val Success(results) = multiSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByIdDesc)): @unchecked val hits = results.summaryResults results.totalCount should be(idsForLang("nb").size) @@ -150,30 +148,32 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit } test("That all returns all documents ordered by title ascending") { - val Success(results) = multiSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByTitleAsc)) + val Success(results) = multiSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByTitleAsc)): @unchecked val hits = results.summaryResults results.totalCount should be(titlesForLang("nb").size) hits.map(_.title.title) should be(titlesForLang("nb").sorted) } test("That all returns all documents ordered by title descending") { - val Success(results) = multiSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByTitleDesc)) + val Success(results) = multiSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByTitleDesc)): @unchecked val hits = results.summaryResults results.totalCount should be(titlesForLang("nb").size) hits.map(_.title.title) should be(titlesForLang("nb").sorted.reverse) } test("That all returns all documents ordered by lastUpdated descending") { - val Success(results) = multiSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByLastUpdatedDesc)) - val hits = results.summaryResults + val Success(results) = + multiSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByLastUpdatedDesc)): @unchecked + val hits = results.summaryResults results.totalCount should be(idsForLang("nb").size) hits.head.id should be(3) hits.last.id should be(5) } test("That all returns all documents ordered by lastUpdated ascending") { - val Success(results) = multiSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByLastUpdatedAsc)) - val hits = results.summaryResults + val Success(results) = + multiSearchService.matchingQuery(searchSettings.copy(sort = Sort.ByLastUpdatedAsc)): @unchecked + val hits = results.summaryResults results.totalCount should be(idsForLang("nb").size) hits.head.id should be(5) hits(1).id should be(1) @@ -182,9 +182,9 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That paging returns only hits on current page and not more than page-size") { val Success(page1) = - multiSearchService.matchingQuery(searchSettings.copy(page = 1, pageSize = 2, sort = Sort.ByTitleAsc)) + multiSearchService.matchingQuery(searchSettings.copy(page = 1, pageSize = 2, sort = Sort.ByTitleAsc)): @unchecked val Success(page2) = - multiSearchService.matchingQuery(searchSettings.copy(page = 2, pageSize = 2, sort = Sort.ByTitleAsc)) + multiSearchService.matchingQuery(searchSettings.copy(page = 2, pageSize = 2, sort = Sort.ByTitleAsc)): @unchecked val hits1 = page1.summaryResults val hits2 = page2.summaryResults page1.totalCount should be(idsForLang("nb").size) @@ -203,7 +203,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(results) = multiSearchService.matchingQuery( searchSettings.copy(query = Some(NonEmptyString.fromString("bil").get), sort = Sort.ByRelevanceDesc) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(3) hits.map(_.id) should be(Seq(1, 5, 3)) @@ -213,7 +213,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(results) = multiSearchService.matchingQuery( searchSettings.copy(Some(NonEmptyString.fromString("bil").get), sort = Sort.ByRelevanceDesc, withIdIn = List(3)) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(3) @@ -224,7 +224,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(results) = multiSearchService.matchingQuery( searchSettings.copy(Some(NonEmptyString.fromString("Pingvinen").get), sort = Sort.ByTitleAsc) - ) + ): @unchecked val hits = results.summaryResults hits.map(_.contexts.head.contextType) should be(Seq("learningpath", "standard")) hits.map(_.id) should be(Seq(1, 2)) @@ -233,7 +233,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That search matches tags") { val Success(results) = multiSearchService.matchingQuery( searchSettings.copy(Some(NonEmptyString.fromString("and").get), sort = Sort.ByTitleAsc) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(2) hits.head.id should be(3) @@ -245,7 +245,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(results) = multiSearchService.matchingQuery( searchSettings.copy(Some(NonEmptyString.fromString("supermann").get), sort = Sort.ByTitleAsc) - ) + ): @unchecked results.totalCount should be(0) } @@ -257,7 +257,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit license = Some(License.Copyrighted.toString), sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(4) @@ -267,14 +267,14 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(search1) = multiSearchService.matchingQuery( searchSettings.copy(Some(NonEmptyString.fromString("bilde + bil").get), sort = Sort.ByTitleAsc) - ) + ): @unchecked val hits1 = search1.summaryResults hits1.map(_.id) should equal(Seq(1, 3, 5)) val Success(search2) = multiSearchService.matchingQuery( searchSettings.copy(Some(NonEmptyString.fromString("batmen + bil").get), sort = Sort.ByTitleAsc) - ) + ): @unchecked val hits2 = search2.summaryResults hits2.map(_.id) should equal(Seq(1)) @@ -283,13 +283,13 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("Searching with NOT returns expected results") { val Success(search1) = multiSearchService.matchingQuery( searchSettings.copy(Some(NonEmptyString.fromString("bil + bilde + -flaggermusmann").get), sort = Sort.ByTitleAsc) - ) + ): @unchecked search1.summaryResults.map(_.id) should equal(Seq(3, 5)) val Success(search2) = multiSearchService.matchingQuery( searchSettings.copy(Some(NonEmptyString.fromString("bil + -hulken").get), sort = Sort.ByTitleAsc) - ) + ): @unchecked search2.summaryResults.map(_.id) should equal(Seq(1, 3)) } @@ -297,7 +297,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(search) = multiSearchService.matchingQuery( searchSettings.copy(Some(NonEmptyString.fromString("mareritt+ragnarok").get), sort = Sort.ByRelevanceDesc) - ) + ): @unchecked val hits = search.summaryResults hits.map(_.id) should equal(Seq(9, 8)) } @@ -305,14 +305,14 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("Search for all languages should return all articles in different languages") { val Success(search) = multiSearchService.matchingQuery( searchSettings.copy(language = AllLanguages, pageSize = 100, sort = Sort.ByTitleAsc) - ) + ): @unchecked search.totalCount should equal(titlesForLang("*").size) } test("Search for all languages should return all articles in correct language") { val Success(search) = - multiSearchService.matchingQuery(searchSettings.copy(language = AllLanguages, pageSize = 100)) + multiSearchService.matchingQuery(searchSettings.copy(language = AllLanguages, pageSize = 100)): @unchecked val hits = search.summaryResults val exp = titlesForLang("*") @@ -349,7 +349,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit pageSize = 100, sort = Sort.ByTitleAsc ) - ) + ): @unchecked val hits = search.summaryResults search.totalCount should equal(1) @@ -363,14 +363,14 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit language = AllLanguages, sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked val Success(searchNb) = multiSearchService.matchingQuery( searchSettings.copy( Some(NonEmptyString.fromString("Katter").get), language = AllLanguages, sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked searchEn.totalCount should equal(1) searchEn.summaryResults.head.id should equal(11) @@ -386,7 +386,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("Searching for unknown language should return nothing") { val Success(searchEn) = - multiSearchService.matchingQuery(searchSettings.copy(language = "mix", sort = Sort.ByRelevanceDesc)) + multiSearchService.matchingQuery(searchSettings.copy(language = "mix", sort = Sort.ByRelevanceDesc)): @unchecked searchEn.totalCount should equal(0) } @@ -398,7 +398,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit language = AllLanguages, sort = Sort.ByRelevanceDesc ) - ) + ): @unchecked search.totalCount should equal(1) search.summaryResults.head.id should equal(11) @@ -410,7 +410,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(search) = multiSearchService.matchingQuery( searchSettings.copy(fallback = true, language = "en", withIdIn = List(9, 10, 11)) - ) + ): @unchecked search.totalCount should equal(3) search.summaryResults.head.id should equal(9) @@ -423,7 +423,9 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That filtering for subjects works as expected") { val Success(search) = - multiSearchService.matchingQuery(searchSettings.copy(language = "*", subjects = List("urn:subject:2"))) + multiSearchService.matchingQuery( + searchSettings.copy(language = "*", subjects = List("urn:subject:2")) + ): @unchecked search.totalCount should be(7) search.summaryResults.head.contexts.length should be(2) search.summaryResults.head.contexts @@ -433,30 +435,38 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That filtering for subjects returns all resources with any of listed subjects") { val Success(search) = - multiSearchService.matchingQuery(searchSettings.copy(subjects = List("urn:subject:2", "urn:subject:1"))) + multiSearchService.matchingQuery( + searchSettings.copy(subjects = List("urn:subject:2", "urn:subject:1")) + ): @unchecked search.totalCount should be(14) search.summaryResults.map(_.id) should be(Seq(1, 1, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 11, 12)) } test("That filtering for invisible subjects returns nothing") { val Success(search) = - multiSearchService.matchingQuery(searchSettings.copy(subjects = List("urn:subject:3"))) + multiSearchService.matchingQuery(searchSettings.copy(subjects = List("urn:subject:3"))): @unchecked search.totalCount should be(0) } test("That filtering for resource-types works as expected") { val Success(search) = - multiSearchService.matchingQuery(searchSettings.copy(resourceTypes = List("urn:resourcetype:academicArticle"))) + multiSearchService.matchingQuery( + searchSettings.copy(resourceTypes = List("urn:resourcetype:academicArticle")) + ): @unchecked search.totalCount should be(2) search.summaryResults.map(_.id) should be(Seq(2, 5)) val Success(search2) = - multiSearchService.matchingQuery(searchSettings.copy(resourceTypes = List("urn:resourcetype:subjectMaterial"))) + multiSearchService.matchingQuery( + searchSettings.copy(resourceTypes = List("urn:resourcetype:subjectMaterial")) + ): @unchecked search2.totalCount should be(7) search2.summaryResults.map(_.id) should be(Seq(1, 2, 3, 5, 6, 7, 12)) val Success(search3) = - multiSearchService.matchingQuery(searchSettings.copy(resourceTypes = List("urn:resourcetype:learningpath"))) + multiSearchService.matchingQuery( + searchSettings.copy(resourceTypes = List("urn:resourcetype:learningpath")) + ): @unchecked search3.totalCount should be(4) search3.summaryResults.map(_.id) should be(Seq(1, 2, 3, 4)) } @@ -464,7 +474,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That filtering for multiple resource-types returns resources from both") { val Success(search) = multiSearchService.matchingQuery( searchSettings.copy(resourceTypes = List("urn:resourcetype:subjectMaterial", "urn:resourcetype:reviewResource")) - ) + ): @unchecked search.totalCount should be(7) search.summaryResults.map(_.id) should be(Seq(1, 2, 3, 5, 6, 7, 12)) } @@ -472,10 +482,10 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That filtering on learning-resource-type works") { val Success(search) = multiSearchService.matchingQuery( searchSettings.copy(language = "*", learningResourceTypes = List(LearningResourceType.Article)) - ) + ): @unchecked val Success(search2) = multiSearchService.matchingQuery( searchSettings.copy(language = "*", learningResourceTypes = List(LearningResourceType.TopicArticle)) - ) + ): @unchecked search.totalCount should be(7) search.summaryResults.map(_.id) should be(Seq(1, 2, 3, 5, 6, 7, 12)) @@ -487,13 +497,13 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That filtering on article-type works") { val Success(search) = multiSearchService.matchingQuery( searchSettings.copy(language = "*", articleTypes = List(ArticleType.Standard.entryName)) - ) + ): @unchecked val Success(search2) = multiSearchService.matchingQuery( searchSettings.copy(language = "*", articleTypes = List(ArticleType.TopicArticle.entryName)) - ) + ): @unchecked val Success(search3) = multiSearchService.matchingQuery( searchSettings.copy(language = "*", articleTypes = List(ArticleType.FrontpageArticle.entryName)) - ) + ): @unchecked search.totalCount should be(7) search.summaryResults.map(_.id) should be(Seq(1, 2, 3, 5, 6, 7, 12)) @@ -512,7 +522,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit language = "*", learningResourceTypes = List(LearningResourceType.Article, LearningResourceType.TopicArticle) ) - ) + ): @unchecked search.totalCount should be(11) search.summaryResults.map(_.id) should be(Seq(1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12)) @@ -521,7 +531,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That filtering on learningpath learningresourcetype returns learningpaths") { val Success(search) = multiSearchService.matchingQuery( searchSettings.copy(language = "*", learningResourceTypes = List(LearningResourceType.LearningPath)) - ) + ): @unchecked search.totalCount should be(6) search.summaryResults.map(_.id) should be(Seq(1, 2, 3, 4, 5, 6)) @@ -537,7 +547,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit language = "*", filterInactive = false ) - ) + ): @unchecked val totalCount = search.totalCount val ids = search.summaryResults.map(_.id).length @@ -548,7 +558,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit language = "*", filterInactive = true ) - ) + ): @unchecked totalCount should be > search2.totalCount ids should be > search2.summaryResults.map(_.id).length @@ -557,24 +567,28 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That filtering on supportedLanguages works") { val Success(search) = - multiSearchService.matchingQuery(searchSettings.copy(language = "*", supportedLanguages = List("en"))) + multiSearchService.matchingQuery(searchSettings.copy(language = "*", supportedLanguages = List("en"))): @unchecked search.totalCount should be(8) search.summaryResults.map(_.id) should be(Seq(2, 3, 4, 5, 6, 10, 11, 12)) val Success(search2) = - multiSearchService.matchingQuery(searchSettings.copy(language = "*", supportedLanguages = List("en", "nb"))) + multiSearchService.matchingQuery( + searchSettings.copy(language = "*", supportedLanguages = List("en", "nb")) + ): @unchecked search2.totalCount should be(18) search2.summaryResults.map(_.id) should be(Seq(1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 14)) val Success(search3) = - multiSearchService.matchingQuery(searchSettings.copy(language = "*", supportedLanguages = List("nb"))) + multiSearchService.matchingQuery(searchSettings.copy(language = "*", supportedLanguages = List("nb"))): @unchecked search3.totalCount should be(15) search3.summaryResults.map(_.id) should be(Seq(1, 1, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 11, 12, 14)) } test("That filtering on supportedLanguages should still prioritize the selected language") { val Success(search) = - multiSearchService.matchingQuery(searchSettings.copy(language = "nb", supportedLanguages = List("en"))) + multiSearchService.matchingQuery( + searchSettings.copy(language = "nb", supportedLanguages = List("en")) + ): @unchecked search.totalCount should be(5) search.summaryResults.map(_.id) should be(Seq(2, 3, 4, 11, 12)) @@ -582,7 +596,8 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit } test("That meta image are returned when searching") { - val Success(search) = multiSearchService.matchingQuery(searchSettings.copy(language = "en", withIdIn = List(10))) + val Success(search) = + multiSearchService.matchingQuery(searchSettings.copy(language = "en", withIdIn = List(10))): @unchecked search.totalCount should be(1) search.summaryResults.head.id should be(10) @@ -595,14 +610,14 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(search1) = multiSearchService.matchingQuery( searchSettings.copy(Some(NonEmptyString.fromString("Kjekspolitiet").get), language = AllLanguages) - ) + ): @unchecked search1.totalCount should be(1) search1.summaryResults.map(_.id) should be(Seq(1)) val Success(search2) = multiSearchService.matchingQuery( searchSettings.copy(Some(NonEmptyString.fromString("Svims").get), language = AllLanguages) - ) + ): @unchecked search2.totalCount should be(2) search2.summaryResults.map(_.id) should be(Seq(2, 5)) } @@ -610,12 +625,12 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That filtering by relevance id makes sense (with and without subject/filter)") { val Success(search1) = multiSearchService.matchingQuery( searchSettings.copy(language = AllLanguages, relevanceIds = List("urn:relevance:core")) - ) + ): @unchecked search1.summaryResults.map(_.id) should be(Seq(1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12)) val Success(search2) = multiSearchService.matchingQuery( searchSettings.copy(language = AllLanguages, relevanceIds = List("urn:relevance:supplementary")) - ) + ): @unchecked search2.summaryResults.map(_.id) should be(Seq(1, 2, 3, 4, 5, 12)) val Success(search3) = multiSearchService.matchingQuery( @@ -623,7 +638,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit language = AllLanguages, relevanceIds = List("urn:relevance:supplementary", "urn:relevance:core") ) - ) + ): @unchecked search3.summaryResults.map(_.id) should be(Seq(1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12)) } @@ -631,7 +646,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(search1) = multiSearchService.matchingQuery( searchSettings .copy(language = AllLanguages, subjects = List("urn:subject:2"), relevanceIds = List("urn:relevance:core")) - ) + ): @unchecked search1.summaryResults.map(_.id) should be(Seq(1, 5, 6, 7, 11)) } @@ -643,17 +658,17 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(initialSearch) = multiSearchService.matchingQuery( searchSettings.copy(language = AllLanguages, pageSize = pageSize, shouldScroll = true) - ) - - val Success(scroll1) = multiSearchService.scroll(initialSearch.scrollId.get, "*") - val Success(scroll2) = multiSearchService.scroll(scroll1.scrollId.get, "*") - val Success(scroll3) = multiSearchService.scroll(scroll2.scrollId.get, "*") - val Success(scroll4) = multiSearchService.scroll(scroll3.scrollId.get, "*") - val Success(scroll5) = multiSearchService.scroll(scroll4.scrollId.get, "*") - val Success(scroll6) = multiSearchService.scroll(scroll5.scrollId.get, "*") - val Success(scroll7) = multiSearchService.scroll(scroll6.scrollId.get, "*") - val Success(scroll8) = multiSearchService.scroll(scroll7.scrollId.get, "*") - val Success(scroll9) = multiSearchService.scroll(scroll8.scrollId.get, "*") + ): @unchecked + + val Success(scroll1) = multiSearchService.scroll(initialSearch.scrollId.get, "*"): @unchecked + val Success(scroll2) = multiSearchService.scroll(scroll1.scrollId.get, "*"): @unchecked + val Success(scroll3) = multiSearchService.scroll(scroll2.scrollId.get, "*"): @unchecked + val Success(scroll4) = multiSearchService.scroll(scroll3.scrollId.get, "*"): @unchecked + val Success(scroll5) = multiSearchService.scroll(scroll4.scrollId.get, "*"): @unchecked + val Success(scroll6) = multiSearchService.scroll(scroll5.scrollId.get, "*"): @unchecked + val Success(scroll7) = multiSearchService.scroll(scroll6.scrollId.get, "*"): @unchecked + val Success(scroll8) = multiSearchService.scroll(scroll7.scrollId.get, "*"): @unchecked + val Success(scroll9) = multiSearchService.scroll(scroll8.scrollId.get, "*"): @unchecked initialSearch.summaryResults.map(_.id) should be(ids.head) scroll1.summaryResults.map(_.id) should be(ids(1)) @@ -669,9 +684,13 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That filtering on context-types works") { val Success(search) = - multiSearchService.matchingQuery(searchSettings.copy(resourceTypes = List("urn:resourcetype:academicArticle"))) + multiSearchService.matchingQuery( + searchSettings.copy(resourceTypes = List("urn:resourcetype:academicArticle")) + ): @unchecked val Success(search2) = - multiSearchService.matchingQuery(searchSettings.copy(resourceTypes = List("urn:resourcetype:movieAndClip"))) + multiSearchService.matchingQuery( + searchSettings.copy(resourceTypes = List("urn:resourcetype:movieAndClip")) + ): @unchecked search.totalCount should be(2) search.summaryResults.map(_.id) should be(Seq(2, 5)) @@ -680,10 +699,10 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit } test("That filtering on grepCodes returns articles which has grepCodes") { - val Success(search1) = multiSearchService.matchingQuery(searchSettings.copy(grepCodes = List("KM123"))) - val Success(search2) = multiSearchService.matchingQuery(searchSettings.copy(grepCodes = List("KE12"))) + val Success(search1) = multiSearchService.matchingQuery(searchSettings.copy(grepCodes = List("KM123"))): @unchecked + val Success(search2) = multiSearchService.matchingQuery(searchSettings.copy(grepCodes = List("KE12"))): @unchecked val Success(search3) = - multiSearchService.matchingQuery(searchSettings.copy(grepCodes = List("KM123", "KE34", "TT2"))) + multiSearchService.matchingQuery(searchSettings.copy(grepCodes = List("KM123", "KE34", "TT2"))): @unchecked search1.summaryResults.map(_.id) should be(Seq(1, 2, 3)) search2.summaryResults.map(_.id) should be(Seq(1, 5)) @@ -694,13 +713,15 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(search1) = multiSearchService.matchingQuery( searchSettings.copy(query = Some(NonEmptyString.fromString("\"utforsking og problemløysing\"").get)) - ) + ): @unchecked search1.summaryResults.map(_.id) should be(Seq(1, 5)) } test("That search result has traits if content has embeds") { val Success(search) = - multiSearchService.matchingQuery(searchSettings.copy(query = Some(NonEmptyString.fromString("Ekstrastoff").get))) + multiSearchService.matchingQuery( + searchSettings.copy(query = Some(NonEmptyString.fromString("Ekstrastoff").get)) + ): @unchecked search.totalCount should be(1) search.summaryResults.head.id should be(12) search.summaryResults.head.traits should be(List(SearchTrait.H5p)) @@ -708,7 +729,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That search can be filtered by traits") { val Success(search) = - multiSearchService.matchingQuery(searchSettings.copy(traits = List(SearchTrait.H5p))) + multiSearchService.matchingQuery(searchSettings.copy(traits = List(SearchTrait.H5p))): @unchecked search.totalCount should be(1) search.summaryResults.head.id should be(12) search.summaryResults.head.traits should be(List(SearchTrait.H5p)) @@ -726,7 +747,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(search1) = multiSearchService.matchingQuery( searchSettings.copy(query = Some(NonEmptyString.fromString("Helse søster").get), language = AllLanguages) - ) + ): @unchecked search1.summaryResults.map(_.id) should be(Seq(12)) search1.totalCount should be(1) @@ -734,7 +755,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(search2) = multiSearchService.matchingQuery( searchSettings.copy(query = Some(NonEmptyString.fromString("Helse søster").get), language = "nb") - ) + ): @unchecked search2.summaryResults.map(_.id) should be(Seq(12)) search2.totalCount should be(1) @@ -743,7 +764,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That filterByNoResourceType works by filtering out every document that does not have resourceTypes") { val Success(search1) = multiSearchService.matchingQuery( searchSettings.copy(language = AllLanguages, sort = Sort.ByIdAsc, filterByNoResourceType = true) - ) + ): @unchecked search1.summaryResults.map(_.id).sorted should be(Seq(6, 8, 9, 10, 11, 14)) } @@ -751,20 +772,22 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(search1) = multiSearchService.matchingQuery( searchSettings.copy(query = Some(NonEmptyString.fromString("Bilsøster").get), language = AllLanguages) - ) + ): @unchecked search1.totalCount should be(0) } test("That searches for embedResource does not partial match") { val Success(results) = - multiSearchService.matchingQuery(searchSettings.copy(embedResource = List("vid"), embedId = Some("55"))) + multiSearchService.matchingQuery( + searchSettings.copy(embedResource = List("vid"), embedId = Some("55")) + ): @unchecked results.totalCount should be(0) } test("That searches for data-resource_id matches") { val Success(results) = - multiSearchService.matchingQuery(searchSettings.copy(embedId = Some("66"))) + multiSearchService.matchingQuery(searchSettings.copy(embedId = Some("66"))): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -778,7 +801,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit embedResource = List("video"), embedId = Some("77") ) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -793,13 +816,13 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit embedResource = List("video"), embedId = Some("77") ) - ) + ): @unchecked results.totalCount should be(0) } test("That search on embed data-resource matches") { val Success(results) = - multiSearchService.matchingQuery(searchSettings.copy(embedResource = List("video"))) + multiSearchService.matchingQuery(searchSettings.copy(embedResource = List("video"))): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -807,7 +830,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That search on embed data-content-id matches") { val Success(results) = - multiSearchService.matchingQuery(searchSettings.copy(embedId = Some("111"))) + multiSearchService.matchingQuery(searchSettings.copy(embedId = Some("111"))): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -815,7 +838,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That search on embed data-url matches") { val Success(results) = - multiSearchService.matchingQuery(searchSettings.copy(embedId = Some("http://test"))) + multiSearchService.matchingQuery(searchSettings.copy(embedId = Some("http://test"))): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -823,7 +846,9 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That search on query as embed data-resource_id matches") { val Success(results) = - multiSearchService.matchingQuery(searchSettings.copy(query = Some(NonEmptyString.fromString("77").get))) + multiSearchService.matchingQuery( + searchSettings.copy(query = Some(NonEmptyString.fromString("77").get)) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -831,7 +856,9 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That search on query as embed data-resouce matches") { val Success(results) = - multiSearchService.matchingQuery(searchSettings.copy(query = Some(NonEmptyString.fromString("video").get))) + multiSearchService.matchingQuery( + searchSettings.copy(query = Some(NonEmptyString.fromString("video").get)) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -839,7 +866,9 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That search on query as article id matches") { val Success(results) = - multiSearchService.matchingQuery(searchSettings.copy(query = Some(NonEmptyString.fromString("11").get))) + multiSearchService.matchingQuery( + searchSettings.copy(query = Some(NonEmptyString.fromString("11").get)) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(11) @@ -847,7 +876,9 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That search on query as deleted context id matches") { val Success(results) = - multiSearchService.matchingQuery(searchSettings.copy(query = Some(NonEmptyString.fromString("asdf1255").get))) + multiSearchService.matchingQuery( + searchSettings.copy(query = Some(NonEmptyString.fromString("asdf1255").get)) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -855,7 +886,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That search on embed id with language filter does only return correct language") { val Success(results) = - multiSearchService.matchingQuery(searchSettings.copy(language = "en", embedId = Some("222"))) + multiSearchService.matchingQuery(searchSettings.copy(language = "en", embedId = Some("222"))): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(12) @@ -863,7 +894,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That search on embed id with language filter=all matches ") { val Success(results) = - multiSearchService.matchingQuery(searchSettings.copy(language = AllLanguages, embedId = Some("222"))) + multiSearchService.matchingQuery(searchSettings.copy(language = AllLanguages, embedId = Some("222"))): @unchecked val hits = results.summaryResults results.totalCount should be(2) hits.map(_.id) should be(Seq(11, 12)) @@ -871,7 +902,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That search on visual element id matches ") { val Success(results) = - multiSearchService.matchingQuery(searchSettings.copy(embedId = Some("333"))) + multiSearchService.matchingQuery(searchSettings.copy(embedId = Some("333"))): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(12)) @@ -879,7 +910,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit test("That search on meta image url matches ") { val Success(results) = - multiSearchService.matchingQuery(searchSettings.copy(language = "*", embedId = Some("442"))) + multiSearchService.matchingQuery(searchSettings.copy(language = "*", embedId = Some("442"))): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(10)) @@ -889,7 +920,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(results) = multiSearchService.matchingQuery( searchSettings.copy(query = Some(NonEmptyString.fromString("\"delt-streng\"").get), language = "*") - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(12)) @@ -899,7 +930,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(results) = multiSearchService.matchingQuery( searchSettings.copy(query = Some(NonEmptyString.fromString("\"delt\\-streng\"").get), language = "*") - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(12)) @@ -912,7 +943,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit query = Some(NonEmptyString.fromString("\"delt!streng\" \"delt?streng\"").get), language = "*" ) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(11)) @@ -925,7 +956,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit query = Some(NonEmptyString.fromString("\"delt!streng\"+\"delt-streng\"").get), language = "*" ) - ) + ): @unchecked results.totalCount should be(0) } @@ -936,7 +967,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit query = Some(NonEmptyString.fromString("\"delt!streng\"+-\"delt-streng\"").get), language = "*" ) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(11)) @@ -946,7 +977,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(results) = multiSearchService.matchingQuery( searchSettings.copy(query = Some(NonEmptyString.fromString("\"delt!streng\"+-katt").get), language = "*") - ) + ): @unchecked results.totalCount should be(0) } @@ -954,7 +985,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(results) = multiSearchService.matchingQuery( searchSettings.copy(query = Some(NonEmptyString.fromString("\"delt!streng\" + katt").get), language = "*") - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(11)) @@ -967,7 +998,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit query = Some(NonEmptyString.fromString("\"artikkeltekst med fire deler\"").get), language = "*" ) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.map(_.id) should be(Seq(10)) @@ -977,7 +1008,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(results) = multiSearchService.matchingQuery( searchSettings.copy(language = AllLanguages, embedResource = List("concept"), embedId = Some("222")) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.id should be(11) @@ -987,11 +1018,11 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(search1) = multiSearchService.matchingQuery( searchSettings.copy(embedId = Some("test-id1")) - ) + ): @unchecked val Success(search2) = multiSearchService.matchingQuery( searchSettings.copy(embedId = Some("http://test")) - ) + ): @unchecked search1.totalCount should be(1) search1.summaryResults.head.id should be(12) @@ -1004,7 +1035,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit val Success(search1) = multiSearchService.matchingQuery( searchSettings.copy(query = Some(NonEmptyString.fromString("utilgjengelig").get), availability = List.empty) - ) + ): @unchecked search1.totalCount should be(0) search1.summaryResults.map(_.id) should be(Seq.empty) @@ -1013,7 +1044,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit query = Some(NonEmptyString.fromString("utilgjengelig").get), availability = List(Availability.everyone) ) - ) + ): @unchecked search2.totalCount should be(0) search2.summaryResults.map(_.id) should be(Seq.empty) @@ -1022,7 +1053,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit query = Some(NonEmptyString.fromString("utilgjengelig").get), availability = List(Availability.everyone, Availability.teacher) ) - ) + ): @unchecked search3.totalCount should be(1) search3.summaryResults.map(_.id) should be(Seq(13)) } @@ -1035,7 +1066,7 @@ class MultiSearchServiceTest extends ElasticsearchIntegrationSuite with UnitSuit sort = Sort.ByRelevanceDesc, withIdIn = List(3) ) - ) + ): @unchecked val hits = results.summaryResults results.totalCount should be(1) hits.head.lastUpdated should be(a[NDLADate]) diff --git a/search-api/src/test/scala/no/ndla/searchapi/service/search/SearchConverterServiceTest.scala b/search-api/src/test/scala/no/ndla/searchapi/service/search/SearchConverterServiceTest.scala index d3c1014684..1087229568 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/service/search/SearchConverterServiceTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/service/search/SearchConverterServiceTest.scala @@ -36,8 +36,8 @@ import scala.util.{Success, Try} class SearchConverterServiceTest extends UnitSuite with TestEnvironment { - override val searchConverterService = new SearchConverterService - val sampleArticle: Article = TestData.sampleArticleWithPublicDomain.copy() + override lazy val searchConverterService = new SearchConverterService + val sampleArticle: Article = TestData.sampleArticleWithPublicDomain.copy() val titles: List[Title] = List( Title("Bokmål tittel", "nb"), @@ -129,7 +129,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { searchConverterService.asSearchableArticle( article, IndexingBundle(Some(TestData.emptyGrepBundle), Some(emptyBundle), Some(TestData.myndlaTestBundle)) - ) + ): @unchecked verifyTitles(searchableArticle) } @@ -139,7 +139,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { searchConverterService.asSearchableArticle( article, IndexingBundle(Some(TestData.emptyGrepBundle), Some(emptyBundle), Some(TestData.myndlaTestBundle)) - ) + ): @unchecked verifyArticles(searchableArticle) } @@ -149,7 +149,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { searchConverterService.asSearchableArticle( article, IndexingBundle(Some(TestData.emptyGrepBundle), Some(emptyBundle), Some(TestData.myndlaTestBundle)) - ) + ): @unchecked verifyTags(searchableArticle) } @@ -159,7 +159,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { searchConverterService.asSearchableArticle( article, IndexingBundle(Some(TestData.emptyGrepBundle), Some(emptyBundle), Some(TestData.myndlaTestBundle)) - ) + ): @unchecked verifyTitles(searchableArticle) verifyArticles(searchableArticle) @@ -175,7 +175,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked val Success(searchable4) = searchConverterService.asSearchableArticle( TestData.article4, @@ -184,7 +184,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked val Success(searchable7) = searchConverterService.asSearchableArticle( TestData.article7, @@ -193,7 +193,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked searchable2.contexts.head.resourceTypeIds.sorted should be( Seq("urn:resourcetype:subjectMaterial", "urn:resourcetype:academicArticle").sorted @@ -219,7 +219,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked val Success(searchable4) = searchConverterService.asSearchableArticle( TestData.article4, @@ -228,7 +228,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked val Success(searchable6) = searchConverterService.asSearchableArticle( TestData.article6, @@ -237,7 +237,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked searchable1.contexts.size should be(2) searchable1.contexts.head.breadcrumbs.languageValues.map(_.value) should be( @@ -289,7 +289,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked val Success(searchable4) = searchConverterService.asSearchableArticle( TestData.article4, @@ -298,7 +298,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked val Success(searchable5) = searchConverterService.asSearchableArticle( TestData.article5, @@ -307,7 +307,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked searchable1.contexts.size should be(2) searchable1.contexts.map(_.domainObject.root.languageValues.map(_.value)) should be( @@ -337,7 +337,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(taxonomyBundleInvisibleMetadata), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked val Success(searchable4) = searchConverterService.asSearchableArticle( TestData.article4, @@ -346,7 +346,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(taxonomyBundleInvisibleMetadata), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked val Success(searchable5) = searchConverterService.asSearchableArticle( TestData.article5, @@ -355,7 +355,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(taxonomyBundleInvisibleMetadata), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked searchable1.contexts.size should be(0) searchable4.contexts.size should be(0) @@ -377,7 +377,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(taxonomyBundleInvisibleMetadata), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked val Success(searchable4) = searchConverterService.asSearchableArticle( TestData.article4, @@ -386,7 +386,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(taxonomyBundleInvisibleMetadata), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked val Success(searchable5) = searchConverterService.asSearchableArticle( TestData.article5, @@ -395,7 +395,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(taxonomyBundleInvisibleMetadata), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked searchable1.contexts.size should be(0) searchable4.contexts.size should be(0) @@ -411,7 +411,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked val Success(searchable4) = searchConverterService.asSearchableArticle( TestData.article4, @@ -420,7 +420,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked val Success(searchable5) = searchConverterService.asSearchableArticle( TestData.article5, @@ -429,7 +429,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked searchable1.contexts.size should be(2) searchable4.contexts.size should be(1) @@ -445,7 +445,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(TestData.taxonomyTestBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked searchable1.contexts.size should be(3) } @@ -465,7 +465,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { Some(emptyBundle), Some(TestData.myndlaTestBundle) ) - ) + ): @unchecked searchableArticle.grepContexts should equal(grepContexts) } @@ -484,7 +484,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { searchConverterService.asSearchableArticle( article, IndexingBundle(Some(TestData.grepBundle), Some(emptyBundle), Some(TestData.myndlaTestBundle)) - ) + ): @unchecked searchableArticle.grepContexts should equal(grepContexts) } @@ -496,7 +496,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { searchConverterService.asSearchableArticle( article, IndexingBundle(Some(TestData.grepBundle), Some(emptyBundle), Some(TestData.myndlaTestBundle)) - ) + ): @unchecked searchableArticle.grepContexts should equal(grepContexts) } @@ -511,7 +511,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { searchConverterService.asSearchableDraft( draft, IndexingBundle(Some(TestData.emptyGrepBundle), Some(emptyBundle), None) - ) + ): @unchecked searchableArticle.grepContexts should equal(grepContexts) } @@ -576,7 +576,10 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { SearchableGrepContext("TT2", Some("tittel2"), "Published") ) val Success(searchableArticle) = - searchConverterService.asSearchableDraft(draft, IndexingBundle(Some(grepBundle), Some(emptyBundle), None)) + searchConverterService.asSearchableDraft( + draft, + IndexingBundle(Some(grepBundle), Some(emptyBundle), None) + ): @unchecked searchableArticle.grepContexts should equal(grepContexts) } @@ -626,7 +629,10 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { val grepContexts = List.empty val Success(searchableArticle) = - searchConverterService.asSearchableDraft(draft, IndexingBundle(Some(grepBundle), Some(emptyBundle), None)) + searchConverterService.asSearchableDraft( + draft, + IndexingBundle(Some(grepBundle), Some(emptyBundle), None) + ): @unchecked searchableArticle.grepContexts should equal(grepContexts) } @@ -647,7 +653,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { searchConverterService.asSearchableArticle( article, IndexingBundle(Some(TestData.emptyGrepBundle), Some(emptyBundle), None) - ) + ): @unchecked searchableArticle.traits should equal(List(SearchTrait.H5p)) val article2 = @@ -673,7 +679,7 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { searchConverterService.asSearchableArticle( article2, IndexingBundle(Some(TestData.emptyGrepBundle), Some(emptyBundle), None) - ) + ): @unchecked searchableArticle2.traits should equal(List(SearchTrait.H5p, SearchTrait.Video)) } @@ -754,7 +760,11 @@ class SearchConverterServiceTest extends UnitSuite with TestEnvironment { SearchableGrepContext("KV123", Some("tittel123"), "Published") ) val Success(searchableNode) = - searchConverterService.asSearchableNode(node, None, IndexingBundle(Some(grepBundle), Some(emptyBundle), None)) + searchConverterService.asSearchableNode( + node, + None, + IndexingBundle(Some(grepBundle), Some(emptyBundle), None) + ): @unchecked searchableNode.grepContexts should equal(grepContexts) } diff --git a/search-api/src/test/scala/no/ndla/searchapi/service/search/SearchServiceTest.scala b/search-api/src/test/scala/no/ndla/searchapi/service/search/SearchServiceTest.scala index bf4fd64c9d..2c0034405d 100644 --- a/search-api/src/test/scala/no/ndla/searchapi/service/search/SearchServiceTest.scala +++ b/search-api/src/test/scala/no/ndla/searchapi/service/search/SearchServiceTest.scala @@ -13,10 +13,10 @@ import no.ndla.searchapi.{TestEnvironment, UnitSuite} class SearchServiceTest extends UnitSuite with TestEnvironment { - override val draftIndexService: DraftIndexService = new DraftIndexService { + override lazy val draftIndexService: DraftIndexService = new DraftIndexService { override val indexShards = 1 } - override val learningPathIndexService: LearningPathIndexService = new LearningPathIndexService { + override lazy val learningPathIndexService: LearningPathIndexService = new LearningPathIndexService { override val indexShards = 1 } diff --git a/search/src/main/scala/no/ndla/search/BaseIndexService.scala b/search/src/main/scala/no/ndla/search/BaseIndexService.scala index 66d71123b6..87eaec12dd 100644 --- a/search/src/main/scala/no/ndla/search/BaseIndexService.scala +++ b/search/src/main/scala/no/ndla/search/BaseIndexService.scala @@ -17,18 +17,18 @@ import com.sksamuel.elastic4s.requests.indexes.{CreateIndexRequest, IndexRequest import com.sksamuel.elastic4s.requests.mappings.MappingDefinition import com.typesafe.scalalogging.StrictLogging import no.ndla.common.configuration.HasBaseProps -import no.ndla.common.implicits.TryQuestionMark +import no.ndla.common.implicits.* import no.ndla.search.model.domain.{BulkIndexResult, ElasticIndexingException, ReindexResult} import java.text.SimpleDateFormat import java.util.Calendar import scala.util.{Failure, Success, Try} +import scala.util.boundary trait BaseIndexService { this: Elastic4sClient & HasBaseProps & SearchLanguage => - trait BaseIndexService extends StrictLogging { - import SearchLanguage.NynorskLanguageAnalyzer + abstract class BaseIndexService extends StrictLogging { val documentType: String val searchIndex: String val MaxResultWindowOption: Int @@ -38,7 +38,7 @@ trait BaseIndexService { val analysis: Analysis = Analysis( - analyzers = List(NynorskLanguageAnalyzer), + analyzers = List(SearchLanguage.NynorskLanguageAnalyzer), tokenFilters = SearchLanguage.NynorskTokenFilters ) @@ -146,22 +146,24 @@ trait BaseIndexService { def createIndexWithGeneratedName: Try[String] = createIndexWithName(getNewIndexName()) - def reindexWithShards(numShards: Int): Try[?] = { - logger.info(s"Internal reindexing $searchIndex with $numShards shards...") - val maybeAliasTarget = getAliasTarget.? - val currentIndex = maybeAliasTarget match { - case Some(target) => target - case None => - logger.info(s"No existing $searchIndex index to reindex from") - return Success(()) - } + def reindexWithShards(numShards: Int): Try[?] = boundary { + permitTry { + logger.info(s"Internal reindexing $searchIndex with $numShards shards...") + val maybeAliasTarget = getAliasTarget.? + val currentIndex = maybeAliasTarget match { + case Some(target) => target + case None => + logger.info(s"No existing $searchIndex index to reindex from") + boundary.break(Success(())) + } - for { - newIndex <- createIndexWithGeneratedName(numShards.some) - _ = logger.info(s"Created index $newIndex for internal reindexing") - _ <- e4sClient.execute(reindex(currentIndex, newIndex)) - _ <- updateAliasTarget(currentIndex.some, newIndex) - } yield () + for { + newIndex <- createIndexWithGeneratedName(numShards.some) + _ = logger.info(s"Created index $newIndex for internal reindexing") + _ <- e4sClient.execute(reindex(currentIndex, newIndex)) + _ <- updateAliasTarget(currentIndex.some, newIndex) + } yield () + } } def createIndexIfNotExists(): Try[?] = getAliasTarget.flatMap { diff --git a/search/src/main/scala/no/ndla/search/Elastic4sClient.scala b/search/src/main/scala/no/ndla/search/Elastic4sClient.scala index c81be5e56f..808caf108c 100644 --- a/search/src/main/scala/no/ndla/search/Elastic4sClient.scala +++ b/search/src/main/scala/no/ndla/search/Elastic4sClient.scala @@ -20,6 +20,7 @@ import java.util.concurrent.Executors import scala.concurrent.duration.DurationInt import scala.concurrent.{Await, ExecutionContext, ExecutionContextExecutor, Future} import scala.util.{Failure, Success, Try} +import scala.reflect.ClassTag trait Elastic4sClient { this: HasBaseProps => @@ -35,14 +36,19 @@ trait Elastic4sClient { private val clientExecutionContext: ExecutionContextExecutor = ExecutionContext.fromExecutor(Executors.newWorkStealingPool(props.MAX_SEARCH_THREADS)) - def executeAsync[T, U]( + def executeAsync[T, U: ClassTag]( request: T - )(implicit handler: Handler[T, U], mf: Manifest[U], ec: ExecutionContext): Future[Try[RequestSuccess[U]]] = { - val result = client.execute(request).map { - case failure: RequestFailure if failure.status == 409 => - Failure(DocumentConflictException(failure.error.reason)) - case failure: RequestFailure => Failure(NdlaSearchException(request, failure)) - case result: RequestSuccess[U] => Success(result) + )(implicit + handler: Handler[T, U], + ec: ExecutionContext + ): Future[Try[RequestSuccess[U]]] = { + val response = client.execute(request) + val result = response.map { + case RequestSuccess(status, body, headers, result) => + Success(RequestSuccess[U](status, body, headers, result)) + case RequestFailure(status, _, _, error) if status == 409 => + Failure(DocumentConflictException(error.reason)) + case failure: RequestFailure => Failure(NdlaSearchException(request, failure)) } result.onComplete { @@ -55,11 +61,11 @@ trait Elastic4sClient { def executeBlocking[T, U]( request: T - )(implicit handler: Handler[T, U], mf: Manifest[U], ec: ExecutionContext): Try[RequestSuccess[U]] = { + )(implicit handler: Handler[T, U], ct: ClassTag[U], ec: ExecutionContext): Try[RequestSuccess[U]] = { Try(Await.result(this.executeAsync(request), elasticTimeout)).flatten } - def execute[T, U](request: T)(implicit handler: Handler[T, U], mf: Manifest[U]): Try[RequestSuccess[U]] = { + def execute[T, U](request: T)(implicit handler: Handler[T, U], ct: ClassTag[U]): Try[RequestSuccess[U]] = { implicit val ec: ExecutionContextExecutor = clientExecutionContext val future = this.executeAsync(request) diff --git a/tapirtesting/src/main/scala/no/ndla/tapirtesting/TapirControllerTest.scala b/tapirtesting/src/main/scala/no/ndla/tapirtesting/TapirControllerTest.scala index ca9ac6330b..a590260351 100644 --- a/tapirtesting/src/main/scala/no/ndla/tapirtesting/TapirControllerTest.scala +++ b/tapirtesting/src/main/scala/no/ndla/tapirtesting/TapirControllerTest.scala @@ -15,6 +15,7 @@ import no.ndla.network.NdlaClient import no.ndla.network.clients.MyNDLAApiClient import no.ndla.network.tapir.{Routes, TapirController, TapirErrorHandling} import no.ndla.scalatestsuite.UnitTestSuite +import scala.compiletime.uninitialized trait TapirControllerTest extends UnitTestSuite @@ -29,7 +30,7 @@ trait TapirControllerTest val controller: TapirController override def services: List[TapirController] = List(controller) - var server: HttpServer = _ + var server: HttpServer = uninitialized override def beforeAll(): Unit = { super.beforeAll() diff --git a/testbase/src/main/scala/no/ndla/testbase/TestSuiteLoggingSetup.scala b/testbase/src/main/scala/no/ndla/testbase/TestSuiteLoggingSetup.scala index faf7bfe903..7c5c5bab03 100644 --- a/testbase/src/main/scala/no/ndla/testbase/TestSuiteLoggingSetup.scala +++ b/testbase/src/main/scala/no/ndla/testbase/TestSuiteLoggingSetup.scala @@ -53,10 +53,7 @@ trait TestSuiteLoggingSetup extends AnyFunSuite with BeforeAndAfterEach with Bef } private def shutdownLogger(): Unit = { - LoggerContext.getContext(false) match { - case context: LoggerContext => context.stop() - case _ => println("No LoggerContext found, cannot stop logging context.") - } + LoggerContext.getContext(false).stop() } override def withFixture(test: NoArgTest): Outcome = { diff --git a/validation/src/main/scala/no/ndla/validation/TagValidator.scala b/validation/src/main/scala/no/ndla/validation/TagValidator.scala index c24c786ca8..e912a7af22 100644 --- a/validation/src/main/scala/no/ndla/validation/TagValidator.scala +++ b/validation/src/main/scala/no/ndla/validation/TagValidator.scala @@ -9,6 +9,7 @@ package no.ndla.validation import io.circe.parser +import cats.implicits.* import io.lemonlabs.uri.typesafe.dsl.* import no.ndla.common.configuration.Constants.EmbedTagName import no.ndla.common.errors.ValidationMessage @@ -266,19 +267,19 @@ object TagValidator { ) ) } - tagRules.children.map(childrenRule => { + tagRules.children.flatMap(childrenRule => { if (childrenRule.required && embed.childNodeSize() == 0) { ValidationMessage( fieldName, s"Tag '$EmbedTagName' with `data-resource=$resourceType` requires at least one child." - ) + ).some } else { val childrenTagNames = embed.children().asScala.toList.map(_.tagName()) if (childrenRule.allowedChildren.isEmpty && childrenTagNames.nonEmpty) { ValidationMessage( fieldName, s"Tag '$EmbedTagName' with `data-resource=$resourceType` can only have plaintext children." - ) + ).some } else { val onlyValidChildren = childrenTagNames.forall(tag => childrenRule.allowedChildren.get.contains(tag)) if (!onlyValidChildren) { @@ -287,9 +288,9 @@ object TagValidator { s"Tag '$EmbedTagName' with `data-resource=$resourceType` can only have the following children tags: [${childrenRule.allowedChildren .getOrElse(List.empty) .mkString(", ")}]." - ) + ).some } else { - return None + None } } }