Skip to content

Commit

Permalink
Merge pull request #321 from NDLANO/config-lists
Browse files Browse the repository at this point in the history
learningpath-api: Add support for `arenaEnabled`/`MY_NDLA_ENABLED_ORGS`
  • Loading branch information
jnatten authored Nov 8, 2023
2 parents 58dd2cc + ee41d22 commit 705efee
Show file tree
Hide file tree
Showing 26 changed files with 422 additions and 142 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
UPDATE configtable c
SET value=(
SELECT value || jsonb_build_object('value', jsonb_build_object('value', (c2.value->>'value')::jsonb))
FROM configtable c2
WHERE c2.configkey = c.configkey
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
UPDATE my_ndla_users mnu
SET document=(
SELECT document || jsonb_build_object('arenaEnabled', false)
FROM my_ndla_users mnu2
WHERE mnu.id = mnu2.id
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import no.ndla.learningpathapi.db.migrationwithdependencies.{
V11__CreatedByNdlaStatusForOwnersWithRoles,
V13__StoreNDLAStepsAsIframeTypes,
V14__ConvertLanguageUnknown,
V15__MergeDuplicateLanguageFields
V15__MergeDuplicateLanguageFields,
V31__ArenaDefaultEnabledOrgs
}
import no.ndla.learningpathapi.integration.DataSource
import org.flywaydb.core.Flyway
Expand All @@ -31,7 +32,8 @@ trait DBMigrator {
new V11__CreatedByNdlaStatusForOwnersWithRoles(props),
new V13__StoreNDLAStepsAsIframeTypes(props),
new V14__ConvertLanguageUnknown(props),
new V15__MergeDuplicateLanguageFields(props)
new V15__MergeDuplicateLanguageFields(props),
new V31__ArenaDefaultEnabledOrgs(props)
)
.locations("no/ndla/learningpathapi/db/migration")
.dataSource(dataSource)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ package no.ndla.learningpathapi.controller

import no.ndla.common.model.NDLADate
import no.ndla.learningpathapi.Props
import no.ndla.learningpathapi.model.api.config.{ConfigMeta, ConfigMetaRestricted, UpdateConfigValue}
import no.ndla.learningpathapi.model.api.config.{ConfigMeta, ConfigMetaRestricted, ConfigMetaValue}
import no.ndla.learningpathapi.model.api.{Error, ValidationError}
import no.ndla.learningpathapi.model.domain.config.ConfigKey
import no.ndla.learningpathapi.service.{ReadService, UpdateService}
Expand Down Expand Up @@ -56,7 +56,9 @@ trait ConfigController {
val configKeyString = params("config_key")
ConfigKey.valueOf(configKeyString) match {
case None =>
BadRequest(s"No such config key was found. Must be one of '${ConfigKey.values.mkString("', '")}'")
BadRequest(
s"No such config key was found. Must be one of '${ConfigKey.all.mkString("', '")}'"
)
case Some(configKey) => callback(configKey)
}
}
Expand Down Expand Up @@ -86,15 +88,16 @@ trait ConfigController {
.parameters(
asHeaderParam(correlationId),
asPathParam(configKeyPathParam),
bodyParam[UpdateConfigValue]
bodyParam[ConfigMetaValue]
)
.responseMessages(response400, response404, response403, response500)
.authorizations("oauth2")
)
) {
requireUserId { userInfo =>
withConfigKey(configKey => {
val newConfigValue = extract[UpdateConfigValue](request.body)
val inpString = request.body
val newConfigValue = extract[ConfigMetaValue](inpString)
updateService.updateConfig(configKey, newConfigValue, userInfo) match {
case Success(c) => c
case Failure(ex) => errorHandler(ex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package no.ndla.learningpathapi.controller
import no.ndla.common.model.NDLADate
import no.ndla.learningpathapi.model.api.{Error, ExportedUserData, MyNDLAUser, UpdatedMyNDLAUser, ValidationError}
import no.ndla.learningpathapi.service.{ConverterService, ReadService, UpdateService}
import no.ndla.network.tapir.auth.Permission.LEARNINGPATH_API_ADMIN
import org.json4s.ext.JavaTimeSerializers
import org.json4s.{DefaultFormats, Formats}
import org.scalatra.NoContent
Expand Down Expand Up @@ -40,6 +41,7 @@ trait UserController {
val response500: ResponseMessage = ResponseMessage(500, "Unknown error", Some("Error"))

private val feideToken = Param[Option[String]]("FeideAuthorization", "Header containing FEIDE access token.")
private val feideId = Param[Option[String]]("feide-id", "FeideID of user")

private def requestFeideToken(implicit request: HttpServletRequest): Option[String] = {
request.header(this.feideToken.paramName).map(_.replaceFirst("Bearer ", ""))
Expand Down Expand Up @@ -79,6 +81,27 @@ trait UserController {
updateService.updateMyNDLAUserData(updatedUserData, requestFeideToken)
}: Unit

patch(
"/update-other-user/?",
operation(
apiOperation[MyNDLAUser]("AdminUpdateMyNDLAUser")
.summary("Update some one elses user data")
.description("Update some one elses user data")
.parameters(
asQueryParam(feideId),
bodyParam[UpdatedMyNDLAUser]
)
.responseMessages(response403, response404, response500)
.authorizations("oauth2")
)
) {
requirePermissionOrAccessDeniedWithUser(LEARNINGPATH_API_ADMIN) { user =>
val updatedUserData = extract[UpdatedMyNDLAUser](request.body)
val feideId = paramOrNone(this.feideId.paramName)
updateService.adminUpdateMyNDLAUserData(updatedUserData, feideId, user)
}
}: Unit

delete(
"/delete-personal-data/?",
operation(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Part of NDLA learningpath-api
* Copyright (C) 2023 NDLA
*
* See LICENSE
*/

package no.ndla.learningpathapi.db.migrationwithdependencies

import io.circe.Json
import io.circe.syntax._
import no.ndla.common.model.NDLADate
import no.ndla.learningpathapi.LearningpathApiProperties
import org.flywaydb.core.api.migration.{BaseJavaMigration, Context}
import org.postgresql.util.PGobject
import scalikejdbc.{DB, DBSession, _}

class V31__ArenaDefaultEnabledOrgs(properties: LearningpathApiProperties) extends BaseJavaMigration {

override def migrate(context: Context): Unit = DB(context.getConnection)
.autoClose(false)
.withinTx { implicit session =>
insertConfig
}

def insertConfig(implicit session: DBSession): Unit = {
val document = Json.obj(
"key" -> Json.fromString("ARENA_ENABLED_ORGS"),
"value" -> Json.obj("value" -> Json.fromValues(orgs.map(Json.fromString))),
"updatedAt" -> NDLADate.now().asJson,
"updatedBy" -> Json.fromString("System")
)

val dataObject = new PGobject()
dataObject.setType("jsonb")
dataObject.setValue(document.noSpaces)

val inserted = sql"""
INSERT INTO configtable(configkey, value)
VALUES (
'ARENA_ENABLED_ORGS',
$dataObject
)
""".update.apply()

if (inserted != 1) throw new RuntimeException("Failed to insert ARENA_ENABLED_ORGS")
}

private def orgs: List[String] = properties.Environment match {
case "local" | "test" =>
List(
"Agder fylkeskommune",
"Innlandet fylkeskommune",
"Møre og Romsdal fylkeskommune",
"Nordland fylkeskommune",
"Rogaland fylkeskommune",
"Troms og Finnmark fylkeskommune",
"Trøndelag fylkeskommune",
"Vestfold og Telemark fylkeskommune",
"Vestland fylkeskommune",
"Viken fylkeskommune",
"Universitetet i Rogn"
)
case _ => List.empty
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ case class MyNDLAUser(
@(ApiModelProperty @field)(description = "ID of the user") id: Long,
@(ApiModelProperty @field)(description = "Favorite subjects of the user") favoriteSubjects: Seq[String],
@(ApiModelProperty @field)(description = "User role") role: String,
@(ApiModelProperty @field)(description = "User organization") organization: String
@(ApiModelProperty @field)(description = "User organization") organization: String,
@(ApiModelProperty @field)(description = "Whether arena is explicitly enabled for the user") arenaEnabled: Boolean
)

// format: off
case class UpdatedMyNDLAUser(
@(ApiModelProperty @field)(description = "Favorite subjects of the user") favoriteSubjects: Option[Seq[String]]
@(ApiModelProperty @field)(description = "Favorite subjects of the user") favoriteSubjects: Option[Seq[String]],
@(ApiModelProperty @field)(description = "Whether arena should explicitly be enabled for the user") arenaEnabled: Option[Boolean]
)
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,10 @@ import no.ndla.common.model.NDLADate
import org.scalatra.swagger.annotations.ApiModelProperty
import org.scalatra.swagger.runtime.annotations.ApiModel

import scala.annotation.meta.field

@ApiModel(description = "Describes configuration value.")
case class ConfigMeta(
@(ApiModelProperty @field)(description = "Configuration key") key: String,
@(ApiModelProperty @field)(description = "Configuration value.") value: String,
@(ApiModelProperty @field)(description = "Date of when configuration was last updated") updatedAt: NDLADate,
@(ApiModelProperty @field)(
description = "UserId of who last updated the configuration parameter."
) updatedBy: String
@ApiModelProperty(description = "Configuration key") key: String,
@ApiModelProperty(description = "Configuration value.") value: Either[Boolean, List[String]],
@ApiModelProperty(description = "Date of when configuration was last updated") updatedAt: NDLADate,
@ApiModelProperty(description = "UserId of who last updated the configuration parameter.") updatedBy: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ import scala.annotation.meta.field
@ApiModel(description = "Describes configuration value.")
case class ConfigMetaRestricted(
@(ApiModelProperty @field)(description = "Configuration key") key: String,
@(ApiModelProperty @field)(description = "Configuration value.") value: String
@(ApiModelProperty @field)(description = "Configuration value.") value: Either[Boolean, List[String]]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Part of NDLA learningpath-api.
* Copyright (C) 2023 NDLA
*
* See LICENSE
*/

package no.ndla.learningpathapi.model.api.config

import no.ndla.learningpathapi.model.domain
import org.scalatra.swagger.annotations.ApiModelProperty

import scala.annotation.meta.field

case class ConfigMetaValue(
@(ApiModelProperty @field)(description = "Value to set configuration param to.")
value: Either[Boolean, List[String]]
)

object ConfigMetaValue {
def from(value: domain.config.ConfigMetaValue): ConfigMetaValue = {
value match {
case domain.config.BooleanValue(value) => ConfigMetaValue(Left(value))
case domain.config.StringListValue(value) => ConfigMetaValue(Right(value))
}
}

def apply(value: Boolean): ConfigMetaValue = ConfigMetaValue(Left(value))
def apply(value: List[String]): ConfigMetaValue = ConfigMetaValue(Right(value))
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ case class MyNDLAUserDocument(
userRole: UserRole.Value,
lastUpdated: NDLADate,
organization: String,
email: String
email: String,
arenaEnabled: Boolean
) {
def toFullUser(
id: Long,
Expand All @@ -34,7 +35,8 @@ case class MyNDLAUserDocument(
userRole = userRole,
lastUpdated = lastUpdated,
organization = organization,
email = email
email = email,
arenaEnabled = arenaEnabled
)
}
}
Expand All @@ -46,16 +48,9 @@ case class MyNDLAUser(
userRole: UserRole.Value,
lastUpdated: NDLADate,
organization: String,
email: String
email: String,
arenaEnabled: Boolean
) {
def toDocument: MyNDLAUserDocument = MyNDLAUserDocument(
favoriteSubjects = favoriteSubjects,
userRole = userRole,
lastUpdated = lastUpdated,
organization = organization,
email = email
)

// Keeping FEIDE and our data in sync
def wasUpdatedLast24h: Boolean = NDLADate.now().isBefore(lastUpdated.minusSeconds(10))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import enumeratum._

sealed abstract class ConfigKey(override val entryName: String) extends EnumEntry

object ConfigKey extends Enum[ConfigKey] {
object ConfigKey extends Enum[ConfigKey] with CirceEnum[ConfigKey] {
case object LearningpathWriteRestricted extends ConfigKey("LEARNINGPATH_WRITE_RESTRICTED")
case object MyNDLAWriteRestricted extends ConfigKey("MY_NDLA_WRITE_RESTRICTED")
case object ArenaEnabledOrgs extends ConfigKey("ARENA_ENABLED_ORGS")

val values: IndexedSeq[ConfigKey] = findValues

val all: Seq[String] = values.map(_.entryName)

def valueOf(s: String): Option[ConfigKey] = ConfigKey.values.find(_.entryName == s)

}
Loading

0 comments on commit 705efee

Please sign in to comment.