Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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 @@ -3,7 +3,7 @@ package controllers.projectApi
import config.WorkbenchConfig
import controllers.core.UserContextActions
import controllers.core.util.ControllerUtilsTrait
import controllers.projectApi.ProjectApi.{CreateTagsRequest, ProjectTagsResponse, ProjectUriResponse}
import controllers.projectApi.ProjectApi.{CreateTagsRequest, DetailedProjectPrefixesResponse, ProjectTagsResponse, ProjectUriResponse}
import controllers.projectApi.doc.ProjectApiDoc
import controllers.projectApi.requests.OriginalTaskDataResponse.OriginalTaskDataJsonFormat
import controllers.projectApi.requests.ReloadFailedTaskRequest
Expand Down Expand Up @@ -395,6 +395,42 @@ class ProjectApi @Inject()(accessMonitor: WorkbenchAccessMonitor) extends Inject
Ok(Json.toJson(project.config.prefixes.prefixMap))
}

/** Returns project and workspace prefixes separately. */
@Operation(
summary = "Detailed project prefixes",
description = "Project namespace prefix definitions split into project-owned, workspace-owned, and built-in default prefixes. If the same prefix exists in both project and workspace maps, the project prefix has precedence.",
responses = Array(
new ApiResponse(
responseCode = "200",
description = "Success",
content = Array(new Content(
mediaType = "application/json",
schema = new Schema(implementation = classOf[DetailedProjectPrefixesResponse]),
examples = Array(new ExampleObject("{ \"projectPrefixes\": { \"customPrefix\": \"http://customPrefix.cc/\" }, \"workspacePrefixes\": { \"rdf\": \"http://www.w3.org/1999/02/22-rdf-syntax-ns#\", \"foaf\": \"http://xmlns.com/foaf/0.1/\" }, \"defaultPrefixes\": { \"rdf\": \"http://www.w3.org/1999/02/22-rdf-syntax-ns#\", \"rdfs\": \"http://www.w3.org/2000/01/rdf-schema#\" } }"))
))
),
new ApiResponse(
responseCode = "404",
description = "If the project does not exist."
)
))
def fetchDetailedProjectPrefixes(@Parameter(
name = "projectId",
description = "The project identifier",
required = true,
in = ParameterIn.PATH,
schema = new Schema(implementation = classOf[String])
)
projectId: String): Action[AnyContent] = UserContextAction { implicit userContext =>
val project = getProject(projectId)
accessMonitor.saveProjectAccess(project.config.id) // Only accessed on the project details page
Ok(Json.toJson(DetailedProjectPrefixesResponse(
projectPrefixes = project.config.projectPrefixes.prefixMap,
workspacePrefixes = project.config.workspacePrefixes.prefixMap,
defaultPrefixes = Prefixes.default.prefixMap
)))
}

/** Add or update project prefix. */
@Operation(
summary = "Add project prefix",
Expand Down Expand Up @@ -840,6 +876,15 @@ class ProjectApi @Inject()(accessMonitor: WorkbenchAccessMonitor) extends Inject
}

