diff --git a/Makefile b/Makefile index 4ac51b224..e45c6ba32 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,11 @@ PROJECT_PATH = $(shell pwd) ENV_FILE := .env +# include .env files if they exist +-include ${ENV_FILE} + NAME := mapping_workbench +DOCKER_PROJECT := ${NAME}_${ENVIRONMENT} BACKEND_INFRA_FOLDER := ${PROJECT_PATH}/${NAME}/backend FRONTEND_HOME := ${NAME}/frontend FRONTEND_INFRA_FOLDER := ${PROJECT_PATH}/${FRONTEND_HOME} @@ -10,10 +14,6 @@ FRONTEND_INFRA_FOLDER := ${PROJECT_PATH}/${FRONTEND_HOME} RML_MAPPER_PATH = ${PROJECT_PATH}/.rmlmapper/rmlmapper.jar -# include .env files if they exist --include ${ENV_FILE} - - #----------------------------------------------------------------------------- # INSTALLING #---------------------------------------------------------------------- @@ -107,37 +107,37 @@ prod-dotenv-file: build-backend: @ echo "Building the BACKEND" - @ docker-compose -p ${NAME} --file ./infra/backend/docker-compose.yml --env-file ${ENV_FILE} build --progress plain --no-cache --force-rm - @ docker-compose -p ${NAME} --file ./infra/backend/docker-compose.yml --env-file ${ENV_FILE} up -d --force-recreate + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/backend/docker-compose.yml --env-file ${ENV_FILE} build --progress plain --no-cache --force-rm + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/backend/docker-compose.yml --env-file ${ENV_FILE} up -d --force-recreate start-backend: @ echo "Starting the BACKEND" - @ docker-compose -p ${NAME} --file ./infra/backend/docker-compose.yml --env-file ${ENV_FILE} up -d + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/backend/docker-compose.yml --env-file ${ENV_FILE} up -d stop-backend: @ echo "Stopping the BACKEND" - @ docker-compose -p ${NAME} --file ./infra/backend/docker-compose.yml --env-file ${ENV_FILE} down + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/backend/docker-compose.yml --env-file ${ENV_FILE} down build-frontend: @ echo "Building the FRONTEND" - @ docker-compose -p ${NAME} --file ./infra/frontend/docker-compose.yml --env-file ${ENV_FILE} build --progress plain --no-cache --force-rm - @ docker-compose -p ${NAME} --file ./infra/frontend/docker-compose.yml --env-file ${ENV_FILE} up -d --force-recreate + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/frontend/docker-compose.yml --env-file ${ENV_FILE} build --progress plain --no-cache --force-rm + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/frontend/docker-compose.yml --env-file ${ENV_FILE} up -d --force-recreate start-frontend: @ echo "Starting the FRONTEND" - @ docker-compose -p ${NAME} --file ./infra/frontend/docker-compose.yml --env-file ${ENV_FILE} up -d + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/frontend/docker-compose.yml --env-file ${ENV_FILE} up -d stop-frontend: @ echo "Stopping the FRONTEND" - @ docker-compose -p ${NAME} --file ./infra/frontend/docker-compose.yml --env-file ${ENV_FILE} down + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/frontend/docker-compose.yml --env-file ${ENV_FILE} down start-mongo: build-externals @ echo "Starting the Mongo services" - @ docker-compose -p ${NAME} --file ./infra/mongodb/docker-compose.yml --env-file ${ENV_FILE} up -d + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/mongodb/docker-compose.yml --env-file ${ENV_FILE} up -d stop-mongo: @ echo "Stopping the Mongo services" - @ docker-compose -p ${NAME} --file ./infra/mongodb/docker-compose.yml --env-file ${ENV_FILE} down + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/mongodb/docker-compose.yml --env-file ${ENV_FILE} down #----------------------------------------------------------------------------- @@ -146,29 +146,29 @@ stop-mongo: build-backend-dev: @ echo "Building the BACKEND" - @ docker-compose -p ${NAME} --file ./infra/backend/docker-compose.dev.yml --env-file ${ENV_FILE} build --progress plain --no-cache --force-rm - @ docker-compose -p ${NAME} --file ./infra/backend/docker-compose.dev.yml --env-file ${ENV_FILE} up -d --force-recreate + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/backend/docker-compose.dev.yml --env-file ${ENV_FILE} build --progress plain --no-cache --force-rm + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/backend/docker-compose.dev.yml --env-file ${ENV_FILE} up -d --force-recreate start-backend-dev: @ echo "Starting the BACKEND" - @ docker-compose -p ${NAME} --file ./infra/backend/docker-compose.dev.yml --env-file ${ENV_FILE} up -d + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/backend/docker-compose.dev.yml --env-file ${ENV_FILE} up -d stop-backend-dev: @ echo "Stopping the BACKEND" - @ docker-compose -p ${NAME} --file ./infra/backend/docker-compose.dev.yml --env-file ${ENV_FILE} down + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/backend/docker-compose.dev.yml --env-file ${ENV_FILE} down build-frontend-dev: @ echo "Building the FRONTEND" - @ docker-compose -p ${NAME} --file ./infra/frontend/docker-compose.dev.yml --env-file ${ENV_FILE} build --progress plain --no-cache --force-rm - @ docker-compose -p ${NAME} --file ./infra/frontend/docker-compose.dev.yml --env-file ${ENV_FILE} up -d --force-recreate + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/frontend/docker-compose.dev.yml --env-file ${ENV_FILE} build --progress plain --no-cache --force-rm + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/frontend/docker-compose.dev.yml --env-file ${ENV_FILE} up -d --force-recreate start-frontend-dev: @ echo "Starting the FRONTEND" - @ docker-compose -p ${NAME} --file ./infra/frontend/docker-compose.dev.yml --env-file ${ENV_FILE} up -d + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/frontend/docker-compose.dev.yml --env-file ${ENV_FILE} up -d stop-frontend-dev: @ echo "Stopping the FRONTEND" - @ docker-compose -p ${NAME} --file ./infra/frontend/docker-compose.dev.yml --env-file ${ENV_FILE} down + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/frontend/docker-compose.dev.yml --env-file ${ENV_FILE} down clear-frontend: @ cd ${FRONTEND_HOME} && rm -rf build && rm -rf node_modules && rm -f .env* && rm -f package-lock.json @@ -185,11 +185,11 @@ start-mongo-console-mode: start-mongo-dev: build-externals @ echo "Starting the Mongo services" - @ docker-compose -p ${NAME} --file ./infra/mongodb/docker-compose.dev.yml --env-file ${ENV_FILE} up -d + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/mongodb/docker-compose.dev.yml --env-file ${ENV_FILE} up -d stop-mongo-dev: @ echo "Stopping the Mongo services" - @ docker-compose -p ${NAME} --file ./infra/mongodb/docker-compose.dev.yml --env-file ${ENV_FILE} down + @ docker-compose -p ${DOCKER_PROJECT} --file ./infra/mongodb/docker-compose.dev.yml --env-file ${ENV_FILE} down #----------------------------------------------------------------------------- # SERVER SERVICES diff --git a/mapping_workbench/backend/core/entrypoints/api/main.py b/mapping_workbench/backend/core/entrypoints/api/main.py index ad0eaa67e..a6e071be0 100755 --- a/mapping_workbench/backend/core/entrypoints/api/main.py +++ b/mapping_workbench/backend/core/entrypoints/api/main.py @@ -2,6 +2,7 @@ from fastapi.middleware.cors import CORSMiddleware from httpx_oauth.clients.google import GoogleOAuth2 +from mapping_workbench.backend.fields_registry.entrypoints.api import routes as fields_registry from mapping_workbench.backend.conceptual_mapping_rule.entrypoints.api import routes as conceptual_mapping_rule_routes from mapping_workbench.backend.config import settings from mapping_workbench.backend.config.entrypoints.api import routes as config_routes @@ -77,7 +78,8 @@ async def on_startup(): generic_triple_map_fragment_routes.router, config_routes.router, ontology_routes.router, - tasks_routes.router + tasks_routes.router, + fields_registry.router ] for secured_router in secured_routers: diff --git a/mapping_workbench/backend/mapping_package/models/entity.py b/mapping_workbench/backend/mapping_package/models/entity.py index 09ccb78c7..a0612a826 100644 --- a/mapping_workbench/backend/mapping_package/models/entity.py +++ b/mapping_workbench/backend/mapping_package/models/entity.py @@ -13,7 +13,7 @@ BaseProjectResourceEntityInSchema, BaseProjectResourceEntityOutSchema from mapping_workbench.backend.shacl_test_suite.models.entity import SHACLTestSuite from mapping_workbench.backend.state_manager.models.state_object import ObjectState, StatefulObjectABC -from mapping_workbench.backend.test_data_suite.models.entity import TestDataSuite, TestDataSuiteState +#from mapping_workbench.backend.test_data_suite.models.entity import TestDataSuite, TestDataSuiteState class MappingPackageException(Exception): @@ -30,7 +30,7 @@ class MappingPackageIn(BaseProjectResourceEntityInSchema): end_date: Optional[datetime] = None min_xsd_version: Optional[str] = None max_xsd_version: Optional[str] = None - test_data_suites: Optional[List[Optional[Link[TestDataSuite]]]] = None + #test_data_suites: Optional[List[Optional[Link[TestDataSuite]]]] = None shacl_test_suites: Optional[List[Optional[Link[SHACLTestSuite]]]] = None @@ -56,7 +56,7 @@ class MappingPackageOut(BaseProjectResourceEntityOutSchema): end_date: Optional[datetime] = None min_xsd_version: Optional[str] = None max_xsd_version: Optional[str] = None - test_data_suites: Optional[List[Link[TestDataSuite]]] = None + #test_data_suites: Optional[List[Link[TestDataSuite]]] = None shacl_test_suites: Optional[List[Link[SHACLTestSuite]]] = None @@ -74,7 +74,7 @@ class MappingPackageState(ObjectState): end_date: Optional[datetime] = None min_xsd_version: Optional[str] = None max_xsd_version: Optional[str] = None - test_data_suites: List[TestDataSuiteState] = [] + #test_data_suites: List[TestDataSuiteState] = [] shacl_test_suites: List[SHACLTestSuite] = [] conceptual_mapping_rule_states: List[ConceptualMappingRuleState] = [] @@ -89,7 +89,7 @@ class MappingPackage(BaseProjectResourceEntity, StatefulObjectABC): end_date: Optional[datetime] = None min_xsd_version: Optional[str] = None max_xsd_version: Optional[str] = None - test_data_suites: Optional[List[Link[TestDataSuite]]] = None + #test_data_suites: Optional[List[Link[TestDataSuite]]] = None shacl_test_suites: Optional[List[Link[SHACLTestSuite]]] = None async def get_conceptual_mapping_rules(self) -> List[ConceptualMappingRuleState]: diff --git a/mapping_workbench/backend/ontology_file_collection/entrypoints/api/routes.py b/mapping_workbench/backend/ontology_file_collection/entrypoints/api/routes.py index 51576c775..5d20dc27c 100644 --- a/mapping_workbench/backend/ontology_file_collection/entrypoints/api/routes.py +++ b/mapping_workbench/backend/ontology_file_collection/entrypoints/api/routes.py @@ -1,5 +1,3 @@ -from typing import List - from beanie import PydanticObjectId from fastapi import APIRouter, status, Depends from starlette.requests import Request diff --git a/mapping_workbench/backend/resource_collection/entrypoints/api/routes.py b/mapping_workbench/backend/resource_collection/entrypoints/api/routes.py index 4378aed04..39f5d89ee 100644 --- a/mapping_workbench/backend/resource_collection/entrypoints/api/routes.py +++ b/mapping_workbench/backend/resource_collection/entrypoints/api/routes.py @@ -47,13 +47,22 @@ response_model=APIListResourceCollectionsPaginatedResponse ) async def route_list_resource_collections( - project: PydanticObjectId = None + project: PydanticObjectId = None, + page: int = None, + limit: int = None, + q: str = None ): filters: dict = {} if project: filters['project'] = Project.link_from_id(project) - items: List[ResourceCollection] = await list_resource_collections(filters) - return APIListResourceCollectionsPaginatedResponse(items=items, count=len(items)) + if q is not None: + filters['q'] = q + + items, total_count = await list_resource_collections(filters, page, limit) + return APIListResourceCollectionsPaginatedResponse( + items=items, + count=total_count + ) @router.post( @@ -112,10 +121,24 @@ async def route_delete_resource_collection(resource_collection: ResourceCollecti response_model=APIListResourceFilesPaginatedResponse ) async def route_list_resource_collection_file_resources( - id: PydanticObjectId = None + resource_collection: ResourceCollection = Depends(get_resource_collection), + project: PydanticObjectId = None, + page: int = None, + limit: int = None, + q: str = None ): - items: List[ResourceFile] = await list_resource_collection_file_resources(id) - return APIListResourceFilesPaginatedResponse(items=items, count=len(items)) + filters: dict = {} + if project: + filters['project'] = Project.link_from_id(project) + if q is not None: + filters['q'] = q + + items, total_count = \ + await list_resource_collection_file_resources(resource_collection, filters, page, limit) + return APIListResourceFilesPaginatedResponse( + items=items, + count=total_count + ) @router.post( diff --git a/mapping_workbench/backend/resource_collection/services/api.py b/mapping_workbench/backend/resource_collection/services/api.py index 39fa01cdf..fd4d96afa 100644 --- a/mapping_workbench/backend/resource_collection/services/api.py +++ b/mapping_workbench/backend/resource_collection/services/api.py @@ -1,25 +1,34 @@ from typing import List from beanie import PydanticObjectId -from bson import DBRef from mapping_workbench.backend.core.models.base_entity import BaseEntityFiltersSchema from mapping_workbench.backend.core.services.exceptions import ResourceNotFoundException from mapping_workbench.backend.core.services.request import request_update_data, api_entity_is_found, \ - request_create_data + request_create_data, prepare_search_param, pagination_params from mapping_workbench.backend.resource_collection.models.entity import ResourceCollection, ResourceFile, \ ResourceFileUpdateIn, ResourceFileCreateIn from mapping_workbench.backend.user.models.user import User -async def list_resource_collections(filters=None) -> List[ResourceCollection]: +async def list_resource_collections(filters: dict = None, page: int = None, limit: int = None) -> \ + (List[ResourceCollection], int): query_filters: dict = dict(filters or {}) | dict(BaseEntityFiltersSchema()) - return await ResourceCollection.find( + + prepare_search_param(query_filters) + skip, limit = pagination_params(page, limit) + + items: List[ResourceCollection] = await ResourceCollection.find( query_filters, projection_model=ResourceCollection, - fetch_links=False + fetch_links=False, + skip=skip, + limit=limit ).to_list() + total_count: int = await ResourceCollection.find(query_filters).count() + return items, total_count + async def create_resource_collection(resource_collection: ResourceCollection, user: User) -> ResourceCollection: resource_collection.on_create(user=user) @@ -48,15 +57,23 @@ async def delete_resource_collection(resource_collection: ResourceCollection): async def list_resource_collection_file_resources( - id: PydanticObjectId = None, - filters=None -) -> List[ResourceFile]: + resource_collection: ResourceCollection, + filters=None, page: int = None, limit: int = None +): query_filters: dict = dict(filters or {}) | dict(BaseEntityFiltersSchema()) - return await ResourceFile.find( - ResourceFile.resource_collection == ResourceCollection.link_from_id(id), + query_filters['resource_collection'] = ResourceCollection.link_from_id(resource_collection.id) + + prepare_search_param(query_filters) + skip, limit = pagination_params(page, limit) + + items: List[ResourceFile] = await ResourceFile.find( query_filters, - fetch_links=False + fetch_links=False, + skip=skip, + limit=limit ).to_list() + total_count: int = await ResourceFile.find(query_filters).count() + return items, total_count async def create_resource_collection_file_resource( diff --git a/mapping_workbench/backend/shacl_test_suite/entrypoints/api/routes.py b/mapping_workbench/backend/shacl_test_suite/entrypoints/api/routes.py index fd4871f2f..ddbe562ad 100644 --- a/mapping_workbench/backend/shacl_test_suite/entrypoints/api/routes.py +++ b/mapping_workbench/backend/shacl_test_suite/entrypoints/api/routes.py @@ -48,15 +48,24 @@ ) async def route_list_shacl_test_suites( project: PydanticObjectId = None, - ids: Annotated[List[PydanticObjectId | str] | None, Query()] = None + ids: Annotated[List[PydanticObjectId | str] | None, Query()] = None, + page: int = None, + limit: int = None, + q: str = None ): filters: dict = {} if project: filters['project'] = Project.link_from_id(project) if ids is not None: filters['_id'] = {"$in": ids} - items: List[SHACLTestSuite] = await list_shacl_test_suites(filters) - return APIListSHACLTestSuitesPaginatedResponse(items=items, count=len(items)) + if q is not None: + filters['q'] = q + + items, total_count = await list_shacl_test_suites(filters, page, limit) + return APIListSHACLTestSuitesPaginatedResponse( + items=items, + count=total_count + ) @router.post( @@ -115,10 +124,25 @@ async def route_delete_shacl_test_suite(shacl_test_suite: SHACLTestSuite = Depen response_model=APIListSHACLTestFileResourcesPaginatedResponse ) async def route_list_shacl_test_suite_file_resources( - id: PydanticObjectId = None + shacl_test_suite: SHACLTestSuite = Depends(get_shacl_test_suite), + project: PydanticObjectId = None, + page: int = None, + limit: int = None, + q: str = None ): - items: List[SHACLTestFileResource] = await list_shacl_test_suite_file_resources(id) - return APIListSHACLTestFileResourcesPaginatedResponse(items=items, count=len(items)) + + filters: dict = {} + if project: + filters['project'] = Project.link_from_id(project) + if q is not None: + filters['q'] = q + + items, total_count = \ + await list_shacl_test_suite_file_resources(shacl_test_suite, filters, page, limit) + return APIListSHACLTestFileResourcesPaginatedResponse( + items=items, + count=total_count + ) @router.post( diff --git a/mapping_workbench/backend/shacl_test_suite/services/api.py b/mapping_workbench/backend/shacl_test_suite/services/api.py index 66dbbb30e..b237b91e9 100644 --- a/mapping_workbench/backend/shacl_test_suite/services/api.py +++ b/mapping_workbench/backend/shacl_test_suite/services/api.py @@ -5,15 +5,29 @@ from mapping_workbench.backend.core.models.base_entity import BaseEntityFiltersSchema from mapping_workbench.backend.core.services.exceptions import ResourceNotFoundException from mapping_workbench.backend.core.services.request import request_update_data, api_entity_is_found, \ - request_create_data -from mapping_workbench.backend.shacl_test_suite.models.entity import SHACLTestSuite, SHACLTestFileResource, \ - SHACLTestFileResourceCreateIn, SHACLTestFileResourceUpdateIn + request_create_data, prepare_search_param, pagination_params +from mapping_workbench.backend.shacl_test_suite.models.entity import SHACLTestSuite, SHACLTestFileResource +from mapping_workbench.backend.shacl_test_suite.models.entity_api_response import SHACLTestFileResourceCreateIn, \ + SHACLTestFileResourceUpdateIn from mapping_workbench.backend.user.models.user import User -async def list_shacl_test_suites(filters=None) -> List[SHACLTestSuite]: +async def list_shacl_test_suites(filters: dict = None, page: int = None, limit: int = None) -> \ + (List[SHACLTestSuite], int): query_filters: dict = dict(filters or {}) | dict(BaseEntityFiltersSchema()) - return await SHACLTestSuite.find(query_filters, projection_model=SHACLTestSuite, fetch_links=False).to_list() + + prepare_search_param(query_filters) + skip, limit = pagination_params(page, limit) + + items: List[SHACLTestSuite] = await SHACLTestSuite.find( + query_filters, + projection_model=SHACLTestSuite, + fetch_links=False, + skip=skip, + limit=limit + ).to_list() + total_count: int = await SHACLTestSuite.find(query_filters).count() + return items, total_count async def create_shacl_test_suite(shacl_test_suite: SHACLTestSuite, user: User) -> SHACLTestSuite: @@ -43,15 +57,23 @@ async def delete_shacl_test_suite(shacl_test_suite: SHACLTestSuite): async def list_shacl_test_suite_file_resources( - id: PydanticObjectId = None, - filters = None -) -> List[SHACLTestFileResource]: + shacl_test_suite: SHACLTestSuite, + filters=None, page: int = None, limit: int = None +): query_filters: dict = dict(filters or {}) | dict(BaseEntityFiltersSchema()) - return await SHACLTestFileResource.find( - SHACLTestFileResource.shacl_test_suite == SHACLTestSuite.link_from_id(id), + query_filters['shacl_test_suite'] = SHACLTestSuite.link_from_id(shacl_test_suite.id) + + prepare_search_param(query_filters) + skip, limit = pagination_params(page, limit) + + items: List[SHACLTestFileResource] = await SHACLTestFileResource.find( query_filters, - fetch_links=False + fetch_links=False, + skip=skip, + limit=limit ).to_list() + total_count: int = await SHACLTestFileResource.find(query_filters).count() + return items, total_count async def create_shacl_test_suite_file_resource( diff --git a/mapping_workbench/backend/sparql_test_suite/entrypoints/api/routes.py b/mapping_workbench/backend/sparql_test_suite/entrypoints/api/routes.py index c5adc228d..b60be636f 100644 --- a/mapping_workbench/backend/sparql_test_suite/entrypoints/api/routes.py +++ b/mapping_workbench/backend/sparql_test_suite/entrypoints/api/routes.py @@ -48,15 +48,24 @@ ) async def route_list_sparql_test_suites( project: PydanticObjectId = None, - ids: Annotated[List[PydanticObjectId | str] | None, Query()] = None + ids: Annotated[List[PydanticObjectId | str] | None, Query()] = None, + page: int = None, + limit: int = None, + q: str = None ): filters: dict = {} if project: filters['project'] = Project.link_from_id(project) if ids is not None: filters['_id'] = {"$in": ids} - items: List[SPARQLTestSuite] = await list_sparql_test_suites(filters) - return APIListSPARQLTestSuitesPaginatedResponse(items=items, count=len(items)) + if q is not None: + filters['q'] = q + + items, total_count = await list_sparql_test_suites(filters, page, limit) + return APIListSPARQLTestSuitesPaginatedResponse( + items=items, + count=total_count + ) @router.post( @@ -131,10 +140,25 @@ async def route_list_sparql_test_file_resources( response_model=APIListSPARQLTestFileResourcesPaginatedResponse ) async def route_list_sparql_test_suite_file_resources( - id: PydanticObjectId = None + sparql_test_suite: SPARQLTestSuite = Depends(get_sparql_test_suite), + project: PydanticObjectId = None, + page: int = None, + limit: int = None, + q: str = None ): - items: List[SPARQLTestFileResource] = await list_sparql_test_suite_file_resources(id) - return APIListSPARQLTestFileResourcesPaginatedResponse(items=items, count=len(items)) + + filters: dict = {} + if project: + filters['project'] = Project.link_from_id(project) + if q is not None: + filters['q'] = q + + items, total_count = \ + await list_sparql_test_suite_file_resources(sparql_test_suite, filters, page, limit) + return APIListSPARQLTestFileResourcesPaginatedResponse( + items=items, + count=total_count + ) @router.post( diff --git a/mapping_workbench/backend/sparql_test_suite/services/api.py b/mapping_workbench/backend/sparql_test_suite/services/api.py index ac50890d8..c3fc6d3de 100644 --- a/mapping_workbench/backend/sparql_test_suite/services/api.py +++ b/mapping_workbench/backend/sparql_test_suite/services/api.py @@ -5,16 +5,29 @@ from mapping_workbench.backend.core.models.base_entity import BaseEntityFiltersSchema from mapping_workbench.backend.core.services.exceptions import ResourceNotFoundException from mapping_workbench.backend.core.services.request import request_update_data, api_entity_is_found, \ - request_create_data + request_create_data, prepare_search_param, pagination_params from mapping_workbench.backend.project.models.entity import Project from mapping_workbench.backend.sparql_test_suite.models.entity import SPARQLTestSuite, SPARQLTestFileResource, \ SPARQLTestFileResourceUpdateIn, SPARQLTestFileResourceCreateIn from mapping_workbench.backend.user.models.user import User -async def list_sparql_test_suites(filters=None) -> List[SPARQLTestSuite]: +async def list_sparql_test_suites(filters: dict = None, page: int = None, limit: int = None) -> \ + (List[SPARQLTestSuite], int): query_filters: dict = dict(filters or {}) | dict(BaseEntityFiltersSchema()) - return await SPARQLTestSuite.find(query_filters, projection_model=SPARQLTestSuite, fetch_links=False).to_list() + + prepare_search_param(query_filters) + skip, limit = pagination_params(page, limit) + + items: List[SPARQLTestSuite] = await SPARQLTestSuite.find( + query_filters, + projection_model=SPARQLTestSuite, + fetch_links=False, + skip=skip, + limit=limit + ).to_list() + total_count: int = await SPARQLTestSuite.find(query_filters).count() + return items, total_count async def create_sparql_test_suite(sparql_test_suite: SPARQLTestSuite, user: User) -> SPARQLTestSuite: @@ -54,15 +67,23 @@ async def list_sparql_test_file_resources( async def list_sparql_test_suite_file_resources( - id: PydanticObjectId = None, - filters=None -) -> List[SPARQLTestFileResource]: + sparql_test_suite: SPARQLTestSuite, + filters=None, page: int = None, limit: int = None +) -> (List[SPARQLTestFileResource], int): query_filters: dict = dict(filters or {}) | dict(BaseEntityFiltersSchema()) - return await SPARQLTestFileResource.find( - SPARQLTestFileResource.sparql_test_suite == SPARQLTestSuite.link_from_id(id), + query_filters['sparql_test_suite'] = SPARQLTestSuite.link_from_id(sparql_test_suite.id) + + prepare_search_param(query_filters) + skip, limit = pagination_params(page, limit) + + items: List[SPARQLTestFileResource] = await SPARQLTestFileResource.find( query_filters, - fetch_links=False + fetch_links=False, + skip=skip, + limit=limit ).to_list() + total_count: int = await SPARQLTestFileResource.find(query_filters).count() + return items, total_count async def create_sparql_test_suite_file_resource( diff --git a/mapping_workbench/backend/sparql_test_suite/services/sparql_validator.py b/mapping_workbench/backend/sparql_test_suite/services/sparql_validator.py index ca22db7cc..9ddbdb304 100644 --- a/mapping_workbench/backend/sparql_test_suite/services/sparql_validator.py +++ b/mapping_workbench/backend/sparql_test_suite/services/sparql_validator.py @@ -1,14 +1,30 @@ from typing import List -from beanie import WriteRules +from beanie import WriteRules, PydanticObjectId from mapping_workbench.backend.sparql_test_suite.adapters.validator import SPARQLValidator from mapping_workbench.backend.sparql_test_suite.models.entity import SPARQLTestSuite, SPARQLTestFileResource from mapping_workbench.backend.test_data_suite.models.entity import TestDataFileResource, TestDataException +from mapping_workbench.backend.user.models.user import User -async def validate_test_data_with_sparql_test_suite(test_data: TestDataFileResource, - sparql_test_suite: SPARQLTestSuite) -> TestDataFileResource: +async def test_data_sparql_validation_for_project( + project_id: PydanticObjectId, + user: User = None +): + """ + + :param test_data: + :param sparql_test_suite: + :return: + """ + pass + + +async def validate_test_data_with_sparql_test_suite( + test_data: TestDataFileResource, + sparql_test_suite: SPARQLTestSuite +) -> TestDataFileResource: """ """ @@ -24,8 +40,10 @@ async def validate_test_data_with_sparql_test_suite(test_data: TestDataFileResou return test_data -async def validate_tests_data_with_shacl_tests(tests_data: List[TestDataFileResource], - sparql_tests: List[SPARQLTestFileResource]) -> List[TestDataFileResource]: +async def validate_tests_data_with_shacl_tests( + tests_data: List[TestDataFileResource], + sparql_tests: List[SPARQLTestFileResource] +) -> List[TestDataFileResource]: """ """ @@ -36,4 +54,4 @@ async def validate_tests_data_with_shacl_tests(tests_data: List[TestDataFileReso raise TestDataException("Test data must have a rdf manifestation") await validate_test_data_with_sparql_test_suite(test_data=test_data, sparql_test_suite=sparql_test_suite) - return tests_data \ No newline at end of file + return tests_data diff --git a/mapping_workbench/backend/test_data_suite/entrypoints/api/routes.py b/mapping_workbench/backend/test_data_suite/entrypoints/api/routes.py index 465a1dc25..afac07488 100644 --- a/mapping_workbench/backend/test_data_suite/entrypoints/api/routes.py +++ b/mapping_workbench/backend/test_data_suite/entrypoints/api/routes.py @@ -10,6 +10,8 @@ file_resource_data_from_form_request from mapping_workbench.backend.project.models.entity import Project from mapping_workbench.backend.security.services.user_manager import current_active_user +from mapping_workbench.backend.sparql_test_suite.services.sparql_validator import \ + test_data_sparql_validation_for_project from mapping_workbench.backend.test_data_suite.models.entity import TestDataSuite, TestDataFileResource, \ TestDataFileResourceUpdateIn, TestDataFileResourceCreateIn from mapping_workbench.backend.test_data_suite.models.entity_api_response import \ @@ -25,8 +27,7 @@ get_test_data_file_resource, delete_test_data_file_resource, update_test_data_file_resource ) -from mapping_workbench.backend.test_data_suite.services.transform_test_data import transform_test_data_file_resource, \ - transform_test_data_for_project +from mapping_workbench.backend.test_data_suite.services.transform_test_data import transform_test_data_for_project from mapping_workbench.backend.user.models.user import User ROUTE_PREFIX = "/test_data_suites" @@ -50,15 +51,24 @@ ) async def route_list_test_data_suites( project: PydanticObjectId = None, - ids: Annotated[List[PydanticObjectId | str] | None, Query()] = None + ids: Annotated[List[PydanticObjectId | str] | None, Query()] = None, + page: int = None, + limit: int = None, + q: str = None ): filters: dict = {} if project: filters['project'] = Project.link_from_id(project) if ids is not None: filters['_id'] = {"$in": ids} - items: List[TestDataSuite] = await list_test_data_suites(filters) - return APIListTestDataSuitesPaginatedResponse(items=items, count=len(items)) + if q is not None: + filters['q'] = q + + items, total_count = await list_test_data_suites(filters, page, limit) + return APIListTestDataSuitesPaginatedResponse( + items=items, + count=total_count + ) @router.post( @@ -117,10 +127,24 @@ async def route_delete_test_data_suite(test_data_suite: TestDataSuite = Depends( response_model=APIListTestDataFileResourcesPaginatedResponse ) async def route_list_test_data_suite_file_resources( - id: PydanticObjectId = None + test_data_suite: TestDataSuite = Depends(get_test_data_suite), + project: PydanticObjectId = None, + page: int = None, + limit: int = None, + q: str = None ): - items: List[TestDataFileResource] = await list_test_data_suite_file_resources(id) - return APIListTestDataFileResourcesPaginatedResponse(items=items, count=len(items)) + filters: dict = {} + if project: + filters['project'] = Project.link_from_id(project) + if q is not None: + filters['q'] = q + + items, total_count = \ + await list_test_data_suite_file_resources(test_data_suite, filters, page, limit) + return APIListTestDataFileResourcesPaginatedResponse( + items=items, + count=total_count + ) @router.post( @@ -201,3 +225,15 @@ async def route_transform_test_data( user: User = Depends(current_active_user) ): return await transform_test_data_for_project(project_id=filters.project, user=user) + + +@router.post( + "/tasks/sparql_validation", + description=f"Test Data SPARQL Validation", + name=f"transform_test_data" +) +async def route_test_data_sparql_validation( + filters: APIRequestWithProject, + user: User = Depends(current_active_user) +): + return await test_data_sparql_validation_for_project(project_id=filters.project, user=user) diff --git a/mapping_workbench/backend/test_data_suite/models/entity.py b/mapping_workbench/backend/test_data_suite/models/entity.py index 4455d5599..29a61d304 100644 --- a/mapping_workbench/backend/test_data_suite/models/entity.py +++ b/mapping_workbench/backend/test_data_suite/models/entity.py @@ -2,12 +2,13 @@ from typing import Optional, List import pymongo -from beanie import Link +from beanie import Link, PydanticObjectId from pymongo import IndexModel from mapping_workbench.backend.core.models.base_project_resource_entity import BaseProjectResourceEntity from mapping_workbench.backend.file_resource.models.file_resource import FileResource, FileResourceCollection, \ FileResourceIn +from mapping_workbench.backend.mapping_package.models.entity import MappingPackage from mapping_workbench.backend.shacl_test_suite.models.validator import SHACLTestDataValidationResult from mapping_workbench.backend.sparql_test_suite.models.validator import SPARQLTestDataValidationResult from mapping_workbench.backend.state_manager.models.state_object import StatefulObjectABC, ObjectState @@ -100,8 +101,12 @@ class TestDataSuiteState(ObjectState): test_data_states: List[TestDataState] -class TestDataSuite(FileResourceCollection, StatefulObjectABC): +class TestDataSuite( + FileResourceCollection, + StatefulObjectABC +): file_resources: Optional[List[Link[TestDataFileResource]]] = [] + mapping_package_id: Optional[PydanticObjectId] = None async def get_state(self) -> TestDataSuiteState: title = self.title diff --git a/mapping_workbench/backend/test_data_suite/services/api.py b/mapping_workbench/backend/test_data_suite/services/api.py index 534cdbe45..e73be4dee 100644 --- a/mapping_workbench/backend/test_data_suite/services/api.py +++ b/mapping_workbench/backend/test_data_suite/services/api.py @@ -5,16 +5,29 @@ from mapping_workbench.backend.core.models.base_entity import BaseEntityFiltersSchema from mapping_workbench.backend.core.services.exceptions import ResourceNotFoundException from mapping_workbench.backend.core.services.request import request_update_data, api_entity_is_found, \ - request_create_data + request_create_data, prepare_search_param, pagination_params from mapping_workbench.backend.test_data_suite.models.entity import TestDataSuite, TestDataFileResource, \ TestDataFileResourceUpdateIn, TestDataFileResourceCreateIn from mapping_workbench.backend.test_data_suite.services.transform_test_data import transform_test_data_file_resource from mapping_workbench.backend.user.models.user import User -async def list_test_data_suites(filters=None) -> List[TestDataSuite]: +async def list_test_data_suites(filters: dict = None, page: int = None, limit: int = None) -> \ + (List[TestDataSuite], int): query_filters: dict = dict(filters or {}) | dict(BaseEntityFiltersSchema()) - return await TestDataSuite.find(query_filters, projection_model=TestDataSuite, fetch_links=False).to_list() + + prepare_search_param(query_filters) + skip, limit = pagination_params(page, limit) + + items: List[TestDataSuite] = await TestDataSuite.find( + query_filters, + projection_model=TestDataSuite, + fetch_links=False, + skip=skip, + limit=limit + ).to_list() + total_count: int = await TestDataSuite.find(query_filters).count() + return items, total_count async def create_test_data_suite(test_data_suite: TestDataSuite, user: User) -> TestDataSuite: @@ -44,15 +57,24 @@ async def delete_test_data_suite(test_data_suite: TestDataSuite): async def list_test_data_suite_file_resources( - id: PydanticObjectId = None, - filters=None -) -> List[TestDataFileResource]: + test_data_suite: TestDataSuite, + filters=None, page: int = None, limit: int = None +) -> (List[TestDataFileResource], int): query_filters: dict = dict(filters or {}) | dict(BaseEntityFiltersSchema()) - return await TestDataFileResource.find( - TestDataFileResource.test_data_suite == TestDataSuite.link_from_id(id), + query_filters['test_data_suite'] = TestDataSuite.link_from_id(test_data_suite.id) + + prepare_search_param(query_filters) + skip, limit = pagination_params(page, limit) + print("K :: ", test_data_suite.id, query_filters) + + items: List[TestDataFileResource] = await TestDataFileResource.find( query_filters, - fetch_links=False + fetch_links=False, + skip=skip, + limit=limit ).to_list() + total_count: int = await TestDataFileResource.find(query_filters).count() + return items, total_count async def create_test_data_suite_file_resource( diff --git a/mapping_workbench/frontend/Makefile b/mapping_workbench/frontend/Makefile index 326bbd9c3..ebaed6f41 100644 --- a/mapping_workbench/frontend/Makefile +++ b/mapping_workbench/frontend/Makefile @@ -9,7 +9,8 @@ start-staging-frontend: start-prod-frontend: @ echo "Starting PROD FRONTEND" - @ serve -s build + @ npm run dev +# @ serve -s build install-dev-frontend: @ echo "Installing DEV FRONTEND" @@ -22,4 +23,5 @@ install-staging-frontend: install-prod-frontend: @ echo "Installing PROD FRONTEND" - @ npm install && npm install -g serve && npm run build && rm -fr node_modules + @ npm install +# @ npm install && npm install -g serve && npm run build && rm -fr node_modules diff --git a/mapping_workbench/frontend/package.json b/mapping_workbench/frontend/package.json index bcd893a9f..33a658aaf 100644 --- a/mapping_workbench/frontend/package.json +++ b/mapping_workbench/frontend/package.json @@ -44,6 +44,7 @@ "aws-amplify": "5.2.1", "axios": "^1.4.0", "date-fns": "2.30.0", + "dotenv": "^16.3.1", "draft-js": "0.11.7", "firebase": "9.22.0", "formik": "2.2.9", diff --git a/mapping_workbench/frontend/src/api/app/index.js b/mapping_workbench/frontend/src/api/app/index.js index 798fc1ef2..4aaec873b 100644 --- a/mapping_workbench/frontend/src/api/app/index.js +++ b/mapping_workbench/frontend/src/api/app/index.js @@ -20,7 +20,6 @@ const METHOD = { }; class AppApi { - constructor() { this.config = apiConfig; this.apiClient = this.getApiClient(this.config); diff --git a/mapping_workbench/frontend/src/api/fields-registry/index.js b/mapping_workbench/frontend/src/api/fields-registry/index.js new file mode 100644 index 000000000..3d57afc9a --- /dev/null +++ b/mapping_workbench/frontend/src/api/fields-registry/index.js @@ -0,0 +1,17 @@ +import {SectionApi} from "../section"; + +class FieldsRegistryApi extends SectionApi { + get SECTION_TITLE() { + return "Fields Registry"; + } + + get SECTION_ITEM_TITLE() { + return "Fields Registry"; + } + + constructor() { + super("fields_registry"); + } +} + +export const fieldsRegistryApi = new FieldsRegistryApi(); diff --git a/mapping_workbench/frontend/src/api/test-data-suites/index.js b/mapping_workbench/frontend/src/api/test-data-suites/index.js index 91b8d0d2f..d47aa9998 100644 --- a/mapping_workbench/frontend/src/api/test-data-suites/index.js +++ b/mapping_workbench/frontend/src/api/test-data-suites/index.js @@ -14,6 +14,7 @@ class TestDataSuitesApi extends FileCollectionsApi { constructor() { super("test_data_suites"); this.isProjectResource = true; + this.hasMappingPackage = true; } async getValuesForSelector(request = {}) { diff --git a/mapping_workbench/frontend/src/components/app/list/list-file-collection-actions.js b/mapping_workbench/frontend/src/components/app/list/list-file-collection-actions.js index 1f49f11b7..27bd5b392 100644 --- a/mapping_workbench/frontend/src/components/app/list/list-file-collection-actions.js +++ b/mapping_workbench/frontend/src/components/app/list/list-file-collection-actions.js @@ -48,7 +48,10 @@ export const ListFileCollectionActions = (props) => { return ( <> -
+
{/* { path: paths.app.specific_triple_map_fragments.index } ] + }, + { + title: t(tokens.nav.fields_registry), + path: paths.app.fields_registry.index, + icon: ( + + + + ), + items: [ + { + title: t(tokens.nav.list), + path: paths.app.fields_registry.index + }, + { + title: t(tokens.nav.create), + path: paths.app.fields_registry.create + } + ] }); } items.resources.push(sections); diff --git a/mapping_workbench/frontend/src/locales/tokens.js b/mapping_workbench/frontend/src/locales/tokens.js index a90814c30..24a51d997 100644 --- a/mapping_workbench/frontend/src/locales/tokens.js +++ b/mapping_workbench/frontend/src/locales/tokens.js @@ -69,6 +69,7 @@ export const tokens = { triple_map_fragments: 'nav.triple_map_fragments', generic_triple_map_fragments: 'nav.generic_triple_map_fragments', specific_triple_map_fragments: 'nav.specific_triple_map_fragments', + fields_registry: 'nav.fields_registry', admin: 'nav.admin', users: 'nav.users', diff --git a/mapping_workbench/frontend/src/locales/translations/en.js b/mapping_workbench/frontend/src/locales/translations/en.js index 32996a2a5..b5ff7cdee 100644 --- a/mapping_workbench/frontend/src/locales/translations/en.js +++ b/mapping_workbench/frontend/src/locales/translations/en.js @@ -69,6 +69,7 @@ export const en = { [tokens.nav.triple_map_fragments]: 'Triple Maps', [tokens.nav.specific_triple_map_fragments]: 'Specific Triple Maps', [tokens.nav.generic_triple_map_fragments]: 'Generic Triple Maps', + [tokens.nav.fields_registry]: 'Fields Registry', [tokens.nav.terms_validator]: 'Terms Validator', [tokens.nav.tasks]: 'Tasks', [tokens.nav.generate_cm_assertions_queries]: 'Generate CM Assertions Queries', diff --git a/mapping_workbench/frontend/src/pages/app/fields-registry/[id]/edit.js b/mapping_workbench/frontend/src/pages/app/fields-registry/[id]/edit.js new file mode 100644 index 000000000..daa2e1f0b --- /dev/null +++ b/mapping_workbench/frontend/src/pages/app/fields-registry/[id]/edit.js @@ -0,0 +1,107 @@ +import ArrowLeftIcon from '@untitled-ui/icons-react/build/esm/ArrowLeft'; +import Chip from '@mui/material/Chip'; +import Link from '@mui/material/Link'; +import Stack from '@mui/material/Stack'; +import SvgIcon from '@mui/material/SvgIcon'; +import Typography from '@mui/material/Typography'; + +import {fieldsRegistryApi as sectionApi} from 'src/api/fields-registry'; +import {RouterLink} from 'src/components/router-link'; +import {Seo} from 'src/components/seo'; +import {usePageView} from 'src/hooks/use-page-view'; +import {Layout as AppLayout} from 'src/layouts/app'; +import {paths} from 'src/paths'; +import {EditForm} from 'src/sections/app/fields-registry/edit-form'; +import {ForItemEditForm} from "src/contexts/app/section/for-item-form"; +import {useItem} from "src/contexts/app/section/for-item-data-state"; +import {useRouter} from "src/hooks/use-router"; + + +const Page = () => { + const router = useRouter(); + if (!router.isReady) return; + + const {id} = router.query; + + if (!id) { + return; + } + + const formState = useItem(sectionApi, id); + const item = formState.item; + + usePageView(); + + if (!item) { + return; + } + + return ( + <> + + + +
+ + + + + + {sectionApi.SECTION_TITLE} + + +
+ + + + + {item.prefix} + + + + + + + +
+ +
+ + ); +}; + +Page.getLayout = (page) => ( + + {page} + +); + +export default Page; diff --git a/mapping_workbench/frontend/src/pages/app/fields-registry/[id]/view.js b/mapping_workbench/frontend/src/pages/app/fields-registry/[id]/view.js new file mode 100644 index 000000000..e4586f6ba --- /dev/null +++ b/mapping_workbench/frontend/src/pages/app/fields-registry/[id]/view.js @@ -0,0 +1,160 @@ +import {useCallback, useState} from 'react'; +import ArrowLeftIcon from '@untitled-ui/icons-react/build/esm/ArrowLeft'; +import Box from '@mui/material/Box'; +import Chip from '@mui/material/Chip'; +import Container from '@mui/material/Container'; +import Divider from '@mui/material/Divider'; +import Grid from '@mui/material/Unstable_Grid2'; +import Link from '@mui/material/Link'; +import Stack from '@mui/material/Stack'; +import SvgIcon from '@mui/material/SvgIcon'; +import Tab from '@mui/material/Tab'; +import Tabs from '@mui/material/Tabs'; +import Typography from '@mui/material/Typography'; + +import {fieldsRegistryApi as sectionApi} from 'src/api/fields-registry'; +import {RouterLink} from 'src/components/router-link'; +import {Seo} from 'src/components/seo'; +import {usePageView} from 'src/hooks/use-page-view'; +import {Layout as AppLayout} from 'src/layouts/app'; +import {paths} from 'src/paths'; +import {BasicDetails} from 'src/sections/app/fields-registry/basic-details'; +import {useRouter} from "src/hooks/use-router"; +import {useItem} from "src/contexts/app/section/for-item-data-state"; + +const tabs = [ + {label: 'Details', value: 'details'} +]; + +const Page = () => { + const router = useRouter(); + if (!router.isReady) return; + + const {id} = router.query; + + if (!id) { + return; + } + + const formState = useItem(sectionApi, id); + const item = formState.item; + + usePageView(); + const [currentTab, setCurrentTab] = useState('details'); + + const handleTabsChange = useCallback((event, value) => { + setCurrentTab(value); + }, []); + + if (!item) { + return; + } + + return ( + <> + + + +
+ + + + + + {sectionApi.SECTION_TITLE} + + +
+ + + + + {item.prefix} + + + + + + + +
+ + {tabs.map((tab) => ( + + ))} + + +
+
+ {currentTab === 'details' && ( +
+ + + + + +
+ )} +
+ + ) +}; + +Page.getLayout = (page) => ( + + {page} + +); + +export default Page; diff --git a/mapping_workbench/frontend/src/pages/app/fields-registry/create.js b/mapping_workbench/frontend/src/pages/app/fields-registry/create.js new file mode 100644 index 000000000..4612e6aa3 --- /dev/null +++ b/mapping_workbench/frontend/src/pages/app/fields-registry/create.js @@ -0,0 +1,61 @@ +import ArrowLeftIcon from '@untitled-ui/icons-react/build/esm/ArrowLeft'; +import Box from '@mui/material/Box'; +import Container from '@mui/material/Container'; +import Link from '@mui/material/Link'; +import Stack from '@mui/material/Stack'; +import SvgIcon from '@mui/material/SvgIcon'; +import Typography from '@mui/material/Typography'; + +import {fieldsRegistryApi as sectionApi} from 'src/api/fields-registry'; +import {RouterLink} from 'src/components/router-link'; +import {Seo} from 'src/components/seo'; +import {usePageView} from 'src/hooks/use-page-view'; +import {Layout as AppLayout} from 'src/layouts/app'; +import {paths} from 'src/paths'; +import {EditForm} from 'src/sections/app/fields-registry/edit-form'; +import {ForItemCreateForm} from "src/contexts/app/section/for-item-form"; + + +const Page = () => { + let item = {}; + + usePageView(); + + return ( + <> + + + +
+ + + + + + {sectionApi.SECTION_TITLE} + + +
+
+ +
+ + ); +}; + +Page.getLayout = (page) => ( + + {page} + +); + +export default Page; diff --git a/mapping_workbench/frontend/src/pages/app/fields-registry/index.js b/mapping_workbench/frontend/src/pages/app/fields-registry/index.js new file mode 100644 index 000000000..122d7a45a --- /dev/null +++ b/mapping_workbench/frontend/src/pages/app/fields-registry/index.js @@ -0,0 +1,182 @@ +import {useCallback, useEffect, useState} from 'react'; +import PlusIcon from '@untitled-ui/icons-react/build/esm/Plus'; +import Breadcrumbs from '@mui/material/Breadcrumbs'; +import Button from '@mui/material/Button'; +import Card from '@mui/material/Card'; +import Link from '@mui/material/Link'; +import Stack from '@mui/material/Stack'; +import SvgIcon from '@mui/material/SvgIcon'; +import Typography from '@mui/material/Typography'; + +import {fieldsRegistryApi as sectionApi} from 'src/api/fields-registry'; +import {BreadcrumbsSeparator} from 'src/components/breadcrumbs-separator'; +import {RouterLink} from 'src/components/router-link'; +import {Seo} from 'src/components/seo'; +import {useMounted} from 'src/hooks/use-mounted'; +import {usePageView} from 'src/hooks/use-page-view'; +import {Layout as AppLayout} from 'src/layouts/app'; +import {paths} from 'src/paths'; +import {ListSearch} from "../../../sections/app/fields-registry/list-search"; +import {ListTable} from "../../../sections/app/fields-registry/list-table"; + +const useItemsSearch = () => { + const [state, setState] = useState({ + filters: { + name: undefined, + category: [], + status: [], + inStock: undefined + }, + page: sectionApi.DEFAULT_PAGE, + rowsPerPage: sectionApi.DEFAULT_ROWS_PER_PAGE + }); + + const handleFiltersChange = useCallback((filters) => { + setState((prevState) => ({ + ...prevState, + filters, + page: 0 + })); + }, []); + + const handlePageChange = useCallback((event, page) => { + setState((prevState) => ({ + ...prevState, + page + })); + }, []); + + const handleRowsPerPageChange = useCallback((event) => { + setState((prevState) => ({ + ...prevState, + rowsPerPage: parseInt(event.target.value, 10) + })); + }, []); + + return { + handleFiltersChange, + handlePageChange, + handleRowsPerPageChange, + state + }; +}; + +const useItemsStore = (searchState) => { + const isMounted = useMounted(); + const [state, setState] = useState({ + items: [], + itemsCount: 0 + }); + + const handleItemsGet = useCallback(async () => { + try { + const response = await sectionApi.getItems(searchState); + if (isMounted()) { + setState({ + items: response.items, + itemsCount: response.count + }); + } + } catch (err) { + console.error(err); + } + }, [searchState, isMounted]); + + useEffect(() => { + handleItemsGet(); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [searchState]); + + return { + ...state + }; +}; + +const Page = () => { + const itemsSearch = useItemsSearch(); + const itemsStore = useItemsStore(itemsSearch.state); + + usePageView(); + + return ( + <> + + + + + + {sectionApi.SECTION_TITLE} + + }> + + App + + + {sectionApi.SECTION_TITLE} + + + List + + + + + + + + + + + + + + ) +}; + +Page.getLayout = (page) => ( + + {page} + +); + +export default Page; diff --git a/mapping_workbench/frontend/src/pages/app/ontology-file-collections/index.js b/mapping_workbench/frontend/src/pages/app/ontology-file-collections/index.js index ce2e0f6d3..23819c112 100644 --- a/mapping_workbench/frontend/src/pages/app/ontology-file-collections/index.js +++ b/mapping_workbench/frontend/src/pages/app/ontology-file-collections/index.js @@ -27,14 +27,15 @@ const useItemsSearch = () => { status: [], inStock: undefined }, - page: 0, - rowsPerPage: 5 + page: sectionApi.DEFAULT_PAGE, + rowsPerPage: sectionApi.DEFAULT_ROWS_PER_PAGE }); const handleFiltersChange = useCallback((filters) => { setState((prevState) => ({ ...prevState, - filters + filters, + page: 0 })); }, []); diff --git a/mapping_workbench/frontend/src/pages/app/resource-collections/index.js b/mapping_workbench/frontend/src/pages/app/resource-collections/index.js index 1b8eeb8f9..bbb06f0aa 100644 --- a/mapping_workbench/frontend/src/pages/app/resource-collections/index.js +++ b/mapping_workbench/frontend/src/pages/app/resource-collections/index.js @@ -1,10 +1,8 @@ import {useCallback, useEffect, useState} from 'react'; import PlusIcon from '@untitled-ui/icons-react/build/esm/Plus'; -import Box from '@mui/material/Box'; import Breadcrumbs from '@mui/material/Breadcrumbs'; import Button from '@mui/material/Button'; import Card from '@mui/material/Card'; -import Container from '@mui/material/Container'; import Link from '@mui/material/Link'; import Stack from '@mui/material/Stack'; import SvgIcon from '@mui/material/SvgIcon'; @@ -21,18 +19,6 @@ import {FileCollectionListSearch} from 'src/sections/app/file-manager/file-colle import {FileCollectionListTable} from 'src/sections/app/file-manager/file-collection-list-table'; -const mockDataPackages = { - title: "F03", - description: "Des03", - formType: "35", - minDate: "01/06/2023", - maxDate: "23/06/2023", - minVersion: "R2.08.55", - maxVersion: "R2.09.66", -}; - -const collectionsID = []; - const useItemsSearch = () => { const [state, setState] = useState({ filters: { @@ -41,14 +27,15 @@ const useItemsSearch = () => { status: [], inStock: undefined }, - page: 0, - rowsPerPage: 5 + page: sectionApi.DEFAULT_PAGE, + rowsPerPage: sectionApi.DEFAULT_ROWS_PER_PAGE }); const handleFiltersChange = useCallback((filters) => { setState((prevState) => ({ ...prevState, - filters + filters, + page: 0 })); }, []); @@ -80,10 +67,6 @@ const useItemsStore = (searchState) => { items: [], itemsCount: 0 }); - const [stateFile, setStateFile] = useState({ - items: [], - itemsCount: 0 - }); const handleItemsGet = useCallback(async () => { try { @@ -100,26 +83,6 @@ const useItemsStore = (searchState) => { } }, [searchState, isMounted]); - const handleItemsGetFiles = useCallback(async () => { - try { - const response2 = await sectionApi.getFileResources(id); - //const collection = await sectionApi.getItem(id); - - - //console.log("response2: ", response); - //console.log("collection: ", collection); - - - setStateFile({ - //collection: collection, - itemsF: response2.items, - itemsFCount: response2.count - }); - - } catch (err) { - console.error(err); - } - }, []); useEffect(() => { handleItemsGet().then(response => { diff --git a/mapping_workbench/frontend/src/pages/app/shacl-test-suites/index.js b/mapping_workbench/frontend/src/pages/app/shacl-test-suites/index.js index 3720edc38..f538bf18b 100644 --- a/mapping_workbench/frontend/src/pages/app/shacl-test-suites/index.js +++ b/mapping_workbench/frontend/src/pages/app/shacl-test-suites/index.js @@ -1,10 +1,8 @@ import {useCallback, useEffect, useState} from 'react'; import PlusIcon from '@untitled-ui/icons-react/build/esm/Plus'; -import Box from '@mui/material/Box'; import Breadcrumbs from '@mui/material/Breadcrumbs'; import Button from '@mui/material/Button'; import Card from '@mui/material/Card'; -import Container from '@mui/material/Container'; import Link from '@mui/material/Link'; import Stack from '@mui/material/Stack'; import SvgIcon from '@mui/material/SvgIcon'; @@ -29,14 +27,15 @@ const useItemsSearch = () => { status: [], inStock: undefined }, - page: 0, - rowsPerPage: 5 + page: sectionApi.DEFAULT_PAGE, + rowsPerPage: sectionApi.DEFAULT_ROWS_PER_PAGE }); const handleFiltersChange = useCallback((filters) => { setState((prevState) => ({ ...prevState, - filters + filters, + page: 0 })); }, []); diff --git a/mapping_workbench/frontend/src/pages/app/sparql-test-suites/index.js b/mapping_workbench/frontend/src/pages/app/sparql-test-suites/index.js index 69b44eab0..649a11bde 100644 --- a/mapping_workbench/frontend/src/pages/app/sparql-test-suites/index.js +++ b/mapping_workbench/frontend/src/pages/app/sparql-test-suites/index.js @@ -1,10 +1,8 @@ import {useCallback, useEffect, useState} from 'react'; import PlusIcon from '@untitled-ui/icons-react/build/esm/Plus'; -import Box from '@mui/material/Box'; import Breadcrumbs from '@mui/material/Breadcrumbs'; import Button from '@mui/material/Button'; import Card from '@mui/material/Card'; -import Container from '@mui/material/Container'; import Link from '@mui/material/Link'; import Stack from '@mui/material/Stack'; import SvgIcon from '@mui/material/SvgIcon'; @@ -29,14 +27,15 @@ const useItemsSearch = () => { status: [], inStock: undefined }, - page: 0, - rowsPerPage: 5 + page: sectionApi.DEFAULT_PAGE, + rowsPerPage: sectionApi.DEFAULT_ROWS_PER_PAGE }); const handleFiltersChange = useCallback((filters) => { setState((prevState) => ({ ...prevState, - filters + filters, + page: 0 })); }, []); diff --git a/mapping_workbench/frontend/src/pages/app/specific-triple-map-fragments/[id]/edit.js b/mapping_workbench/frontend/src/pages/app/specific-triple-map-fragments/[id]/edit.js index f42ca2bc2..7c4538eff 100644 --- a/mapping_workbench/frontend/src/pages/app/specific-triple-map-fragments/[id]/edit.js +++ b/mapping_workbench/frontend/src/pages/app/specific-triple-map-fragments/[id]/edit.js @@ -1,7 +1,5 @@ import ArrowLeftIcon from '@untitled-ui/icons-react/build/esm/ArrowLeft'; -import Box from '@mui/material/Box'; import Chip from '@mui/material/Chip'; -import Container from '@mui/material/Container'; import Link from '@mui/material/Link'; import Stack from '@mui/material/Stack'; import SvgIcon from '@mui/material/SvgIcon'; diff --git a/mapping_workbench/frontend/src/pages/app/test-data-suites/create.js b/mapping_workbench/frontend/src/pages/app/test-data-suites/create.js index b7084d58a..9ee39ad53 100644 --- a/mapping_workbench/frontend/src/pages/app/test-data-suites/create.js +++ b/mapping_workbench/frontend/src/pages/app/test-data-suites/create.js @@ -1,6 +1,4 @@ import ArrowLeftIcon from '@untitled-ui/icons-react/build/esm/ArrowLeft'; -import Box from '@mui/material/Box'; -import Container from '@mui/material/Container'; import Link from '@mui/material/Link'; import Stack from '@mui/material/Stack'; import SvgIcon from '@mui/material/SvgIcon'; diff --git a/mapping_workbench/frontend/src/pages/app/test-data-suites/index.js b/mapping_workbench/frontend/src/pages/app/test-data-suites/index.js index 8c85b9854..58ea4e56f 100644 --- a/mapping_workbench/frontend/src/pages/app/test-data-suites/index.js +++ b/mapping_workbench/frontend/src/pages/app/test-data-suites/index.js @@ -1,10 +1,8 @@ import {useCallback, useEffect, useState} from 'react'; import PlusIcon from '@untitled-ui/icons-react/build/esm/Plus'; -import Box from '@mui/material/Box'; import Breadcrumbs from '@mui/material/Breadcrumbs'; import Button from '@mui/material/Button'; import Card from '@mui/material/Card'; -import Container from '@mui/material/Container'; import Link from '@mui/material/Link'; import Stack from '@mui/material/Stack'; import SvgIcon from '@mui/material/SvgIcon'; @@ -33,14 +31,15 @@ const useItemsSearch = () => { status: [], inStock: undefined }, - page: 0, - rowsPerPage: 5 + page: sectionApi.DEFAULT_PAGE, + rowsPerPage: sectionApi.DEFAULT_ROWS_PER_PAGE }); const handleFiltersChange = useCallback((filters) => { setState((prevState) => ({ ...prevState, - filters + filters, + page: 0 })); }, []); diff --git a/mapping_workbench/frontend/src/paths.js b/mapping_workbench/frontend/src/paths.js index 218cd631a..ecd49acf4 100644 --- a/mapping_workbench/frontend/src/paths.js +++ b/mapping_workbench/frontend/src/paths.js @@ -139,6 +139,12 @@ export const paths = { edit: '/app/ontology-terms/[id]/edit', view: '/app/ontology-terms/[id]/view' }, + fields_registry: { + index: '/app/fields-registry', + create: '/app/fields-registry/create', + edit: '/app/fields-registry/[id]/edit', + view: '/app/fields-registry/[id]/view' + }, tasks: { index: '/app/tasks', terms_validator: '/app/tasks/terms_validator', @@ -244,6 +250,11 @@ export const apiPaths = { me: '/users/me' }, + fields_registry: { + items: '/fields_registry', + item: '/fields_registry/:id' + }, + tasks: { terms_validator: '/tasks/terms_validator', generate_cm_assertions_queries: '/tasks/generate_cm_assertions_queries', diff --git a/mapping_workbench/frontend/src/sections/app/conceptual-mapping-rule/edit-form.js b/mapping_workbench/frontend/src/sections/app/conceptual-mapping-rule/edit-form.js index 9d6bf57c9..4f8f6b91e 100644 --- a/mapping_workbench/frontend/src/sections/app/conceptual-mapping-rule/edit-form.js +++ b/mapping_workbench/frontend/src/sections/app/conceptual-mapping-rule/edit-form.js @@ -183,7 +183,7 @@ export const EditForm = (props) => { source_xpath: prepareTextareaListValue(item.source_xpath), target_class_path: item.target_class_path || '', target_property_path: item.target_property_path || '', - mapping_packages: (item.mapping_packages || []).map(x => x.id), + refers_to_mapping_package_ids: item.refers_to_mapping_package_ids || [], sparql_assertions: (item.sparql_assertions || []).map(x => x.id), triple_map_fragment: (item.triple_map_fragment && item.triple_map_fragment.id) || '', notes: (item.notes || []), @@ -477,7 +477,7 @@ export const EditForm = (props) => { - + @@ -554,7 +554,7 @@ export const EditForm = (props) => { )} value={COMMENT_PRIORITY.HIGH} - checked={formik.values.note.priority === COMMENT_PRIORITY.HIGH} + checked={formik.values.note && formik.values.note.priority === COMMENT_PRIORITY.HIGH} /> } @@ -569,7 +569,7 @@ export const EditForm = (props) => { )} value={COMMENT_PRIORITY.NORMAL} - checked={formik.values.note.priority === COMMENT_PRIORITY.NORMAL} + checked={formik.values.note && formik.values.note.priority === COMMENT_PRIORITY.NORMAL} /> } @@ -584,7 +584,7 @@ export const EditForm = (props) => { )} value={COMMENT_PRIORITY.LOW} - checked={formik.values.note.priority === COMMENT_PRIORITY.LOW} + checked={formik.values.note && formik.values.note.priority === COMMENT_PRIORITY.LOW} /> @@ -595,7 +595,7 @@ export const EditForm = (props) => { fullWidth label="Add new Note ..." helperText="... for external viewers" - value={formik.values.note.comment} + value={formik.values.note && formik.values.note.comment || ''} onBlur={formik.handleBlur} onChange={formik.handleChange} /> @@ -659,7 +659,7 @@ export const EditForm = (props) => { )} value={COMMENT_PRIORITY.HIGH} - checked={formik.values.comment.priority === COMMENT_PRIORITY.HIGH} + checked={formik.values.comment && formik.values.comment.priority === COMMENT_PRIORITY.HIGH} /> } @@ -674,7 +674,7 @@ export const EditForm = (props) => { )} value={COMMENT_PRIORITY.NORMAL} - checked={formik.values.comment.priority === COMMENT_PRIORITY.NORMAL} + checked={formik.values.comment && formik.values.comment.priority === COMMENT_PRIORITY.NORMAL} /> } @@ -689,7 +689,7 @@ export const EditForm = (props) => { )} value={COMMENT_PRIORITY.LOW} - checked={formik.values.comment.priority === COMMENT_PRIORITY.LOW} + checked={formik.values.comment && formik.values.comment.priority === COMMENT_PRIORITY.LOW} /> @@ -700,7 +700,7 @@ export const EditForm = (props) => { fullWidth label="Add new Comment ..." helperText="... for other editors" - value={formik.values.comment.comment} + value={formik.values.comment && formik.values.comment.comment || ''} onBlur={formik.handleBlur} onChange={formik.handleChange} /> diff --git a/mapping_workbench/frontend/src/sections/app/conceptual-mapping-rule/list-table.js b/mapping_workbench/frontend/src/sections/app/conceptual-mapping-rule/list-table.js index 510d71d61..226604bb3 100644 --- a/mapping_workbench/frontend/src/sections/app/conceptual-mapping-rule/list-table.js +++ b/mapping_workbench/frontend/src/sections/app/conceptual-mapping-rule/list-table.js @@ -301,7 +301,7 @@ export const ListTableMappingPackages = (props) => { isHovered } = props; - let ruleFilteredMappingPackages = item.mapping_packages.map(x => x.id); + let ruleFilteredMappingPackages = (item.mapping_packages || []).map(x => x.id); const [mappingPackages, setMappingPackages] = useState(ruleFilteredMappingPackages); const [projectMappingPackages, setProjectMappingPackages] = useState(initProjectMappingPackages || []); const [tempMappingPackages, setTempMappingPackages] = @@ -665,7 +665,7 @@ export const ListTableRow = (props) => { - {item.source_xpath.map( + {item.source_xpath && item.source_xpath.map( x => ( { + const {id, prefix, uri, is_syncable, ...other} = props; + + const router = useRouter(); + const itemctx = new ForListItemAction(id, sectionApi); + + const handleEditAction = useCallback(async () => { + router.push({ + //pathname: paths.app[item.api.section].edit, + pathname: paths.app.ontology_namespaces.edit, query: {id: id} + }); + + }, [router]); + + const handleDeleteAction = useCallback(async () => { + const response = await itemctx.api.deleteItem(id); + + router.push({ + pathname: paths.app.ontology_namespaces.index + }); + //window.location.reload(); + }, [router, itemctx]); + + return ( + <> + + + + + + } + /> + + + + + + + + + + + + ) + ; +}; + +BasicDetails.propTypes = { + id: PropTypes.string.isRequired, prefix: PropTypes.string, uri: PropTypes.string, is_syncable: PropTypes.string +}; diff --git a/mapping_workbench/frontend/src/sections/app/fields-registry/edit-form.js b/mapping_workbench/frontend/src/sections/app/fields-registry/edit-form.js new file mode 100644 index 000000000..f9dceb709 --- /dev/null +++ b/mapping_workbench/frontend/src/sections/app/fields-registry/edit-form.js @@ -0,0 +1,119 @@ +import PropTypes from 'prop-types'; +import toast from 'react-hot-toast'; +import * as Yup from 'yup'; +import {useFormik} from 'formik'; +import Button from '@mui/material/Button'; +import Card from '@mui/material/Card'; +import CardContent from '@mui/material/CardContent'; +import CardHeader from '@mui/material/CardHeader'; +import Grid from '@mui/material/Unstable_Grid2'; +import Stack from '@mui/material/Stack'; + +import {RouterLink} from 'src/components/router-link'; +import {paths} from 'src/paths'; +import {useRouter} from 'src/hooks/use-router'; +import {FormTextField} from "../../../components/app/form/text-field"; + + +export const EditForm = (props) => { + const {itemctx, ...other} = props; + const router = useRouter(); + const sectionApi = itemctx.api; + const item = itemctx.data; + + let initialValues = { + title: item.title || '' + }; + + const formik = useFormik({ + initialValues: initialValues, + validationSchema: Yup.object({ + title: Yup + .string() + .max(255) + .required('Title is required') + }), + onSubmit: async (values, helpers) => { + try { + let response; + if (itemctx.isNew) { + response = await sectionApi.createItem(values); + } else { + values['id'] = item._id; + response = await sectionApi.updateItem(values); + } + helpers.setStatus({success: true}); + helpers.setSubmitting(false); + toast.success(sectionApi.SECTION_ITEM_TITLE + ' ' + (itemctx.isNew ? "created" : "updated")); + if (response) { + if (itemctx.isNew) { + router.push({ + pathname: paths.app[sectionApi.section].edit, + query: {id: response._id} + }); + } else if (itemctx.isStateable) { + itemctx.setState(response); + } + } + } catch (err) { + console.error(err); + toast.error('Something went wrong!'); + helpers.setStatus({success: false}); + helpers.setErrors({submit: err.message}); + helpers.setSubmitting(false); + } + } + }); + + return ( +
+ + + + + + + + + + + + + + + + + +
+ ); +}; + +EditForm.propTypes = { + itemctx: PropTypes.object.isRequired +}; diff --git a/mapping_workbench/frontend/src/sections/app/fields-registry/list-search.js b/mapping_workbench/frontend/src/sections/app/fields-registry/list-search.js new file mode 100644 index 000000000..7119eed5f --- /dev/null +++ b/mapping_workbench/frontend/src/sections/app/fields-registry/list-search.js @@ -0,0 +1,255 @@ +import {useCallback, useMemo, useRef, useState} from 'react'; +import PropTypes from 'prop-types'; +import SearchMdIcon from '@untitled-ui/icons-react/build/esm/SearchMd'; +import Box from '@mui/material/Box'; +import Chip from '@mui/material/Chip'; +import Divider from '@mui/material/Divider'; +import Input from '@mui/material/Input'; +import Stack from '@mui/material/Stack'; +import SvgIcon from '@mui/material/SvgIcon'; +import Typography from '@mui/material/Typography'; + +import {MultiSelect} from 'src/components/multi-select'; +import {useUpdateEffect} from 'src/hooks/use-update-effect'; + + +const statusOptions = [ + { + label: 'Published', + value: 'published' + }, + { + label: 'Draft', + value: 'draft' + } +]; + +export const ListSearch = (props) => { + const {onFiltersChange, ...other} = props; + const queryRef = useRef(null); + const [chips, setChips] = useState([]); + + const handleChipsUpdate = useCallback(() => { + const filters = { + q: undefined, + status: [], + }; + + chips.forEach((chip) => { + switch (chip.field) { + case 'q': + // There will (or should) be only one chips with field "q" + // so we can set up it directly + filters.q = chip.value; + break; + case 'status': + filters.status.push(chip.value); + break; + default: + break; + } + }); + + onFiltersChange?.(filters); + }, [chips, onFiltersChange]); + + useUpdateEffect(() => { + handleChipsUpdate(); + }, [chips, handleChipsUpdate]); + + const handleChipDelete = useCallback((deletedChip) => { + setChips((prevChips) => { + return prevChips.filter((chip) => { + // There can exist multiple chips for the same field. + // Filter them by value. + + return !(deletedChip.field === chip.field && deletedChip.value === chip.value); + }); + }); + }, []); + + const handleQueryChange = useCallback((event) => { + event.preventDefault(); + + const value = queryRef.current?.value || ''; + + setChips((prevChips) => { + const found = prevChips.find((chip) => chip.field === 'q'); + + if (found && value) { + return prevChips.map((chip) => { + if (chip.field === 'q') { + return { + ...chip, + value: queryRef.current?.value || '' + }; + } + + return chip; + }); + } + + if (found && !value) { + return prevChips.filter((chip) => chip.field !== 'q'); + } + + if (!found && value) { + const chip = { + label: 'Q', + field: 'q', + value + }; + + return [...prevChips, chip]; + } + + return prevChips; + }); + + if (queryRef.current) { + queryRef.current.value = ''; + } + }, []); + + const handleStatusChange = useCallback((values) => { + setChips((prevChips) => { + const valuesFound = []; + + // First cleanup the previous chips + const newChips = prevChips.filter((chip) => { + if (chip.field !== 'status') { + return true; + } + + const found = values.includes(chip.value); + + if (found) { + valuesFound.push(chip.value); + } + + return found; + }); + + // Nothing changed + if (values.length === valuesFound.length) { + return newChips; + } + + values.forEach((value) => { + if (!valuesFound.includes(value)) { + const option = statusOptions.find((option) => option.value === value); + + newChips.push({ + label: 'Status', + field: 'status', + value, + displayValue: option.label + }); + } + }); + + return newChips; + }); + }, []); + + + // We memoize this part to prevent re-render issues + const statusValues = useMemo(() => chips + .filter((chip) => chip.field === 'status') + .map((chip) => chip.value), [chips]); + + const showChips = chips.length > 0; + + return ( +
+ + + + + + + + {showChips + ? ( + + {chips.map((chip, index) => ( + + <> + + {chip.label} + + : + {' '} + {chip.displayValue || chip.value} + + + )} + onDelete={() => handleChipDelete(chip)} + variant="outlined" + /> + ))} + + ) + : ( + + + No filters applied + + + )} + + {false && + + } +
+ ); +}; + +ListSearch.propTypes = { + onFiltersChange: PropTypes.func +}; diff --git a/mapping_workbench/frontend/src/components/app/file-manager/collection-files.js b/mapping_workbench/frontend/src/sections/app/fields-registry/list-table.js similarity index 51% rename from mapping_workbench/frontend/src/components/app/file-manager/collection-files.js rename to mapping_workbench/frontend/src/sections/app/fields-registry/list-table.js index 63c7d5d31..0626fa21e 100644 --- a/mapping_workbench/frontend/src/components/app/file-manager/collection-files.js +++ b/mapping_workbench/frontend/src/sections/app/fields-registry/list-table.js @@ -1,14 +1,10 @@ import {Fragment, useCallback, useState} from 'react'; import PropTypes from 'prop-types'; -import {toast} from 'react-hot-toast'; import ChevronDownIcon from '@untitled-ui/icons-react/build/esm/ChevronDown'; import ChevronRightIcon from '@untitled-ui/icons-react/build/esm/ChevronRight'; -import Button from '@mui/material/Button'; import CardContent from '@mui/material/CardContent'; import Divider from '@mui/material/Divider'; -import Grid from '@mui/material/Grid'; import IconButton from '@mui/material/IconButton'; -import Stack from '@mui/material/Stack'; import SvgIcon from '@mui/material/SvgIcon'; import Table from '@mui/material/Table'; import TableBody from '@mui/material/TableBody'; @@ -17,23 +13,17 @@ import TableSortLabel from '@mui/material/TableSortLabel'; import TableHead from '@mui/material/TableHead'; import TablePagination from '@mui/material/TablePagination'; import TableRow from '@mui/material/TableRow'; -import TextField from '@mui/material/TextField'; import Typography from '@mui/material/Typography'; -import { format } from 'date-fns'; -import {useMounted} from 'src/hooks/use-mounted'; import {Scrollbar} from 'src/components/scrollbar'; -import {SeverityPill} from 'src/components/severity-pill'; +import {ListItemActions} from 'src/components/app/list/list-item-actions'; import {ForListItemAction} from 'src/contexts/app/section/for-list-item-action'; import Tooltip from "@mui/material/Tooltip"; -import {ListFileCollectionActions} from "src/components/app/list/list-file-collection-actions"; -import { PropertyListItem } from 'src/components/property-list-item'; -import {FileCollectionListSearch} from "./file-collection-list-search"; -import {sparqlTestSuitesApi as sectionApi} from "../../../api/sparql-test-suites"; -import Card from "@mui/material/Card"; +import Switch from "@mui/material/Switch"; -export const CollectionFiles = (props) => { + +export const ListTable = (props) => { const { count = 0, items = [], @@ -45,15 +35,9 @@ export const CollectionFiles = (props) => { sectionApi } = props; - const [currentItem, setCurrentItem] = useState(null); - const isMounted = useMounted(); + //console.log("PROJECT PROPS: ", props); - // if(isMounted()){ - // console.log("itemsWEneed: ", items[0]._id); - // const itemFileCollection = useItemsStoreFiles(items[0]._id); - // } - //const itemFileCollection = useItemsStoreFiles(items[0]._id); - //console.log("itemFileCollection: ", itemFileCollection); + const [currentItem, setCurrentItem] = useState(null); const handleItemToggle = useCallback((itemId) => { setCurrentItem((prevItemId) => { @@ -63,32 +47,50 @@ export const CollectionFiles = (props) => { return itemId; }); - //useItemsStoreFiles(itemId); }, []); - const handleItemClose = useCallback(() => { - setCurrentItem(null); - }, []); + // const handleItemClose = useCallback(() => { + // setCurrentItem(null); + // }, []); - const handleItemUpdate = useCallback(() => { - setCurrentItem(null); - toast.success('Item updated'); - }, []); + // const handleItemUpdate = useCallback(() => { + // setCurrentItem(null); + // toast.success('Item updated'); + // }, []); - const handleItemDelete = useCallback(() => { - toast.error('Item cannot be deleted'); - }, []); - - //console.log("date before: ", items); - //console.log(" items[0].created_at ",(items[0].created_at).replace("T", " ").split(".")[0]); + // const handleItemDelete = useCallback(() => { + + // toast.error('Item cannot be deleted'); + // }, []); return (
+ + {/* + + + Name + + + */} { - Title + Prefix - Description + URI - {/* - Status - */} - + - Created + Syncable @@ -161,24 +159,42 @@ export const CollectionFiles = (props) => { + {/* + + + + {item.name} + + + + */} - {item.title} + {item.prefix} - {item.description} + {item.uri} - {/* - - {item.status} - - */} - - {(item.created_at).replace("T", " ").split(".")[0]} + + - @@ -201,73 +217,8 @@ export const CollectionFiles = (props) => { }} > - - - - Details - - - - - {/* */} - - - - - {/* */} - - - - - - - - - - + )} @@ -284,13 +235,13 @@ export const CollectionFiles = (props) => { onRowsPerPageChange={onRowsPerPageChange} page={page} rowsPerPage={rowsPerPage} - rowsPerPageOptions={[5, 10, 25]} + rowsPerPageOptions={sectionApi.DEFAULT_ROWS_PER_PAGE_SELECTION} /> ); }; -FileCollectionListTable.propTypes = { +ListTable.propTypes = { count: PropTypes.number, items: PropTypes.array, onPageChange: PropTypes.func, diff --git a/mapping_workbench/frontend/src/sections/app/file-manager/file-collection-edit-form.js b/mapping_workbench/frontend/src/sections/app/file-manager/file-collection-edit-form.js index fbde4cfd7..f2557b6a8 100644 --- a/mapping_workbench/frontend/src/sections/app/file-manager/file-collection-edit-form.js +++ b/mapping_workbench/frontend/src/sections/app/file-manager/file-collection-edit-form.js @@ -23,6 +23,7 @@ import FormLabel from "@mui/material/FormLabel"; import Select from "@mui/material/Select"; import {FormTextField} from "../../../components/app/form/text-field"; import {FormTextArea} from "../../../components/app/form/text-area"; +import {MappingPackageFormSelect} from "../mapping-package/components/mapping-package-form-select"; export const FileCollectionEditForm = (props) => { @@ -49,6 +50,10 @@ export const FileCollectionEditForm = (props) => { initialValues['type'] = item.type || null; } + if (sectionApi.hasMappingPackage) { + initialValues['mapping_package_id'] = item.mapping_package_id || ''; + } + switch (sectionApi.section) { case 'test_data_suites': customPathName = paths.app.test_data_suites.index; @@ -131,6 +136,11 @@ export const FileCollectionEditForm = (props) => { > + {sectionApi.hasMappingPackage && ( + + + + )} {sectionApi.hasFileCollectionType && ( { const handleMappingPackageChange = useCallback(async (event) => { let value = event.target.value; - formik.setFieldValue('mapping_package', value); + formik.setFieldValue('mapping_package_id', value); }, [formik]) return ( <> {mappingPackagesStore.items.map((mapping_package) => ( { end_date: item.end_date && new Date(item.end_date) || '', min_xsd_version: item.min_xsd_version || '', max_xsd_version: item.max_xsd_version || '', - test_data_suites: (item.test_data_suites || []).map(x => x.id), + //test_data_suites: (item.test_data_suites || []).map(x => x.id), shacl_test_suites: (item.shacl_test_suites || []).map(x => x.id) }; @@ -125,7 +125,7 @@ export const EditForm = (props) => { - + {false && @@ -136,7 +136,7 @@ export const EditForm = (props) => { - + } diff --git a/mapping_workbench/frontend/src/sections/app/ontology-namespace/edit-form.js b/mapping_workbench/frontend/src/sections/app/ontology-namespace/edit-form.js index 3cc6d7359..07cde76de 100644 --- a/mapping_workbench/frontend/src/sections/app/ontology-namespace/edit-form.js +++ b/mapping_workbench/frontend/src/sections/app/ontology-namespace/edit-form.js @@ -9,13 +9,11 @@ import CardContent from '@mui/material/CardContent'; import CardHeader from '@mui/material/CardHeader'; import Grid from '@mui/material/Unstable_Grid2'; import Stack from '@mui/material/Stack'; -import TextField from '@mui/material/TextField'; import {RouterLink} from 'src/components/router-link'; import {paths} from 'src/paths'; import {useRouter} from 'src/hooks/use-router'; import {FormTextField} from "../../../components/app/form/text-field"; -import {FormTextArea} from "../../../components/app/form/text-area"; import FormControlLabel from "@mui/material/FormControlLabel"; import Switch from "@mui/material/Switch"; import MenuList from "@mui/material/MenuList"; @@ -39,7 +37,7 @@ export const EditForm = (props) => { prefix: Yup .string() .max(255) - .required('Title is required'), + .required('Prefix is required'), uri: Yup.string().max(2048), is_syncable: Yup.boolean() }), diff --git a/mapping_workbench/frontend/src/sections/app/specific-triple-map-fragment/edit-form.js b/mapping_workbench/frontend/src/sections/app/specific-triple-map-fragment/edit-form.js index 953a3e4df..9417f4e32 100644 --- a/mapping_workbench/frontend/src/sections/app/specific-triple-map-fragment/edit-form.js +++ b/mapping_workbench/frontend/src/sections/app/specific-triple-map-fragment/edit-form.js @@ -16,9 +16,6 @@ import {FormTextField} from "../../../components/app/form/text-field"; import {sessionApi} from "../../../api/session"; import {MappingPackageFormSelect} from "../mapping-package/components/mapping-package-form-select"; import {FormCodeTextArea} from "../../../components/app/form/code-text-area"; -import FormControl from "@mui/material/FormControl"; -import FormLabel from "@mui/material/FormLabel"; -import Select from "@mui/material/Select"; import MenuItem from "@mui/material/MenuItem"; import TextField from "@mui/material/TextField"; import * as React from "react"; @@ -34,7 +31,7 @@ export const EditForm = (props) => { triple_map_uri: item.triple_map_uri || '', triple_map_content: item.triple_map_content || '', format: item.format || sectionApi.FILE_RESOURCE_DEFAULT_FORMAT || '', - mapping_package: (item.mapping_package && item.mapping_package.id) || '' + mapping_package_id: item.mapping_package_id || '' }; const formik = useFormik({ @@ -45,7 +42,7 @@ export const EditForm = (props) => { .max(255) .required('URI is required'), triple_map_content: Yup.string(), - mapping_package: Yup.string().max(255).required('Package is required') + mapping_package_id: Yup.string().max(255).required('Package is required') }), onSubmit: async (values, helpers) => { try { diff --git a/mapping_workbench/frontend/src/sections/app/specific-triple-map-fragment/list-table.js b/mapping_workbench/frontend/src/sections/app/specific-triple-map-fragment/list-table.js index e26a83dd3..7931af9a5 100644 --- a/mapping_workbench/frontend/src/sections/app/specific-triple-map-fragment/list-table.js +++ b/mapping_workbench/frontend/src/sections/app/specific-triple-map-fragment/list-table.js @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import ChevronDownIcon from '@untitled-ui/icons-react/build/esm/ChevronDown'; import ChevronRightIcon from '@untitled-ui/icons-react/build/esm/ChevronRight'; import CardContent from '@mui/material/CardContent'; -import Divider from '@mui/material/Divider'; import Grid from '@mui/material/Grid'; import IconButton from '@mui/material/IconButton'; import SvgIcon from '@mui/material/SvgIcon'; @@ -57,7 +56,7 @@ export const ListTable = (props) => { (async () => { setProjectMappingPackages(await mappingPackagesApi.getProjectPackages()); })() - }, [mappingPackagesApi]) + }, []) const [projectMappingPackagesMap, setProjectMappingPackagesMap] = useState({}); @@ -184,7 +183,7 @@ export const ListTable = (props) => { - {projectMappingPackagesMap[item.mapping_package.id]} + {item.mapping_package && projectMappingPackagesMap[item.mapping_package.id]} {