Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add endpoint to show source of resolved resources #4465

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.{contexts, schemas}
import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.{ContextValue, RemoteContextResolution}
import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.encoder.JsonLdEncoder
import ch.epfl.bluebrain.nexus.delta.rdf.utils.JsonKeyOrdering
import ch.epfl.bluebrain.nexus.delta.routes.ResolutionType.{AllResolversInProject, SingleResolver}
import ch.epfl.bluebrain.nexus.delta.sdk._
import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck
import ch.epfl.bluebrain.nexus.delta.sdk.ce.DeltaDirectives._
Expand Down Expand Up @@ -87,20 +88,21 @@ final class ResolversRoutes(
(baseUriPrefix(baseUri.prefix) & replaceUri("resolvers", schemas.resolvers)) {
pathPrefix("resolvers") {
extractCaller { implicit caller =>
(resolveProjectRef & indexingMode) { (ref, mode) =>
def index(resolver: ResolverResource): IO[Unit] = indexAction(resolver.value.project, resolver, mode)
val authorizeRead = authorizeFor(ref, Read)
val authorizeWrite = authorizeFor(ref, Write)
(resolveProjectRef & indexingMode) { (project, indexingMode) =>
def index(resolver: ResolverResource): IO[Unit] =
indexAction(resolver.value.project, resolver, indexingMode)
val authorizeRead = authorizeFor(project, Read)
val authorizeWrite = authorizeFor(project, Write)
concat(
pathEndOrSingleSlash {
// Create a resolver without an id segment
(post & noParameter("rev") & entity(as[Json])) { payload =>
authorizeWrite {
emitMetadata(Created, resolvers.create(ref, payload).flatTap(index))
emitMetadata(Created, resolvers.create(project, payload).flatTap(index))
}
}
},
idSegment { id =>
idSegment { resolver =>
concat(
pathEndOrSingleSlash {
concat(
Expand All @@ -109,55 +111,72 @@ final class ResolversRoutes(
(parameter("rev".as[Int].?) & pathEndOrSingleSlash & entity(as[Json])) {
case (None, payload) =>
// Create a resolver with an id segment
emitMetadata(Created, resolvers.create(id, ref, payload).flatTap(index))
emitMetadata(Created, resolvers.create(resolver, project, payload).flatTap(index))
case (Some(rev), payload) =>
// Update a resolver
emitMetadata(resolvers.update(id, ref, rev, payload).flatTap(index))
emitMetadata(resolvers.update(resolver, project, rev, payload).flatTap(index))
}
}
},
(delete & parameter("rev".as[Int])) { rev =>
authorizeWrite {
// Deprecate a resolver
emitMetadataOrReject(resolvers.deprecate(id, ref, rev).flatTap(index))
emitMetadataOrReject(resolvers.deprecate(resolver, project, rev).flatTap(index))
}
},
// Fetches a resolver
(get & idSegmentRef(id)) { id =>
(get & idSegmentRef(resolver)) { resolverRef =>
emitOrFusionRedirect(
ref,
id,
project,
resolverRef,
authorizeRead {
emitFetch(resolvers.fetch(id, ref))
emitFetch(resolvers.fetch(resolverRef, project))
}
)
}
)
},
// Fetches a resolver original source
(pathPrefix("source") & get & pathEndOrSingleSlash & idSegmentRef(id) & authorizeRead) { id =>
emitSource(resolvers.fetch(id, ref))
(pathPrefix("source") & get & pathEndOrSingleSlash & idSegmentRef(resolver) & authorizeRead) {
resolverRef =>
emitSource(resolvers.fetch(resolverRef, project))
},
// Tags
(pathPrefix("tags") & pathEndOrSingleSlash) {
concat(
// Fetch a resolver tags
(get & idSegmentRef(id) & authorizeRead) { id =>
emitTags(resolvers.fetch(id, ref))
(get & idSegmentRef(resolver) & authorizeRead) { resolverRef =>
emitTags(resolvers.fetch(resolverRef, project))
},
// Tag a resolver
(post & parameter("rev".as[Int])) { rev =>
authorizeWrite {
entity(as[Tag]) { case Tag(tagRev, tag) =>
emitMetadata(Created, resolvers.tag(id, ref, tag, tagRev, rev).flatTap(index))
emitMetadata(Created, resolvers.tag(resolver, project, tag, tagRev, rev).flatTap(index))
}
}
}
)
},
// Fetch a resource using a resolver
(idSegmentRef & pathEndOrSingleSlash) { resourceIdRef =>
resolve(resourceIdRef, ref, underscoreToOption(id))
(get & idSegmentRef) { resourceIdRef =>
concat(
pathEndOrSingleSlash {
parameter("showReport".as[Boolean].withDefault(default = false)) { showReport =>
val outputType =
if (showReport) ResolvedResourceOutputType.Report else ResolvedResourceOutputType.JsonLd
resolveResource(resourceIdRef, project, resolutionType(resolver), outputType)
}
},
(pathPrefix("source") & pathEndOrSingleSlash) {
resolveResource(
resourceIdRef,
project,
resolutionType(resolver),
ResolvedResourceOutputType.Source
)
}
)
}
)
}
Expand All @@ -167,23 +186,48 @@ final class ResolversRoutes(
}
}

private def resolve(resourceSegment: IdSegmentRef, projectRef: ProjectRef, resolverId: Option[IdSegment])(implicit
private def resolveResource(
resource: IdSegmentRef,
project: ProjectRef,
resolutionType: ResolutionType,
output: ResolvedResourceOutputType
)(implicit
caller: Caller
): Route =
authorizeFor(projectRef, Permissions.resources.read).apply {
parameter("showReport".as[Boolean].withDefault(default = false)) { showReport =>
def emitResult[R: JsonLdEncoder](io: IO[MultiResolutionResult[R]]) =
if (showReport)
emit(io.map(_.report).attemptNarrow[ResolverRejection])
else
emit(io.map(_.value.jsonLdValue).attemptNarrow[ResolverRejection])

resolverId.fold(emitResult(multiResolution(resourceSegment, projectRef))) { resolverId =>
emitResult(multiResolution(resourceSegment, projectRef, resolverId))
authorizeFor(project, Permissions.resources.read).apply {
def emitResult[R: JsonLdEncoder](io: IO[MultiResolutionResult[R]]) = {
output match {
case ResolvedResourceOutputType.Report => emit(io.map(_.report).attemptNarrow[ResolverRejection])
case ResolvedResourceOutputType.JsonLd => emit(io.map(_.value.jsonLdValue).attemptNarrow[ResolverRejection])
case ResolvedResourceOutputType.Source => emit(io.map(_.value.source).attemptNarrow[ResolverRejection])
}
}

resolutionType match {
case ResolutionType.AllResolversInProject => emitResult(multiResolution(resource, project))
case SingleResolver(resolver) => emitResult(multiResolution(resource, project, resolver))
}
}

private def resolutionType(segment: IdSegment): ResolutionType = {
underscoreToOption(segment) match {
case Some(resolver) => SingleResolver(resolver)
case None => AllResolversInProject
}
}
}

sealed trait ResolutionType
object ResolutionType {
case object AllResolversInProject extends ResolutionType
case class SingleResolver(id: IdSegment) extends ResolutionType
}

sealed trait ResolvedResourceOutputType
object ResolvedResourceOutputType {
case object Report extends ResolvedResourceOutputType
case object JsonLd extends ResolvedResourceOutputType
case object Source extends ResolvedResourceOutputType
}

object ResolversRoutes {
Expand Down
Loading
Loading