DslWorkspacePreResolutionInterceptor.java
package com.taxonomy.versioning.controller;
import com.taxonomy.versioning.service.RepositoryStateService;
import com.taxonomy.workspace.service.RepositoryContext;
import com.taxonomy.workspace.service.WorkspaceContext;
import com.taxonomy.workspace.service.WorkspaceResolver;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.Objects;
/**
* Resolves an isolated workspace and its exact repository before entering HTTP
* endpoints that read or mutate workspace-scoped repository, hypothesis,
* analysis or relation state.
*
* <p>Provisioning and context resolution failures propagate before controller
* code runs. Successful results are request-cached by the corresponding
* services, so repeated controller lookups cannot later switch repository or
* workspace. Despite the historical class name, the interceptor is also used
* by analysis and graph-search endpoints whose results carry workspace-scoped
* hypotheses or relation visibility.</p>
*/
@Component
public class DslWorkspacePreResolutionInterceptor implements HandlerInterceptor {
private final WorkspaceResolver workspaceResolver;
private final RepositoryStateService repositoryStateService;
public DslWorkspacePreResolutionInterceptor(WorkspaceResolver workspaceResolver,
RepositoryStateService repositoryStateService) {
this.workspaceResolver = workspaceResolver;
this.repositoryStateService = repositoryStateService;
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
String username = workspaceResolver.resolveCurrentUsername();
repositoryStateService.ensureWorkspaceState(username);
// Resolve the repository first. Besides request-caching the mandatory
// tenant identity, this persists the deterministic primary provenance
// for any pre-migration workspace that still lacks source_repository_id.
RepositoryContext repositoryContext =
workspaceResolver.resolveCurrentRepositoryContext();
WorkspaceContext workspaceContext = workspaceResolver.resolveCurrentContext();
if (repositoryContext == null) {
throw new IllegalStateException(
"Repository context resolver returned null for a workspace-scoped operation");
}
if (workspaceContext == null) {
throw new IllegalStateException(
"Workspace context resolver returned null for a workspace-scoped operation");
}
if (WorkspaceContext.SHARED.equals(workspaceContext)) {
throw new IllegalStateException(
"Authenticated workspace-scoped operation did not resolve an isolated workspace");
}
if (!Objects.equals(
repositoryContext.workspaceId(), workspaceContext.workspaceId())) {
throw new IllegalStateException(
"Repository and workspace resolution selected different workspace identities");
}
return true;
}
}