object ProjectApi {
@Schema(description = "Project namespace prefix definitions split by ownership.")
case class DetailedProjectPrefixesResponse(
@Schema(description = "Prefixes owned by the project. These take precedence over equally named workspace prefixes.")
projectPrefixes: Map[String, String],
@Schema(description = "Prefixes provided by the workspace. These are available in the project but not owned by it.")
workspacePrefixes: Map[String, String],
@Schema(description = "Built-in default prefixes that are always available in DI/Silk.")
defaultPrefixes: Map[String, String]
)

@Schema(description = "Lists all user-defined tags.")
case class ProjectTagsResponse(@ArraySchema(schema = new Schema(implementation = classOf[FullTag]))
Expand Down Expand Up @@ -889,7 +934,8 @@ object ProjectApi {
}
}

implicit val detailedProjectPrefixesResponseFormat: Format[DetailedProjectPrefixesResponse] = Json.format[DetailedProjectPrefixesResponse]
implicit val projectTagsResponseFormat: Format[ProjectTagsResponse] = Json.format[ProjectTagsResponse]
implicit val createTagFormat: Format[CreateTag] = Json.format[CreateTag]
implicit val createTagsRequestFormat: Format[CreateTagsRequest] = Json.format[CreateTagsRequest]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ PUT /:projectId/accessControl controlle

# Prefixes
GET /:projectId/prefixes controllers.projectApi.ProjectApi.fetchProjectPrefixes(projectId: String)
GET /:projectId/prefixes/detailed controllers.projectApi.ProjectApi.fetchDetailedProjectPrefixes(projectId: String)
PUT /:projectId/prefixes/:prefixName controllers.projectApi.ProjectApi.addProjectPrefix(projectId: String, prefixName: String)
DELETE /:projectId/prefixes/:prefixName controllers.projectApi.ProjectApi.deleteProjectPrefix(projectId: String, prefixName: String)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package controllers.workspaceApi

import controllers.projectApi.ProjectApi.{CreateTag, CreateTagsRequest}
import controllers.projectApi.ProjectApi.{CreateTag, CreateTagsRequest, DetailedProjectPrefixesResponse}
import controllers.util.ProjectApiClient
import controllers.workspaceApi.project.ProjectApiRestPayloads.{ItemMetaData, CreateProjectRequest}
import helper.IntegrationTestTrait

import org.silkframework.config.MetaData
import org.silkframework.config.{MetaData, Prefixes}
import org.silkframework.runtime.serialization.{ReadContext, TestReadContext}
import org.silkframework.serialization.json.JsonSerializers
import org.silkframework.serialization.json.JsonSerializers._
Expand All @@ -28,6 +28,7 @@ class ProjectApiTest extends AnyFlatSpec with IntegrationTestTrait with Matchers

lazy val projectsUrl: String = controllers.projectApi.routes.ProjectApi.createNewProject().url
def projectPrefixesUrl(projectId: String): String = controllers.projectApi.routes.ProjectApi.fetchProjectPrefixes(projectId).url
def detailedProjectPrefixesUrl(projectId: String): String = controllers.projectApi.routes.ProjectApi.fetchDetailedProjectPrefixes(projectId).url
def projectPrefixUrl(projectId: String, prefixName: String): String = controllers.projectApi.routes.ProjectApi.addProjectPrefix(projectId, prefixName).url
private def projectsMetaDataUrl(projectId: String): String = controllers.projectApi.routes.ProjectApi.updateProjectMetaData(projectId).url
implicit val readContext: ReadContext = TestReadContext()
Expand Down Expand Up @@ -76,6 +77,21 @@ class ProjectApiTest extends AnyFlatSpec with IntegrationTestTrait with Matchers
prefixes.get.size must be > 0
}

it should "fetch the detailed project prefixes" in {
val project = retrieveOrCreateProject(prefixProjectId)
val overlappingPrefix = project.config.workspacePrefixes.prefixMap.keys.head
val overridingProjectUri = "http://project.example/override/"
project.config = project.config.copy(
projectPrefixes = Prefixes(project.config.projectPrefixes.prefixMap ++ Map(overlappingPrefix -> overridingProjectUri))
)

val detailedPrefixes = fetchDetailedPrefixes
detailedPrefixes.isSuccess mustBe true
detailedPrefixes.get.projectPrefixes(overlappingPrefix) mustBe overridingProjectUri
detailedPrefixes.get.workspacePrefixes(overlappingPrefix) mustBe project.config.workspacePrefixes(overlappingPrefix)
detailedPrefixes.get.defaultPrefixes mustBe Prefixes.default.prefixMap
}

it should "Update project prefixes" in {
retrieveOrCreateProject(prefixProjectId)

Expand Down Expand Up @@ -149,6 +165,11 @@ class ProjectApiTest extends AnyFlatSpec with IntegrationTestTrait with Matchers
Json.fromJson[Map[String, String]](responseJson)
}

private def fetchDetailedPrefixes: JsResult[DetailedProjectPrefixesResponse] = {
val responseJson = checkResponse(client.url(s"$baseUrl${detailedProjectPrefixesUrl(prefixProjectId)}").get()).json
Json.fromJson[DetailedProjectPrefixesResponse](responseJson)
}

