net.nemerosa.ontrack.boot.graphql.GQLLinksContributorImpl.kt Maven / Gradle / Ivy
package net.nemerosa.ontrack.boot.graphql
import graphql.Scalars.GraphQLString
import graphql.schema.DataFetcher
import graphql.schema.GraphQLFieldDefinition
import graphql.schema.GraphQLFieldDefinition.newFieldDefinition
import graphql.schema.GraphQLObjectType.newObject
import net.nemerosa.ontrack.graphql.schema.GQLFieldContributor
import net.nemerosa.ontrack.model.security.SecurityService
import net.nemerosa.ontrack.ui.controller.URIBuilder
import net.nemerosa.ontrack.ui.resource.DefaultResourceContext
import net.nemerosa.ontrack.ui.resource.ResourceContext
import net.nemerosa.ontrack.ui.resource.ResourceDecorator
import net.nemerosa.ontrack.ui.resource.ResourceDecoratorDelegate
import org.springframework.stereotype.Component
import java.util.concurrent.ConcurrentHashMap
@Component
class GQLLinksContributorImpl(
private val uriBuilder: URIBuilder,
private val securityService: SecurityService,
private val decorators: List>
) : GQLFieldContributor {
override fun getFields(type: Class<*>): List {
val resourceContext = DefaultResourceContext(uriBuilder, securityService)
val definitions = mutableListOf()
// Links
val typeDecorators = decorators
.filter { decorator -> decorator.appliesFor(type) }
val linkNames = typeDecorators
.flatMap { decorator -> decorator.linkNames }
.distinct()
if (linkNames.isNotEmpty()) {
definitions.add(
newFieldDefinition()
.name("links")
.description("Links")
.deprecate("Use the `actions` field instead.")
.type(
newObject()
.name(type.simpleName + "Links")
.description(type.simpleName + " links")
.fields(
linkNames
.map { linkName ->
newFieldDefinition()
.name(linkName)
.type(GraphQLString)
.dataFetcher(linkDataFetcher(linkName))
.build()
}
)
.build()
)
.dataFetcher { env -> LinksCache(resourceContext, env.getSource(), typeDecorators) }
.build()
)
}
// OK
return definitions
}
private fun linkDataFetcher(linkName: String) =
DataFetcher { environment ->
val linksCache = environment.getSource()
linksCache.getLink(linkName)
}
private class LinksCache(
private val resourceContext: ResourceContext,
private val source: Any,
private val typeDecorators: List>
) {
private val cache = ConcurrentHashMap()
fun getLink(linkName: String): String? =
cache.getOrPut(linkName) {
computeLink(linkName)
}.link
private fun computeLink(linkName: String): CachedLink {
val link = typeDecorators.mapNotNull { decorator ->
computeLink(decorator, linkName)
}.firstOrNull()
return CachedLink(link)
}
private fun computeLink(decorator: ResourceDecorator, linkName: String): String? =
if (linkName in decorator.linkNames) {
@Suppress("UNCHECKED_CAST")
val t: T = if (source is ResourceDecoratorDelegate) {
source.getLinkDelegate()
} else {
source
} as T
val link = decorator.linkByName(t, resourceContext, linkName)
link?.href?.toString()
} else {
null
}
}
private class CachedLink(val link: String?)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy