Skip to content

Commit

Permalink
Save scope initialization errors (#4620)
Browse files Browse the repository at this point in the history
  • Loading branch information
olivergrabinski authored Jan 4, 2024
1 parent 6644f89 commit e868da8
Show file tree
Hide file tree
Showing 14 changed files with 219 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package ch.epfl.bluebrain.nexus.delta.plugins.blazegraph

import cats.effect.IO

import ch.epfl.bluebrain.nexus.delta.kernel.Logger
import ch.epfl.bluebrain.nexus.delta.kernel.kamon.KamonMetricComponent
import ch.epfl.bluebrain.nexus.delta.kernel.syntax.kamonSyntax
import ch.epfl.bluebrain.nexus.delta.plugins.blazegraph.BlazegraphViews.entityType
import ch.epfl.bluebrain.nexus.delta.plugins.blazegraph.model.BlazegraphViewRejection.{ProjectContextRejection, ResourceAlreadyExists}
import ch.epfl.bluebrain.nexus.delta.plugins.blazegraph.model.BlazegraphViewValue.IndexingBlazegraphViewValue
import ch.epfl.bluebrain.nexus.delta.plugins.blazegraph.model._
Expand All @@ -14,8 +12,8 @@ import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.ServiceAccount
import ch.epfl.bluebrain.nexus.delta.sdk.organizations.model.Organization
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.Project
import ch.epfl.bluebrain.nexus.delta.sdk.{Defaults, ScopeInitialization}
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{EntityType, Identity}

/**
* The default creation of the default SparqlView as part of the project initialization.
Expand All @@ -24,6 +22,8 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject
* the BlazegraphViews module
* @param serviceAccount
* the subject that will be recorded when performing the initialization
* @param defaults
* default name and description for the view
*/
class BlazegraphScopeInitialization(
views: BlazegraphViews,
Expand All @@ -33,7 +33,7 @@ class BlazegraphScopeInitialization(

private val logger = Logger[BlazegraphScopeInitialization]
implicit private val serviceAccountSubject: Subject = serviceAccount.subject
implicit private val kamonComponent: KamonMetricComponent = KamonMetricComponent(entityType.value)
implicit private val kamonComponent: KamonMetricComponent = KamonMetricComponent(BlazegraphViews.entityType.value)

private def defaultValue: IndexingBlazegraphViewValue = IndexingBlazegraphViewValue(
name = Some(defaults.name),
Expand Down Expand Up @@ -65,4 +65,6 @@ class BlazegraphScopeInitialization(
subject: Identity.Subject
): IO[Unit] = IO.unit

override def entityType: EntityType = BlazegraphViews.entityType

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package ch.epfl.bluebrain.nexus.delta.plugins.elasticsearch

import cats.effect.IO

import ch.epfl.bluebrain.nexus.delta.kernel.Logger
import ch.epfl.bluebrain.nexus.delta.kernel.kamon.KamonMetricComponent
import ch.epfl.bluebrain.nexus.delta.kernel.syntax._
import ch.epfl.bluebrain.nexus.delta.plugins.elasticsearch.ElasticSearchViews.entityType
import ch.epfl.bluebrain.nexus.delta.plugins.elasticsearch.model.ElasticSearchViewRejection.{ProjectContextRejection, ResourceAlreadyExists}
import ch.epfl.bluebrain.nexus.delta.plugins.elasticsearch.model.ElasticSearchViewValue.IndexingElasticSearchViewValue
import ch.epfl.bluebrain.nexus.delta.plugins.elasticsearch.model.{defaultViewId, permissions}
Expand All @@ -15,8 +13,8 @@ import ch.epfl.bluebrain.nexus.delta.sdk.organizations.model.Organization
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.Project
import ch.epfl.bluebrain.nexus.delta.sdk.views.PipeStep
import ch.epfl.bluebrain.nexus.delta.sdk.{Defaults, ScopeInitialization}
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{EntityType, Identity}
import ch.epfl.bluebrain.nexus.delta.sourcing.stream.pipes.{DefaultLabelPredicates, SourceAsText}

/**
Expand All @@ -26,6 +24,8 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.stream.pipes.{DefaultLabelPredicat
* the ElasticSearchViews module
* @param serviceAccount
* the subject that will be recorded when performing the initialization
* @param defaults
* default name and description for the view
*/
class ElasticSearchScopeInitialization(
views: ElasticSearchViews,
Expand All @@ -35,7 +35,7 @@ class ElasticSearchScopeInitialization(

private val logger = Logger[ElasticSearchScopeInitialization]
implicit private val serviceAccountSubject: Subject = serviceAccount.subject
implicit private val kamonComponent: KamonMetricComponent = KamonMetricComponent(entityType.value)
implicit private val kamonComponent: KamonMetricComponent = KamonMetricComponent(ElasticSearchViews.entityType.value)

lazy val defaultValue: IndexingElasticSearchViewValue =
IndexingElasticSearchViewValue(
Expand Down Expand Up @@ -68,4 +68,5 @@ class ElasticSearchScopeInitialization(
subject: Identity.Subject
): IO[Unit] = IO.unit

override def entityType: EntityType = ElasticSearchViews.entityType
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ch.epfl.bluebrain.nexus.delta.plugins.search

import cats.effect.IO

import ch.epfl.bluebrain.nexus.delta.kernel.Logger
import ch.epfl.bluebrain.nexus.delta.kernel.syntax._
import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.CompositeViews
Expand All @@ -14,7 +13,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.model.BaseUri
import ch.epfl.bluebrain.nexus.delta.sdk.organizations.model.Organization
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.Project
import ch.epfl.bluebrain.nexus.delta.sdk.{Defaults, ScopeInitialization}
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{EntityType, Identity}
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject

final class SearchScopeInitialization(
Expand Down Expand Up @@ -51,4 +50,6 @@ final class SearchScopeInitialization(
organization: Organization,
subject: Identity.Subject
): IO[Unit] = IO.unit

override def entityType: EntityType = EntityType("searchView")
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package ch.epfl.bluebrain.nexus.delta.plugins.storage

import cats.effect.IO

import ch.epfl.bluebrain.nexus.delta.kernel.Logger
import ch.epfl.bluebrain.nexus.delta.kernel.kamon.KamonMetricComponent
import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.Storages.entityType
import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.model.StorageFields.DiskStorageFields
import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.model.StorageRejection.{ProjectContextRejection, ResourceAlreadyExists}
import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.{defaultStorageId, Storages}
Expand All @@ -14,7 +12,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.implicits._
import ch.epfl.bluebrain.nexus.delta.sdk.organizations.model.Organization
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.Project
import ch.epfl.bluebrain.nexus.delta.sdk.{Defaults, ScopeInitialization}
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{EntityType, Identity}

/**
* The default creation of the default disk storage as part of the project initialization.
Expand All @@ -23,6 +21,8 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity
* the storages module
* @param serviceAccount
* the subject that will be recorded when performing the initialization
* @param defaults
* default name and description for the storage
*/
class StorageScopeInitialization(
storages: Storages,
Expand All @@ -31,7 +31,7 @@ class StorageScopeInitialization(
) extends ScopeInitialization {

private val logger = Logger[StorageScopeInitialization]
implicit private val kamonComponent: KamonMetricComponent = KamonMetricComponent(entityType.value)
implicit private val kamonComponent: KamonMetricComponent = KamonMetricComponent(Storages.entityType.value)

implicit private val caller: Caller = serviceAccount.caller

Expand Down Expand Up @@ -65,4 +65,5 @@ class StorageScopeInitialization(
subject: Identity.Subject
): IO[Unit] = IO.unit

override def entityType: EntityType = Storages.entityType
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ch.epfl.bluebrain.nexus.delta.sdk
import cats.effect.IO
import ch.epfl.bluebrain.nexus.delta.sdk.organizations.model.Organization
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.Project
import ch.epfl.bluebrain.nexus.delta.sourcing.model.EntityType
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject

/**
Expand Down Expand Up @@ -39,4 +40,6 @@ trait ScopeInitialization {
*/
def onProjectCreation(project: Project, subject: Subject): IO[Unit]

def entityType: EntityType

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import ch.epfl.bluebrain.nexus.delta.sdk.acls.model.{Acl, AclRejection}
import ch.epfl.bluebrain.nexus.delta.sdk.error.ServiceError.ScopeInitializationFailed
import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.ServiceAccount
import ch.epfl.bluebrain.nexus.delta.sdk.organizations.model.Organization
import ch.epfl.bluebrain.nexus.delta.sdk.permissions.Permissions
import ch.epfl.bluebrain.nexus.delta.sdk.permissions.model.Permission
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.Project
import ch.epfl.bluebrain.nexus.delta.sourcing.model.EntityType
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject

/**
Expand Down Expand Up @@ -51,6 +53,8 @@ class OwnerPermissionsScopeInitialization(appendAcls: Acl => IO[Unit], ownerPerm
logger.error(str) >> IO.raiseError(ScopeInitializationFailed(str))
}
.span("setProjectPermissions")

override def entityType: EntityType = Permissions.entityType
}

object OwnerPermissionsScopeInitialization {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import fs2.Stream
final class ProjectsImpl private (
log: ProjectsLog,
scopeInitializations: Set[ScopeInitialization],
errorStore: ScopeInitializationErrorStore,
override val defaultApiMappings: ApiMappings
) extends Projects {

Expand All @@ -33,15 +34,20 @@ final class ProjectsImpl private (
override def create(
ref: ProjectRef,
fields: ProjectFields
)(implicit caller: Subject): IO[ProjectResource] = {
)(implicit caller: Subject): IO[ProjectResource] =
for {
resource <- eval(CreateProject(ref, fields, caller)).span("createProject")
_ <- scopeInitializations
.parUnorderedTraverse(_.onProjectCreation(resource.value, caller))
.parUnorderedTraverse { init =>
init
.onProjectCreation(resource.value, caller)
.recoverWith { case e: ScopeInitializationFailed =>
errorStore.save(init.entityType, resource.value.ref, e)
}
}
.adaptError { case e: ScopeInitializationFailed => ProjectInitializationFailed(e) }
.span("initializeProject")
} yield resource
}

override def update(ref: ProjectRef, rev: Int, fields: ProjectFields)(implicit
caller: Subject
Expand Down Expand Up @@ -126,6 +132,7 @@ object ProjectsImpl {
new ProjectsImpl(
ScopedEventLog(Projects.definition(fetchAndValidateOrg, validateDeletion, clock), config.eventLog, xas),
scopeInitializations,
ScopeInitializationErrorStore(xas, clock),
defaultApiMappings
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ch.epfl.bluebrain.nexus.delta.sdk.projects

import cats.effect.{Clock, IO}
import cats.implicits._
import ch.epfl.bluebrain.nexus.delta.kernel.Logger
import ch.epfl.bluebrain.nexus.delta.sdk.error.ServiceError.ScopeInitializationFailed
import ch.epfl.bluebrain.nexus.delta.sourcing.Transactors
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{EntityType, ProjectRef}
import doobie.implicits._
import doobie.postgres.implicits._

trait ScopeInitializationErrorStore {

/**
* Save a scope initialization error
* @param entityType
* type of the entity this error is for
* @param project
* project for which the error occurred
* @param e
* the error to save
*/
def save(entityType: EntityType, project: ProjectRef, e: ScopeInitializationFailed): IO[Unit]

}

object ScopeInitializationErrorStore {

private val logger = Logger[ScopeInitializationErrorStore]

def apply(xas: Transactors, clock: Clock[IO]): ScopeInitializationErrorStore =
(entityType: EntityType, project: ProjectRef, e: ScopeInitializationFailed) => {
clock.realTimeInstant
.flatMap { instant =>
sql"""
|INSERT INTO scope_initialization_errors (type, org, project, message, instant)
|VALUES ($entityType, ${project.organization}, ${project.project}, ${e.getMessage}, $instant)
|""".stripMargin.update.run.void.transact(xas.write)
}
.onError { e =>
logger.error(e)(s"Failed to save error for '$entityType' initialization step on project '$project'")
}
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ch.epfl.bluebrain.nexus.delta.sdk.resolvers

import cats.effect.IO

import ch.epfl.bluebrain.nexus.delta.kernel.Logger
import ch.epfl.bluebrain.nexus.delta.kernel.kamon.KamonMetricComponent
import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.nxv
Expand All @@ -10,22 +9,21 @@ import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.{Caller, ServiceAccoun
import ch.epfl.bluebrain.nexus.delta.sdk.organizations.model.Organization
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.Project
import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverScopeInitialization.{logger, CreateResolver}
import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.Resolvers.entityType
import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResolverRejection.{ProjectContextRejection, ResourceAlreadyExists}
import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResolverValue.InProjectValue
import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.{Priority, ResolverValue}
import ch.epfl.bluebrain.nexus.delta.sdk.syntax._
import ch.epfl.bluebrain.nexus.delta.sdk.{Defaults, ScopeInitialization}
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject
import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{EntityType, ProjectRef}

/**
* The default creation of the InProject resolver as part of the project initialization.
*
* @param resolvers
* the resolvers module
* @param serviceAccount
* the subject that will be recorded when performing the initialization
* @param createResolver
* function used to create a resolver
* @param defaults
* default name and description for the resolver
*/
class ResolverScopeInitialization(createResolver: CreateResolver, defaults: Defaults) extends ScopeInitialization {

Expand All @@ -46,6 +44,8 @@ class ResolverScopeInitialization(createResolver: CreateResolver, defaults: Defa
.span("createDefaultResolver")

override def onOrganizationCreation(organization: Organization, subject: Subject): IO[Unit] = IO.unit

override def entityType: EntityType = Resolvers.entityType
}

object ResolverScopeInitialization {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package ch.epfl.bluebrain.nexus.delta.sdk
import cats.effect.IO
import ch.epfl.bluebrain.nexus.delta.sdk.error.ServiceError.ScopeInitializationFailed
import ch.epfl.bluebrain.nexus.delta.sdk.organizations.model.Organization
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.Project
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{EntityType, Identity}

/**
* Simple implementation that records created orgs and projects
*/
final class FailingScopeInitializationLog extends ScopeInitialization {

override def onOrganizationCreation(
organization: Organization,
subject: Identity.Subject
): IO[Unit] =
IO.raiseError(ScopeInitializationFailed("failed at org creation"))
override def onProjectCreation(
project: Project,
subject: Identity.Subject
): IO[Unit] =
IO.raiseError(ScopeInitializationFailed("failed at project creation"))

override def entityType: EntityType = EntityType("failingScopeInitializationLog")
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package ch.epfl.bluebrain.nexus.delta.sdk
import cats.effect.IO
import ch.epfl.bluebrain.nexus.delta.sdk.organizations.model.Organization
import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.Project
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{Identity, Label, ProjectRef}
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{EntityType, Identity, Label, ProjectRef}
import cats.effect.Ref

/**
Expand All @@ -24,6 +24,8 @@ final class ScopeInitializationLog private (
subject: Identity.Subject
): IO[Unit] =
createdProjects.update(_ + project.ref)

override def entityType: EntityType = EntityType("scopeInitializationLog")
}

object ScopeInitializationLog {
Expand Down
Loading

0 comments on commit e868da8

Please sign in to comment.