private def createProjectByLabel(label: String, description: Option[String] = None, id: Option[String] = None): WSResponse = {
val responseFuture = client.url(s"$baseUrl$projectsUrl").post(Json.toJson(CreateProjectRequest(ItemMetaData(label, description), id)))
val response = checkResponse(responseFuture)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,4 @@
import { IPrefixDefinition, IWidgetsState } from "@ducks/workspace/typings";

export function initialNewPrefixState(): IPrefixDefinition {
return {
prefixName: "",
prefixUri: "",
};
}

export function initialConfigurationState() {
return {
prefixes: [],
newPrefix: initialNewPrefixState(),
isLoading: false,
error: {},
};
}
import { IWidgetsState } from "@ducks/workspace/typings";

export function initialWarningItemState() {
return {
Expand Down Expand Up @@ -47,7 +31,6 @@ export function initialFilesState() {

export function initialWidgetsState(): IWidgetsState {
return {
configuration: initialConfigurationState(),
warnings: initialWarningState(),
files: initialFilesState(),
};
Expand Down
6 changes: 0 additions & 6 deletions workspace/src/app/store/ducks/workspace/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { routerOp } from "@ducks/router";
import { IFacetState } from "@ducks/workspace/typings";
import { workspaceSel } from "@ducks/workspace";
import qs, { ParsedQs } from "qs";
import { fetchProjectPrefixesAsync } from "@ducks/workspace/widgets/configuration.thunk";
import { widgetsSlice } from "@ducks/workspace/widgetsSlice";
import { fetchWarningListAsync, fetchWarningMarkdownAsync } from "@ducks/workspace/widgets/warning.thunk";
import { fetchResourcesListAsync } from "@ducks/workspace/widgets/file.thunk";
import { commonSel } from "@ducks/common";
Expand All @@ -31,8 +29,6 @@ const {

const { setLoading, setError, fetchList, fetchListSuccess } = previewSlice.actions;

const { updateNewPrefix } = widgetsSlice.actions;

const ARRAY_DELIMITER = "|";
const VALUE_DELIMITER = ",";

Expand Down Expand Up @@ -289,12 +285,10 @@ const workspaceOps = {
changeLimitOp,
toggleFacetOp,
setupFiltersFromQs,
fetchProjectPrefixesAsync,
fetchWarningListAsync,
fetchWarningMarkdownAsync,
fetchResourcesListAsync,
resetFilters,
updateNewPrefix,
applyFilters,
changeProjectsLimit,
};
Expand Down
24 changes: 11 additions & 13 deletions workspace/src/app/store/ducks/workspace/requests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
IAppliedFacetState,
IDetailedProjectPrefixes,
IFacetState,
IProjectExecutionStatus,
IProjectImportDetails,
Expand Down Expand Up @@ -224,25 +225,22 @@ export const requestCreateProject = async (payload: ICreateProjectPayload): Prom
}
};

//missing-type
export const requestProjectPrefixesLegacy = async (projectId: string): Promise<any | never> => {
try {
const { data } = await fetch({
url: workspaceApi(`/projects/${projectId}/prefixes`),
});
return data;
} catch (e) {
throw handleError(e);
}
};

/** Fetch project prefixes. */
/** Fetch effective project prefixes. */
export const requestProjectPrefixes = async (projectId: string): Promise<FetchResponse<Record<string, string>>> => {
return fetch({
url: workspaceApi(`/projects/${projectId}/prefixes`),
});
};

/** Fetch project and workspace prefixes separately. */
export const requestDetailedProjectPrefixes = async (
projectId: string,
): Promise<FetchResponse<IDetailedProjectPrefixes>> => {
return fetch({
url: workspaceApi(`/projects/${projectId}/prefixes/detailed`),
});
};

//missing-type
export const requestChangePrefixes = async (
prefixName: string,
Expand Down
9 changes: 0 additions & 9 deletions workspace/src/app/store/ducks/workspace/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ const appliedFacetsSelector = createSelector([filtersSelector], (filters) => fil

const paginationSelector = createSelector([filtersSelector], (filters) => filters.pagination);

const prefixListSelector = createSelector([widgetsSelector], (widgets) => widgets.configuration.prefixes);

const widgetErrorSelector = createSelector([widgetsSelector], (widgets) => widgets.configuration.error);

const warningListSelector = createSelector([widgetsSelector], (widgets) => widgets.warnings.results);

const filesListSelector = createSelector([widgetsSelector, commonSelector], (widgets, common) =>
Expand All @@ -40,8 +36,6 @@ const filesListSelector = createSelector([widgetsSelector, commonSelector], (wid
})),
);

const newPrefixSelector = createSelector([widgetsSelector], (widgets) => widgets.configuration.newPrefix);

const isEmptyPageSelector = createSelector(
[isLoadingSelector, resultsSelector, commonSelector],
(isLoading, results, commonStore) => !isLoading && !results.length && commonStore.initialSettings.emptyWorkspace,
Expand All @@ -56,14 +50,11 @@ const workspaceSelectors = {
facetsSelector,
errorSelector,
isLoadingSelector,
prefixListSelector,
newPrefixSelector,
warningListSelector,
filesListSelector,
isEmptyPageSelector,
widgetsSelector,
commonSelector,
widgetErrorSelector,
};

export default workspaceSelectors;
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,10 @@ export interface IPrefixDefinition {
prefixUri: string;
}

export interface IWorkspaceConfigurationWidget {
/**
* Array of prefixes List
*/
prefixes: IPrefixDefinition[];
/**
* Plain object for new prefix
*/
newPrefix: IPrefixDefinition;

isLoading: boolean;

error: any;
export interface IDetailedProjectPrefixes {
projectPrefixes: Record<string, string>;
workspacePrefixes: Record<string, string>;
defaultPrefixes: Record<string, string>;
}

export interface IWarningWidgetItem {
Expand Down Expand Up @@ -55,11 +46,6 @@ export interface IFilesWidget {
}

export interface IWidgetsState {
/**
* Store Project details page all widgets by widget name
*/
configuration: IWorkspaceConfigurationWidget;

warnings: IWarningWidget;

files: IFilesWidget;
Expand Down

This file was deleted.

Loading
Loading