From 02987edce5fb79bc6d7f07d0c389b774cae18800 Mon Sep 17 00:00:00 2001 From: Gunnar Velle Date: Tue, 26 Nov 2024 07:06:37 +0100 Subject: [PATCH 1/3] Add topic to possible favorite types --- .../common/model/domain/ResourceType.scala | 7 +++-- .../controller/FolderController.scala | 28 +++++++++---------- .../controller/StatsController.scala | 4 +-- .../myndlaapi/service/FolderReadService.scala | 9 ++---- .../service/FolderWriteService.scala | 18 ++++-------- .../ndla/myndlaapi/e2e/CloneFolderTest.scala | 7 +++-- .../repository/FolderRepositoryTest.scala | 19 +++++++++++-- 7 files changed, 49 insertions(+), 43 deletions(-) diff --git a/common/src/main/scala/no/ndla/common/model/domain/ResourceType.scala b/common/src/main/scala/no/ndla/common/model/domain/ResourceType.scala index 52ca3625f..f827090af 100644 --- a/common/src/main/scala/no/ndla/common/model/domain/ResourceType.scala +++ b/common/src/main/scala/no/ndla/common/model/domain/ResourceType.scala @@ -26,11 +26,12 @@ object ResourceType extends Enum[ResourceType] with CirceEnum[ResourceType] { implicit val enumTsType: TSNamedType[ResourceType] = TSType.alias[ResourceType]("ResourceType", TSUnion(values.map(e => TSLiteralString(e.entryName)))) + case object Article extends ResourceType("article") + case object Audio extends ResourceType("audio") case object Concept extends ResourceType("concept") case object Image extends ResourceType("image") - case object Audio extends ResourceType("audio") - case object Multidisciplinary extends ResourceType("multidisciplinary") - case object Article extends ResourceType("article") case object Learningpath extends ResourceType("learningpath") + case object Multidisciplinary extends ResourceType("multidisciplinary") + case object Topic extends ResourceType("topic") case object Video extends ResourceType("video") } 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 1973db455..9ef2d29ca 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 @@ -38,7 +38,7 @@ import sttp.tapir.server.ServerEndpoint import java.util.UUID trait FolderController { - this: FolderReadService with FolderWriteService with ErrorHandling with TapirController => + this: FolderReadService & FolderWriteService & ErrorHandling & TapirController => val folderController: FolderController class FolderController extends TapirController { @@ -71,7 +71,7 @@ trait FolderController { s"Which resource types to exclude. If None all resource types are included. To provide multiple resource types, separate by comma (,)." ) - import io.circe.generic.auto._ + import io.circe.generic.auto.* private def getAllFolders: ServerEndpoint[Any, Eff] = endpoint.get .summary("Fetch top folders that belongs to a user") @@ -111,7 +111,7 @@ trait FolderController { folderWriteService.newFolder(newFolder, feideHeader) } - private def updateFolder: ServerEndpoint[Any, Eff] = endpoint.patch + private def updateFolder(): ServerEndpoint[Any, Eff] = endpoint.patch .summary("Update folder with new data") .description("Update folder with new data") .in(feideHeader) @@ -123,7 +123,7 @@ trait FolderController { folderWriteService.updateFolder(folderId, updatedFolder, feideHeader) } - private def removeFolder: ServerEndpoint[Any, Eff] = endpoint.delete + private def removeFolder(): ServerEndpoint[Any, Eff] = endpoint.delete .summary("Remove folder from user folders") .description("Remove folder from user folders") .in(feideHeader) @@ -134,7 +134,7 @@ trait FolderController { folderWriteService.deleteFolder(folderId, feideHeader).map(_ => ()) } - val defaultSize = 5 + private val defaultSize: Int = 5 val size: EndpointInput.Query[Int] = query[Int]("size") .description("Limit the number of results to this many elements") .default(defaultSize) @@ -179,7 +179,7 @@ trait FolderController { folderWriteService.newFolderResourceConnection(folderId, newResource, feideHeader) } - private def updateResource: ServerEndpoint[Any, Eff] = endpoint.patch + private def updateResource(): ServerEndpoint[Any, Eff] = endpoint.patch .summary("Updated selected resource") .description("Updates selected resource") .in("resources" / pathResourceId) @@ -191,7 +191,7 @@ trait FolderController { folderWriteService.updateResource(resourceId, updatedResource, feideHeader) } - private def deleteResource: ServerEndpoint[Any, Eff] = endpoint.delete + private def deleteResource(): ServerEndpoint[Any, Eff] = endpoint.delete .summary("Delete selected resource") .description("Delete selected resource") .in(feideHeader) @@ -215,7 +215,7 @@ trait FolderController { folderReadService.getSharedFolder(folderId, feideHeader) } - val folderStatus: EndpointInput.Query[FolderStatus.Value] = + private val folderStatus: EndpointInput.Query[FolderStatus.Value] = query[FolderStatus.Value]("folder-status").description("Status of the folder") private def changeStatusForFolderAndSubFolders: ServerEndpoint[Any, Eff] = endpoint.patch .summary("Change status for given folder and all its subfolders") @@ -292,7 +292,7 @@ trait FolderController { folderWriteService.newSaveSharedFolder(folderId, feideHeader) } - private def deleteFolderUserConnection: ServerEndpoint[Any, Eff] = endpoint.delete + private def deleteFolderUserConnection(): ServerEndpoint[Any, Eff] = endpoint.delete .summary("Deletes a saved shared folder") .description("Deletes a saved shared folder") .in("shared" / pathFolderId / "save") @@ -309,11 +309,11 @@ trait FolderController { fetchRecent, getSingleFolder, createNewFolder, - updateFolder, - removeFolder, + updateFolder(), + removeFolder(), createFolderResource, - updateResource, - deleteResource, + updateResource(), + deleteResource(), fetchSharedFolder, changeStatusForFolderAndSubFolders, cloneFolder, @@ -321,7 +321,7 @@ trait FolderController { sortFolderFolders, sortSavedSharedFolders, createFolderUserConnection, - deleteFolderUserConnection + deleteFolderUserConnection() ) } } diff --git a/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/StatsController.scala b/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/StatsController.scala index cb8f55a61..c42e72433 100644 --- a/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/StatsController.scala +++ b/myndla-api/src/main/scala/no/ndla/myndlaapi/controller/StatsController.scala @@ -47,7 +47,7 @@ trait StatsController { private val pathResourceIds = path[CommaSeparated[String]]("resourceIds").description("IDs of the resources to look up") - def getFolderResourceFavorites: ServerEndpoint[Any, Eff] = endpoint.get + private def getFolderResourceFavorites: ServerEndpoint[Any, Eff] = endpoint.get .summary("Get folder resource favorites") .description("Get folder resource favorites") .in("favorites" / pathResourceType / pathResourceIds) @@ -57,7 +57,7 @@ trait StatsController { folderReadService.getFavouriteStatsForResource(resourceIds.values, resourceType.values) } - def getAllTheFavorites: ServerEndpoint[Any, Eff] = endpoint.get + private def getAllTheFavorites: ServerEndpoint[Any, Eff] = endpoint.get .summary("Get number of favorites for favorited resources") .description("Get number of favorites for favorited resources") .in("favorites") 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 9d38067fd..e92d40ac1 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 @@ -29,13 +29,8 @@ import scala.annotation.tailrec import scala.util.{Failure, Success, Try} trait FolderReadService { - this: FolderConverterService - with FolderRepository - with UserRepository - with FeideApiClient - with Clock - with ConfigService - with UserService => + this: FolderConverterService & FolderRepository & UserRepository & FeideApiClient & Clock & ConfigService & + UserService => val folderReadService: FolderReadService 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 78f1f1100..8e0279f7a 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 @@ -50,15 +50,8 @@ import scala.annotation.tailrec import scala.util.{Failure, Success, Try} trait FolderWriteService { - this: FolderReadService - with Clock - with FeideApiClient - with FolderRepository - with FolderConverterService - with UserRepository - with ConfigService - with UserService - with SearchApiClient => + this: FolderReadService & Clock & FeideApiClient & FolderRepository & FolderConverterService & UserRepository & + ConfigService & UserService & SearchApiClient => val folderWriteService: FolderWriteService class FolderWriteService { @@ -543,7 +536,7 @@ trait FolderWriteService { _ <- checkDepth(validatedParentId) } yield validatedParentId - private def getNextRank(siblings: Seq[_]): Int = siblings.length + 1 + private def getNextRank(siblings: Seq[?]): Int = siblings.length + 1 private[service] def changeStatusToSharedIfParentIsShared( newFolder: NewFolder, @@ -630,6 +623,7 @@ trait FolderWriteService { resource.resourceType match { case ResourceType.Multidisciplinary => searchApiClient.reindexDraft(resource.resourceId) case ResourceType.Article => searchApiClient.reindexDraft(resource.resourceId) + case ResourceType.Topic => searchApiClient.reindexDraft(resource.resourceId) case ResourceType.Learningpath => searchApiClient.reindexLearningpath(resource.resourceId) case _ => } @@ -697,7 +691,7 @@ trait FolderWriteService { def canWriteDuringMyNDLAWriteRestrictionsOrAccessDenied( feideId: FeideID, feideAccessToken: Option[FeideAccessToken] - ): Try[_] = { + ): Try[?] = { getMyNDLAUser(feideId, feideAccessToken) .flatMap(myNDLAUser => canWriteNow(myNDLAUser).flatMap { @@ -739,7 +733,7 @@ trait FolderWriteService { folderRepository.deleteFolderUserConnection(folderId.some, feideId.some) } - private def isTeacherOrAccessDenied(feideId: FeideID, feideAccessToken: Option[FeideAccessToken]): Try[_] = { + private def isTeacherOrAccessDenied(feideId: FeideID, feideAccessToken: Option[FeideAccessToken]): Try[?] = { getMyNDLAUser(feideId, feideAccessToken) .flatMap(myNDLAUser => { if (myNDLAUser.isTeacher) Success(()) diff --git a/myndla-api/src/test/scala/no/ndla/myndlaapi/e2e/CloneFolderTest.scala b/myndla-api/src/test/scala/no/ndla/myndlaapi/e2e/CloneFolderTest.scala index 54b166dd5..824bb9f66 100644 --- a/myndla-api/src/test/scala/no/ndla/myndlaapi/e2e/CloneFolderTest.scala +++ b/myndla-api/src/test/scala/no/ndla/myndlaapi/e2e/CloneFolderTest.scala @@ -28,7 +28,7 @@ import sttp.client3.quick.* import java.util.UUID import java.util.concurrent.Executors -import scala.concurrent.{ExecutionContext, Future} +import scala.concurrent.{ExecutionContext, ExecutionContextExecutorService, Future} import scala.concurrent.duration.DurationInt import scala.util.{Failure, Success} @@ -41,7 +41,7 @@ class CloneFolderTest with UnitSuite { val myndlaApiPort: Int = findFreePort - val pgc: PostgreSQLContainer[_] = postgresContainer.get + val pgc: PostgreSQLContainer[?] = postgresContainer.get val redisPort: Int = redisContainer.get.port val myndlaproperties: MyNdlaApiProperties = new MyNdlaApiProperties { override def ApplicationPort: Int = myndlaApiPort @@ -87,7 +87,8 @@ class CloneFolderTest val myndlaApiFolderUrl: String = s"$myndlaApiBaseUrl/myndla-api/v1/folders" override def beforeAll(): Unit = { - implicit val ec = ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor) + implicit val ec: ExecutionContextExecutorService = + ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor) Future { myndlaApi.run() }: Unit Thread.sleep(4000) } 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 d41e76bfe..40a7adb17 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 @@ -114,13 +114,28 @@ class FolderRepositoryTest val resource1 = repository.insertResource("feide", "/path1", ResourceType.Article, created, TestData.baseResourceDocument) val resource2 = - repository.insertResource("feide", "/path2", ResourceType.Article, created, TestData.baseResourceDocument) + repository.insertResource("feide", "/path2", ResourceType.Topic, created, TestData.baseResourceDocument) val resource3 = - repository.insertResource("feide", "/path3", ResourceType.Article, created, TestData.baseResourceDocument) + repository.insertResource("feide", "/path3", ResourceType.Multidisciplinary, created, TestData.baseResourceDocument) + val resource4 = + repository.insertResource("feide", "/path4", ResourceType.Image, created, TestData.baseResourceDocument) + val resource5 = + repository.insertResource("feide", "/path5", ResourceType.Audio, created, TestData.baseResourceDocument) + val resource6 = + repository.insertResource("feide", "/path6", ResourceType.Concept, created, TestData.baseResourceDocument) + val resource7 = + repository.insertResource("feide", "/path7", ResourceType.Learningpath, created, TestData.baseResourceDocument) + val resource8 = + repository.insertResource("feide", "/path8", ResourceType.Video, created, TestData.baseResourceDocument) repository.resourceWithId(resource1.get.id) should be(resource1) repository.resourceWithId(resource2.get.id) should be(resource2) repository.resourceWithId(resource3.get.id) should be(resource3) + repository.resourceWithId(resource4.get.id) should be(resource4) + repository.resourceWithId(resource5.get.id) should be(resource5) + repository.resourceWithId(resource6.get.id) should be(resource6) + repository.resourceWithId(resource7.get.id) should be(resource7) + repository.resourceWithId(resource8.get.id) should be(resource8) } test("that connecting folders and resources works as expected") { From 11dc320a78d463d3d6c7cfac93e08a55a07f67c9 Mon Sep 17 00:00:00 2001 From: Gunnar Velle Date: Tue, 26 Nov 2024 08:01:36 +0100 Subject: [PATCH 2/3] Update typescript types --- project/myndlaapi.scala | 1 + typescript/types-backend/myndla-api.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/project/myndlaapi.scala b/project/myndlaapi.scala index 59b061392..eaea020e3 100644 --- a/project/myndlaapi.scala +++ b/project/myndlaapi.scala @@ -33,6 +33,7 @@ object myndlaapi extends Module { "ConfigMetaRestricted", "no.ndla.common.model.api.myndla.MyNDLAUser", "no.ndla.common.model.api.myndla.UpdatedMyNDLAUser", + "no.ndla.common.model.domain.ResourceType", "config.ConfigMeta", "Folder", "FolderData", diff --git a/typescript/types-backend/myndla-api.ts b/typescript/types-backend/myndla-api.ts index e8085a52d..b05ec457f 100644 --- a/typescript/types-backend/myndla-api.ts +++ b/typescript/types-backend/myndla-api.ts @@ -288,4 +288,4 @@ export interface IUserFolder { sharedFolders: IFolder[] } -export type ResourceType = ("concept" | "image" | "audio" | "multidisciplinary" | "article" | "learningpath" | "video") +export type ResourceType = ("article" | "audio" | "concept" | "image" | "learningpath" | "multidisciplinary" | "topic" | "video") From 8967ab1bcf79e22ceceddbcb2e4a9b59200f6da9 Mon Sep 17 00:00:00 2001 From: Gunnar Velle Date: Thu, 28 Nov 2024 08:11:46 +0100 Subject: [PATCH 3/3] Add stats for favorited topics --- .../searchapi/service/search/SearchConverterService.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 43a75d399..342b30917 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 @@ -428,12 +428,15 @@ trait SearchConverterService { Success( value.getFavorites( draft.id.get.toString, - List(MyNDLAResourceType.Article, MyNDLAResourceType.Multidisciplinary) + List(MyNDLAResourceType.Article, MyNDLAResourceType.Multidisciplinary, MyNDLAResourceType.Topic) ) ) case None => myndlaApiClient - .getStatsFor(draft.id.get.toString, List(MyNDLAResourceType.Article, MyNDLAResourceType.Multidisciplinary)) + .getStatsFor( + draft.id.get.toString, + List(MyNDLAResourceType.Article, MyNDLAResourceType.Multidisciplinary, MyNDLAResourceType.Topic) + ) .map(_.map(_.favourites).sum) }).?