From 5bde6ce67187a413ab961e897bb846cb783a08a1 Mon Sep 17 00:00:00 2001 From: "yvernet@slb.com" Date: Fri, 25 Mar 2022 14:09:26 +0100 Subject: [PATCH 1/5] valid record id at API level common test to validate success/invalid id in various get & delete APIs (remove already covered cases from a previous old test) --- app/model/osdu_model.py | 24 +-- app/model/osdu_record_id.py | 12 +- app/routers/bulk/bulk_routes.py | 5 +- app/routers/ddms_v3/markerset_ddms_v3.py | 17 +- app/routers/ddms_v3/well_ddms_v3.py | 32 ++-- app/routers/ddms_v3/wellbore_ddms_v3.py | 21 +-- .../ddms_v3/wellbore_trajectory_ddms_v3.py | 15 +- app/routers/ddms_v3/welllog_ddms_v3.py | 14 +- .../routers/ddms_v3/common_ddms_v3_test.py | 178 +++++++++--------- .../routers/ddms_v3/delete_ddms_v3_test.py | 10 +- 10 files changed, 153 insertions(+), 175 deletions(-) diff --git a/app/model/osdu_model.py b/app/model/osdu_model.py index 99556900..08b8948a 100644 --- a/app/model/osdu_model.py +++ b/app/model/osdu_model.py @@ -23,6 +23,8 @@ from typing import Any, Dict, List, Optional, Union from pydantic import BaseModel, Extra, Field, constr from .model_curated import DDMSBaseModel +from .osdu_record_id import WellId, WellboreId, WellboreTrajectoryId, WellboreMarkerSetId, WellLogId + class Tags(BaseModel): class Config: @@ -1257,7 +1259,7 @@ class Well(DDMSBaseModel): The origin of a set of wellbores. """ - id: Optional[constr(regex=r'^[\w\-\.]+:master-data\-\-Well:[\w\-\.\:\%]+$')] = Field( + id: Optional[WellId] = Field( None, description='Previously called ResourceID or SRN which identifies this OSDU resource object without version.', example='namespace:master-data--Well:6c60ceb0-3521-57b7-9bd8-e1d7c9f66230', @@ -1398,9 +1400,7 @@ class Wellbore(DDMSBaseModel): A hole in the ground extending from a point at the earth's surface to the maximum point of penetration. """ - id: Optional[ - constr(regex=r'^[\w\-\.]+:master-data\-\-Wellbore:[\w\-\.\:\%]+$') - ] = Field( + id: Optional[WellboreId] = Field( None, description='Previously called ResourceID or SRN which identifies this OSDU resource object without version.', example='namespace:master-data--Wellbore:c7c421a7-f496-5aef-8093-298c32bfdea9', @@ -1709,9 +1709,7 @@ class WellLogData110( class WellLog(DDMSBaseModel): - id: Optional[ - constr(regex=r'^[\w\-\.]+:work-product-component\-\-WellLog:[\w\-\.\:\%]+$') - ] = Field( + id: Optional[WellLogId] = Field( None, description='Previously called ResourceID or SRN which identifies this OSDU resource object without version.', example='namespace:work-product-component--WellLog:c2c79f1c-90ca-5c92-b8df-04dbe438f414', @@ -2147,11 +2145,7 @@ class WellboreTrajectory(DDMSBaseModel): Work Product Component describing an individual instance of a wellbore trajectory data object. Also called a deviation survey, wellbore trajectory is data that is used to calculate the position and spatial uncertainty of a planned or actual wellbore in 2-dimensional and 3-dimensional space. """ - id: Optional[ - constr( - regex=r'^[\w\-\.]+:work-product-component\-\-WellboreTrajectory:[\w\-\.\:\%]+$' - ) - ] = Field( + id: Optional[WellboreTrajectoryId] = Field( None, description='Previously called ResourceID or SRN which identifies this OSDU resource object without version.', example='namespace:work-product-component--WellboreTrajectory:606f224a-ef1f-5690-9843-d26cd7e33e10', @@ -2456,11 +2450,7 @@ class WellboreMarkerSet(DDMSBaseModel): Wellbore Markers identify the depth in a wellbore, measured below a reference elevation, at which a person or an automated process identifies a noteworthy observation, which is usually a change in the rock that intersects that wellbore. Formation Marker data includes attributes/properties that put these depths in context. Formation Markers are sometimes known as picks or formation tops. """ - id: Optional[ - constr( - regex=r'^[\w\-\.]+:work-product-component\-\-WellboreMarkerSet:[\w\-\.\:\%]+$' - ) - ] = Field( + id: Optional[WellboreMarkerSetId] = Field( None, description='Previously called ResourceID or SRN which identifies this OSDU resource object without version.', example='namespace:work-product-component--WellboreMarkerSet:d5303b79-7904-5bfe-9c44-9a3ff41b6d6c', diff --git a/app/model/osdu_record_id.py b/app/model/osdu_record_id.py index 42faa1c4..e6588288 100644 --- a/app/model/osdu_record_id.py +++ b/app/model/osdu_record_id.py @@ -31,9 +31,9 @@ def split_record_id_version(record_id: str) -> Tuple[Optional[str], Optional[int return match["record_id"], None if not version else int(version) -# specific record_id model regex -OSDU_WELL_REGEX = re.compile(r"^[\w\-\.]+:master-data\-\-Well:[\w\-\.\:\%]+$") -OSDU_WELLBORE_REGEX = re.compile(r"^[\w\-\.]+:master-data\-\-Wellbore:[\w\-\.\:\%]+$") -OSDU_WELLLOG_REGEX = re.compile(r"^[\w\-\.]+:work-product-component\-\-WellLog:[\w\-\.\:\%]+$") -OSDU_WELLBORETRAJECTORY_REGEX = re.compile(r"^[\w\-\.]+:work-product-component\-\-WellboreTrajectory:[\w\-\.\:\%]+$") -OSDU_WELLBOREMARKERSET_REGEX = re.compile(r"^[\w\-\.]+:work-product-component\-\-WellboreMarkerSet:[\w\-\.\:\%]+$") +# specific record_id model, type const str with regex +WellId = constr(regex=r'^[\w\-\.]+:master-data\-\-Well:[\w\-\.\:\%]+$') +WellboreId = constr(regex=r'^[\w\-\.]+:master-data\-\-Wellbore:[\w\-\.\:\%]+$') +WellboreTrajectoryId = constr(regex=r'^[\w\-\.]+:work-product-component\-\-WellboreTrajectory:[\w\-\.\:\%]+$') +WellboreMarkerSetId = constr(regex=r'^[\w\-\.]+:work-product-component\-\-WellboreMarkerSet:[\w\-\.\:\%]+$') +WellLogId = constr(regex=r'^[\w\-\.]+:work-product-component\-\-WellLog:[\w\-\.\:\%]+$') diff --git a/app/routers/bulk/bulk_routes.py b/app/routers/bulk/bulk_routes.py index a617e023..7153aba3 100644 --- a/app/routers/bulk/bulk_routes.py +++ b/app/routers/bulk/bulk_routes.py @@ -43,7 +43,7 @@ from app.routers.bulk.utils import (with_dask_blob_storage, get_data_consistency_checks) # imports for session manipulation -from app.bulk_persistence import ( +from app.persistence.sessions_storage import ( Session, SessionException, SessionState, @@ -63,7 +63,7 @@ from app.bulk_persistence.dataframe_validators import (auto_cast_columns_to_stri DataFrameValidationFunc, no_validation) from app.bulk_persistence import JSONOrient, get_dataframe, download_bulk -from app.bulk_persistence import DaskBulkStorage +from app.bulk_persistence.dask.dask_bulk_storage import DaskBulkStorage from app.bulk_persistence.dask.errors import BulkError, BulkRecordNotFound, FilterError, TooManyColumnsRequested from app.bulk_persistence.mime_types import MimeTypes, MimeType from app.bulk_persistence.dask.traces import trace_dataframe_attributes, trace_attributes_root_span @@ -381,6 +381,7 @@ async def complete_session( _, updated_version = split_record_id_version(new_record.record_id_versions[0]) if updated_version is None: + # TODO same behavior as before but should we raise or just set 'version' to None ? raise RuntimeError(f"{new_record.record_id_versions[0]} is not valid.") response = CommitSessionResponse( diff --git a/app/routers/ddms_v3/markerset_ddms_v3.py b/app/routers/ddms_v3/markerset_ddms_v3.py index 9e5cf08a..65979d9a 100644 --- a/app/routers/ddms_v3/markerset_ddms_v3.py +++ b/app/routers/ddms_v3/markerset_ddms_v3.py @@ -22,7 +22,7 @@ from starlette.requests import Request from app.clients.storage_service_client import get_storage_record_service from app.model.model_utils import to_record, from_record -from app.model.osdu_record_id import split_record_id_version +from app.model.osdu_record_id import split_record_id_version, WellboreMarkerSetId from app.model.osdu_model import WellboreMarkerSet110 as WellboreMarkerSet from app.routers.common_parameters import REQUIRED_ROLES_READ, REQUIRED_ROLES_WRITE from app.routers.ddms_v3.ddms_v3_utils import DMSV3RouterUtils @@ -45,13 +45,14 @@ router = APIRouter(route_class=TracingRoute) }, ) async def get_wellbore_markerset_osdu( - wellboremarkersetid: str, request: Request, ctx: Context = Depends(get_ctx) + wellboremarkersetid: WellboreMarkerSetId, request: Request, ctx: Context = Depends(get_ctx) ) -> WellboreMarkerSet: - # Note: version is dropped here - record_id, _ = split_record_id_version(wellboremarkersetid) + # TODO version is dropped here, it would be better to either return an error or return the version not the latest + wellboremarkersetid, _ = split_record_id_version(wellboremarkersetid) storage_client = await get_storage_record_service(ctx) + wellboreMarkerset_record = await storage_client.get_record( - id=record_id, data_partition_id=ctx.partition_id + id=wellboremarkersetid, data_partition_id=ctx.partition_id ) DMSV3RouterUtils.raise_if_not_osdu_right_entity_kind(wellboreMarkerset_record, request.state) return from_record(WellboreMarkerSet, wellboreMarkerset_record) @@ -72,7 +73,7 @@ async def get_wellbore_markerset_osdu( }, }, ) -async def del_osdu_wellboreMarkerset(wellboremarkersetid: str, ctx: Context = Depends(get_ctx)): +async def del_osdu_wellboreMarkerset(wellboremarkersetid: WellboreMarkerSetId, ctx: Context = Depends(get_ctx)): storage_client = await get_storage_record_service(ctx) wellboremarkersetid, _ = split_record_id_version(wellboremarkersetid) await storage_client.delete_record( @@ -91,7 +92,7 @@ async def del_osdu_wellboreMarkerset(wellboremarkersetid: str, ctx: Context = De }, ) async def get_osdu_wellboreMarkerset_versions( - wellboremarkersetid: str, request: Request, ctx: Context = Depends(get_ctx) + wellboremarkersetid: WellboreMarkerSetId, request: Request, ctx: Context = Depends(get_ctx) ) -> RecordVersions: record = await fetch_record(ctx, wellboremarkersetid) DMSV3RouterUtils.raise_if_not_osdu_right_entity_kind(record, request.state) @@ -113,7 +114,7 @@ async def get_osdu_wellboreMarkerset_versions( response_model_exclude_unset=True, ) async def get_osdu_wellboreMarkerset_version( - wellboremarkersetid: str, version: int, request: Request, ctx: Context = Depends(get_ctx) + wellboremarkersetid: WellboreMarkerSetId, version: int, request: Request, ctx: Context = Depends(get_ctx) ) -> WellboreMarkerSet: storage_client = await get_storage_record_service(ctx) wellboremarkersetid, _ = split_record_id_version(wellboremarkersetid) diff --git a/app/routers/ddms_v3/well_ddms_v3.py b/app/routers/ddms_v3/well_ddms_v3.py index 535210c9..a4f359c2 100644 --- a/app/routers/ddms_v3/well_ddms_v3.py +++ b/app/routers/ddms_v3/well_ddms_v3.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from fastapi import APIRouter, Depends, Response, status, Body, HTTPException +from fastapi import APIRouter, Depends, Response, status, Body from starlette.requests import Request from app.clients.storage_service_client import get_storage_record_service @@ -22,7 +22,7 @@ from odes_storage.models import ( RecordVersions, ) from app.model.osdu_model import Well -from app.model.osdu_record_id import split_record_id_version, OSDU_WELL_REGEX +from app.model.osdu_record_id import split_record_id_version, WellId from ..common_parameters import REQUIRED_ROLES_READ, REQUIRED_ROLES_WRITE from app.context import Context, get_ctx from app.utils import load_schema_example @@ -45,14 +45,9 @@ router = APIRouter(route_class=TracingRoute) status.HTTP_404_NOT_FOUND: {"description": "Well not found"} }, ) -async def get_well_osdu( - wellid: str, ctx: Context = Depends(get_ctx) -) -> Well: - if OSDU_WELL_REGEX.match(wellid) is None: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Id is not OSDU Well") - # Note: version is dropped here +async def get_well_osdu(wellid: WellId, ctx: Context = Depends(get_ctx)) -> Well: + # TODO version is dropped here, it would be better to either return an error or return the version not the latest record_id, _ = split_record_id_version(wellid) - storage_client = await get_storage_record_service(ctx) well_record = await storage_client.get_record(id=record_id, data_partition_id=ctx.partition_id) return from_record(Well, well_record) @@ -73,11 +68,9 @@ async def get_well_osdu( }, }, ) -async def del_osdu_well(wellid: str, ctx: Context = Depends(get_ctx)): +async def del_osdu_well(wellid: WellId, ctx: Context = Depends(get_ctx)): storage_client = await get_storage_record_service(ctx) - await storage_client.delete_record( - id=wellid, data_partition_id=ctx.partition_id - ) + await storage_client.delete_record(id=wellid, data_partition_id=ctx.partition_id) @router.get( @@ -90,15 +83,11 @@ async def del_osdu_well(wellid: str, ctx: Context = Depends(get_ctx)): status.HTTP_404_NOT_FOUND: {"description": "Well not found"} }, ) -async def get_osdu_well_versions( - wellid: str, request: Request, ctx: Context = Depends(get_ctx) -) -> RecordVersions: +async def get_osdu_well_versions(wellid: WellId, request: Request, ctx: Context = Depends(get_ctx)) -> RecordVersions: record = await fetch_record(ctx, wellid) DMSV3RouterUtils.raise_if_not_osdu_right_entity_kind(record, request.state) storage_client = await get_storage_record_service(ctx) - return await storage_client.get_all_record_versions( - id=wellid, data_partition_id=ctx.partition_id - ) + return await storage_client.get_all_record_versions(id=wellid, data_partition_id=ctx.partition_id) @router.get( @@ -113,7 +102,7 @@ async def get_osdu_well_versions( response_model_exclude_unset=True, ) async def get_osdu_well_version( - wellid: str, version: int, request: Request, ctx: Context = Depends(get_ctx) + wellid: WellId, version: int, request: Request, ctx: Context = Depends(get_ctx) ) -> Well: storage_client = await get_storage_record_service(ctx) well_record = await storage_client.get_record_version( @@ -141,7 +130,8 @@ async def post_well_osdu( DMSV3RouterUtils.validate_record_against_kinds_schema(wells) storage_client = await get_storage_record_service(ctx) - return await storage_client.create_or_update_records( + r= await storage_client.create_or_update_records( record=[to_record(w) for w in wells], data_partition_id=ctx.partition_id, ) + return r diff --git a/app/routers/ddms_v3/wellbore_ddms_v3.py b/app/routers/ddms_v3/wellbore_ddms_v3.py index 5e3c99ea..5058dc54 100644 --- a/app/routers/ddms_v3/wellbore_ddms_v3.py +++ b/app/routers/ddms_v3/wellbore_ddms_v3.py @@ -12,11 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from fastapi import APIRouter, Body, Depends, HTTPException, Response, status -from odes_storage.models import CreateUpdateRecordsResponse, List, RecordVersions +from fastapi import APIRouter, Body, Depends, Response, status from starlette.requests import Request -from app.model.osdu_record_id import split_record_id_version, OSDU_WELLBORE_REGEX +from odes_storage.models import CreateUpdateRecordsResponse, List, RecordVersions +from app.model.osdu_record_id import split_record_id_version, WellboreId from app.clients.storage_service_client import get_storage_record_service from app.model.model_utils import from_record, to_record @@ -43,14 +43,9 @@ router = APIRouter(route_class=TracingRoute) status.HTTP_404_NOT_FOUND: {"description": "Wellbore not found"} }, ) -async def get_wellbore_osdu( - wellboreid: str, ctx: Context = Depends(get_ctx) -) -> Wellbore: - if OSDU_WELLBORE_REGEX.match(wellboreid) is None: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Id is not OSDU Wellbore") - # Note: version is dropped here +async def get_wellbore_osdu(wellboreid: WellboreId, ctx: Context = Depends(get_ctx)) -> Wellbore: + # TODO version is dropped here, it would be better to either return an error or return the version not the latest record_id, _ = split_record_id_version(wellboreid) - storage_client = await get_storage_record_service(ctx) well_record = await storage_client.get_record(id=record_id, data_partition_id=ctx.partition_id) return from_record(Wellbore, well_record) @@ -71,7 +66,7 @@ async def get_wellbore_osdu( }, }, ) -async def del_osdu_wellbore(wellboreid: str, ctx: Context = Depends(get_ctx)): +async def del_osdu_wellbore(wellboreid: WellboreId, ctx: Context = Depends(get_ctx)): storage_client = await get_storage_record_service(ctx) await storage_client.delete_record( id=wellboreid, data_partition_id=ctx.partition_id @@ -89,7 +84,7 @@ async def del_osdu_wellbore(wellboreid: str, ctx: Context = Depends(get_ctx)): }, ) async def get_osdu_wellbore_versions( - wellboreid: str, request: Request, ctx: Context = Depends(get_ctx) + wellboreid: WellboreId, request: Request, ctx: Context = Depends(get_ctx) ) -> RecordVersions: record = await fetch_record(ctx, wellboreid) DMSV3RouterUtils.raise_if_not_osdu_right_entity_kind(record, request.state) @@ -111,7 +106,7 @@ async def get_osdu_wellbore_versions( response_model_exclude_unset=True, ) async def get_osdu_wellbore_version( - wellboreid: str, version: int, request: Request, ctx: Context = Depends(get_ctx) + wellboreid: WellboreId, version: int, request: Request, ctx: Context = Depends(get_ctx) ) -> Wellbore: storage_client = await get_storage_record_service(ctx) wellbore_record = await storage_client.get_record_version( diff --git a/app/routers/ddms_v3/wellbore_trajectory_ddms_v3.py b/app/routers/ddms_v3/wellbore_trajectory_ddms_v3.py index 9bf62b0d..00e30d6d 100644 --- a/app/routers/ddms_v3/wellbore_trajectory_ddms_v3.py +++ b/app/routers/ddms_v3/wellbore_trajectory_ddms_v3.py @@ -13,13 +13,14 @@ # limitations under the License. from fastapi import APIRouter, Body, Depends, HTTPException, Response, status -from odes_storage.models import CreateUpdateRecordsResponse, List, RecordVersions from starlette.requests import Request +from odes_storage.models import CreateUpdateRecordsResponse, List, RecordVersions + from app.clients.storage_service_client import get_storage_record_service from app.consistency import DuplicatedStationProperties, check_trajectory_consistency from app.model.model_utils import from_record, to_record -from app.model.osdu_record_id import split_record_id_version +from app.model.osdu_record_id import split_record_id_version, WellboreTrajectoryId from app.model.osdu_model import WellboreTrajectory110 as WellboreTrajectory from app.routers.bulk.bulk_uri_dependencies import BulkIdAccess, get_bulk_id_access from app.routers.common_parameters import REQUIRED_ROLES_READ, REQUIRED_ROLES_WRITE @@ -47,9 +48,10 @@ WELLBORE_TRAJECTORIES_API_BASE_PATH = '/wellboretrajectories' }, ) async def get_wellbore_trajectory_osdu( - wellboretrajectoryid: str, request: Request, ctx: Context = Depends(get_ctx) + wellboretrajectoryid: WellboreTrajectoryId, request: Request, ctx: Context = Depends(get_ctx) ) -> WellboreTrajectory: storage_client = await get_storage_record_service(ctx) + # TODO version is dropped here, it would be better to either return an error or return the version not the latest wellboretrajectoryid, _ = split_record_id_version(wellboretrajectoryid) wellboreTrajectory_record = await storage_client.get_record( @@ -73,7 +75,7 @@ async def get_wellbore_trajectory_osdu( }, ) async def del_osdu_wellboreTrajectory( - wellboretrajectoryid: str, + wellboretrajectoryid: WellboreTrajectoryId, purge: bool = False, ctx: Context = Depends(get_ctx), bulk_uri_access: BulkIdAccess = Depends(get_bulk_id_access), @@ -93,7 +95,7 @@ async def del_osdu_wellboreTrajectory( }, ) async def get_osdu_wellboreTrajectory_versions( - wellboretrajectoryid: str, request: Request, ctx: Context = Depends(get_ctx) + wellboretrajectoryid: WellboreTrajectoryId, request: Request, ctx: Context = Depends(get_ctx) ) -> RecordVersions: record = await fetch_record(ctx, wellboretrajectoryid) DMSV3RouterUtils.raise_if_not_osdu_right_entity_kind(record, request.state) @@ -117,11 +119,10 @@ async def get_osdu_wellboreTrajectory_versions( response_model_exclude_unset=True, ) async def get_osdu_wellboreTrajectory_version( - wellboretrajectoryid: str, version: int, request: Request, ctx: Context = Depends(get_ctx) + wellboretrajectoryid: WellboreTrajectoryId, version: int, request: Request, ctx: Context = Depends(get_ctx) ) -> WellboreTrajectory: storage_client = await get_storage_record_service(ctx) wellboretrajectoryid, _ = split_record_id_version(wellboretrajectoryid) - wellboreTrajectory_record = await storage_client.get_record_version( id=wellboretrajectoryid, version=version, data_partition_id=ctx.partition_id ) diff --git a/app/routers/ddms_v3/welllog_ddms_v3.py b/app/routers/ddms_v3/welllog_ddms_v3.py index 32827145..a65512f7 100644 --- a/app/routers/ddms_v3/welllog_ddms_v3.py +++ b/app/routers/ddms_v3/welllog_ddms_v3.py @@ -13,9 +13,10 @@ # limitations under the License. from fastapi import APIRouter, Body, Depends, HTTPException, Response, status -from odes_storage.models import CreateUpdateRecordsResponse, List, RecordVersions from starlette.requests import Request +from odes_storage.models import CreateUpdateRecordsResponse, List, RecordVersions + from app.clients.storage_service_client import get_storage_record_service from app.consistency import ( DuplicatedCurveIdException, @@ -23,8 +24,8 @@ from app.consistency import ( check_welllog_consistency ) from app.model.model_utils import from_record, to_record +from app.model.osdu_record_id import split_record_id_version, WellLogId from app.model.osdu_model import WellLog110 as WellLog -from app.model.osdu_record_id import split_record_id_version from app.routers.bulk.bulk_uri_dependencies import BulkIdAccess, get_bulk_id_access from app.routers.common_parameters import REQUIRED_ROLES_READ, REQUIRED_ROLES_WRITE from app.routers.ddms_v3.ddms_v3_utils import DMSV3RouterUtils @@ -51,9 +52,10 @@ WELL_LOGS_API_BASE_PATH = '/welllogs' }, ) async def get_welllog_osdu( - welllogid: str, request: Request, ctx: Context = Depends(get_ctx) + welllogid: WellLogId, request: Request, ctx: Context = Depends(get_ctx) ) -> WellLog: storage_client = await get_storage_record_service(ctx) + # TODO version is dropped here, it would be better to either return an error or return the version not the latest welllogid, _ = split_record_id_version(welllogid) welllog_record = await storage_client.get_record( @@ -79,7 +81,7 @@ async def get_welllog_osdu( }, ) async def del_osdu_welllog( - welllogid: str, + welllogid: WellLogId, purge: bool = False, ctx: Context = Depends(get_ctx), bulk_uri_access: BulkIdAccess = Depends(get_bulk_id_access), @@ -99,7 +101,7 @@ async def del_osdu_welllog( }, ) async def get_osdu_welllog_versions( - welllogid: str, request: Request, ctx: Context = Depends(get_ctx) + welllogid: WellLogId, request: Request, ctx: Context = Depends(get_ctx) ) -> RecordVersions: record = await fetch_record(ctx, welllogid) DMSV3RouterUtils.raise_if_not_osdu_right_entity_kind(record, request.state) @@ -123,7 +125,7 @@ async def get_osdu_welllog_versions( response_model_exclude_unset=True, ) async def get_osdu_welllog_version( - welllogid: str, version: int, request: Request, ctx: Context = Depends(get_ctx) + welllogid: WellLogId, version: int, request: Request, ctx: Context = Depends(get_ctx) ) -> WellLog: storage_client = await get_storage_record_service(ctx) welllogid, _ = split_record_id_version(welllogid) diff --git a/tests/unit/routers/ddms_v3/common_ddms_v3_test.py b/tests/unit/routers/ddms_v3/common_ddms_v3_test.py index 5c8aacc2..b262e606 100644 --- a/tests/unit/routers/ddms_v3/common_ddms_v3_test.py +++ b/tests/unit/routers/ddms_v3/common_ddms_v3_test.py @@ -17,6 +17,7 @@ from unittest import mock from odes_storage import UnexpectedResponse import pandas as pd import pytest +from app.wdms_app import DDMS_V3_PATH from app.auth.auth import require_opendes_authorized_user from app.bulk_persistence import DaskBulkStorage, make_local_dask_bulk_storage, SessionsStorage from app.clients import SearchServiceClient, StorageRecordServiceClient @@ -38,37 +39,6 @@ from tests.unit.test_utils import create_mock_class Contains unified common tests for the different kind. Mainly CRUD test cases """ -tests_parameters = [ - ( - "/ddms/v3/wellbores", - r"namespace:master-data--Wellbore:c7c421a7-f496-5aef-8093-298c32bfdea9", - Wellbore( - id=r"namespace:master-data--Wellbore:c7c421a7-f496-5aef-8093-298c32bfdea9:", - kind="namespace:osdu:master-data--Wellbore:1.0.0", - acl={"owners": ["me@osdu.org"], "viewers": ["ze@osdu.org"]}, - legal={ - "legaltags": ["string"], - "otherRelevantDataCountries": ["FR"], - }, - data={}, - ), - ), - ( - "/ddms/v3/wellbores", - r"namespace:master-data--Wellbore:c7c421a7-f496-5aef-8093-298c32bfdea9", - Wellbore( - id=r"namespace:master-data--Wellbore:c7c421a7-f496-5aef-8093-298c32bfdea9:145", - kind="namespace:osdu:master-data--Wellbore:1.0.0", - acl={"owners": ["me@osdu.org"], "viewers": ["ze@osdu.org"]}, - legal={ - "legaltags": ["string"], - "otherRelevantDataCountries": ["FR"], - }, - data={}, - ), - ), -] - StorageRecordServiceClientMock = create_mock_class(StorageRecordServiceClient) SearchServiceClientMock = create_mock_class(SearchServiceClient) @@ -143,50 +113,81 @@ def test_post_records_successful(dasked_test_app_with_mocked_core_service): assert CreateUpdateRecordsResponse.parse_raw(response.text) == expected_response -def replace_template(source_obj_str: str) -> str: - source_obj_str = ( - source_obj_str.replace("{{datapartitionid}}", "datapartitionid") - .replace("{datapartitionid}", "datapartitionid") - .replace("{{domain}}", "domain") - .replace("{{wellboreId}}", "wellboreId") - .replace("{{wellId}}", "wellId") - ) - return source_obj_str - - -get_invalid_id_parameters = [ - (Wellbore, "/ddms/v3/wellbores", "toto"), - (Well, "/ddms/v3/wells", "schmurf"), -] - - -@pytest.mark.parametrize("entity_class, base_url, record_id", get_invalid_id_parameters) -def test_get_record_incorrect_id(dasked_test_app_with_mocked_core_service, entity_class, base_url, record_id): - response = dasked_test_app_with_mocked_core_service.get( - f"{base_url}/{record_id}", - headers={"data-partition-id": "testing_partition"}, +@pytest.mark.parametrize( + "method, relative_path", + [ + # examples of string that are expected to fail because of id not matching regex + ("GET", "some_random_string"), + ("GET", "some_random_string/versions"), + ("GET", "some_random_string/versions/42"), + ("DELETE", "some_random_string"), + ], +) +@pytest.mark.parametrize("url_entity_base_path", [ + "wells", + "wellbores", + "wellboremarkersets", + "wellboretrajectories", + "welllogs"]) +def test_get_delete_routes_refuse_incorrect_record_id( + app_configurable_with_testclient, method, relative_path, url_entity_base_path +): + app, client = app_configurable_with_testclient() + + response = client.request(method=method, url=f'{DDMS_V3_PATH}/{url_entity_base_path}/{relative_path}') + assert response.status_code == 422 + assert "string does not match regex" in response.json()["detail"][0]["msg"] + + +def records_with_version(records): + record_version = {} + for r in records: + previous_version = record_version.setdefault(r.id, 0) + r.version = previous_version+1 + record_version[r.id] = r.version + return records + + +@pytest.mark.parametrize("url_entity_base_path, record_list_fixture", [ + ("wells", "well100_v3_list"), + ("wellbores", "wellbore100_v3_list"), + ("wellboremarkersets", "marker110_v3_list"), + ("wellboretrajectories", "trajectory110_v3_list"), + ("welllogs", "welllog110_v3_list") +]) +def test_get_delete_v3_routes_success(app_configurable_with_testclient, + mock_storage_client_holding_data, + url_entity_base_path, + record_list_fixture, + request): + all_records = request.getfixturevalue(record_list_fixture) # dynamically load fixture + record = all_records[0] # using the first record + model_cls = record.__class__ + _, client = app_configurable_with_testclient( + storage_client_mock=mock_storage_client_holding_data(data=records_with_version(all_records)) ) - assert response.status_code == status.HTTP_400_BAD_REQUEST + # Get latest + response = client.get(DDMS_V3_PATH + f"/{url_entity_base_path}/{record.id}") + assert response.status_code == 200 + record_data = response.json() + retrieved_wr = model_cls.parse_obj(record_data) + assert retrieved_wr == record -@pytest.mark.parametrize("base_url, id, record_obj", tests_parameters) -def test_get_record_success(dasked_test_app_with_mocked_core_service, base_url, id, record_obj): - record_id = record_obj.id - moc = mock.AsyncMock(return_value=record_obj) + # get all versions + response = client.get(DDMS_V3_PATH + f"/{url_entity_base_path}/{record.id}/versions") + assert response.status_code == 200 + first_version = response.json()['versions'][0] - with mock.patch.object(StorageRecordServiceClientMock, "get_record", moc): - # when - response = dasked_test_app_with_mocked_core_service.get( - f"{base_url}/{record_id}", - headers={"data-partition-id": "testing_partition"}, - ) - assert response.status_code == status.HTTP_200_OK - - # then assert storage is called with the proper id and data_partition - moc.assert_called_with(id=id, data_partition_id="testing_partition") + # get first version + response = client.get(DDMS_V3_PATH + f"/{url_entity_base_path}/{record.id}/versions/{first_version}") + assert response.status_code == 200 + retrieved_wr = model_cls.parse_obj(response.json()) + assert retrieved_wr == record - # assert it validates the input object schema - record_obj.validate(response.json()) + # delete + response = client.delete(DDMS_V3_PATH + f"/{url_entity_base_path}/{record.id}") + assert response.status_code == 204 tests_parameters_restricted_record_id = [ @@ -228,10 +229,7 @@ tests_parameters_restricted_well = [ ] -def validation_test_restricted_record_id( - record_id, record_id_to_test, response, ok_response=status.HTTP_200_OK, - error_response=status.HTTP_400_BAD_REQUEST -): +def validation_test_restricted_record_id(record_id, record_id_to_test, response, ok_response, error_response): if record_id != record_id_to_test: assert response.status_code == error_response else: @@ -263,23 +261,15 @@ def test_restricted_record_id( response = dasked_test_app_with_mocked_core_service.post(f"{base_url}", json=[record_to_test]) validation_test_restricted_record_id(record_id, record_id_to_test, response, - error_response=status.HTTP_422_UNPROCESSABLE_ENTITY) - - response = dasked_test_app_with_mocked_core_service.get(f"{base_url}/{record_id_to_test}") - validation_test_restricted_record_id(record_id, record_id_to_test, response) - - response = dasked_test_app_with_mocked_core_service.get(f"{base_url}/{record_id_to_test}/versions") - validation_test_restricted_record_id(record_id, record_id_to_test, response) - - response = dasked_test_app_with_mocked_core_service.get(f"{base_url}/{record_id_to_test}/versions/{version}") - validation_test_restricted_record_id(record_id, record_id_to_test, response) + status.HTTP_200_OK,status.HTTP_422_UNPROCESSABLE_ENTITY) if base_url == "/ddms/v3/welllogs" or base_url == "/ddms/v3/wellboretrajectories": # Session response = dasked_test_app_with_mocked_core_service.post( f"{base_url}/{record_id_to_test}/sessions", json={"fromVersion": 11351351, "mode": "update"} ) - validation_test_restricted_record_id(record_id, record_id_to_test, response) + validation_test_restricted_record_id(record_id, record_id_to_test, response, + status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST) session_id = "56df654df654df65" response = dasked_test_app_with_mocked_core_service.post( @@ -287,15 +277,18 @@ def test_restricted_record_id( data=chunk.to_json(orient="split"), headers={"Content-Type": "application/json"}, ) - validation_test_restricted_record_id(record_id, record_id_to_test, response) + validation_test_restricted_record_id(record_id, record_id_to_test, response, + status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST) response = dasked_test_app_with_mocked_core_service.get(f"{base_url}/{record_id_to_test}/sessions") - validation_test_restricted_record_id(record_id, record_id_to_test, response) + validation_test_restricted_record_id(record_id, record_id_to_test, response, + status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST) response = dasked_test_app_with_mocked_core_service.get( f"{base_url}/{record_id_to_test}/sessions/{session_id}" ) - validation_test_restricted_record_id(record_id, record_id_to_test, response) + validation_test_restricted_record_id(record_id, record_id_to_test, response, + status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST) # Data moc_record = Record( @@ -321,17 +314,20 @@ def test_restricted_record_id( response = dasked_test_app_with_mocked_core_service.post( f"{base_url}/{record_id_to_test}/data", data=data, headers=headers ) - validation_test_restricted_record_id(record_id, record_id_to_test, response) + validation_test_restricted_record_id(record_id, record_id_to_test, response, + status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST) response = dasked_test_app_with_mocked_core_service.get( f"{base_url}/{record_id_to_test}/data?orient=split", headers={"Accept": "application/json"} ) - validation_test_restricted_record_id(record_id, record_id_to_test, response) + validation_test_restricted_record_id(record_id, record_id_to_test, response, + status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST) response = dasked_test_app_with_mocked_core_service.get( f"{base_url}/{record_id_to_test}/versions/{version}/data" ) - validation_test_restricted_record_id(record_id, record_id_to_test, response) + validation_test_restricted_record_id(record_id, record_id_to_test, response, + status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST) tests_parameters_record_ids = [ diff --git a/tests/unit/routers/ddms_v3/delete_ddms_v3_test.py b/tests/unit/routers/ddms_v3/delete_ddms_v3_test.py index c910bb73..d667f5e4 100644 --- a/tests/unit/routers/ddms_v3/delete_ddms_v3_test.py +++ b/tests/unit/routers/ddms_v3/delete_ddms_v3_test.py @@ -98,9 +98,11 @@ versions = [1972724675421999416275969243854301388, 19727246917190413694256303710 v3_entities = ["welllogs", "wellboretrajectories"] -@pytest.mark.parametrize("v3_entity", v3_entities) -def test_delete_purge_record(client_delete, logger_fixture, v3_entity): - record_id = f'opendes:work-product-component--{v3_entity}:00001234' +@pytest.mark.parametrize("url_base_path, record_id", [ + ("/ddms/v3/welllogs", "opendes:work-product-component--WellLog:00001234"), + ("/ddms/v3/wellboretrajectories", "opendes:work-product-component--WellboreTrajectory:00001234") +]) +def test_delete_purge_record(client_delete, logger_fixture, url_base_path, record_id): record_versions = RecordVersions(record_id=record_id, versions=versions) mock_storage_service_delete_record = mock.AsyncMock(return_value=status.HTTP_204_NO_CONTENT, @@ -120,7 +122,7 @@ def test_delete_purge_record(client_delete, logger_fixture, v3_entity): mock.patch.object(BlobStorageMock, "delete", mock_blob_storage), \ mock.patch.object(delete_bulk_data, "_get_bulk_uri_from_version", mock_get_bulk_uri_from_version): response = client_delete.delete( - f"/ddms/v3/{v3_entity}/{record_id}?purge=true", + f"{url_base_path}/{record_id}?purge=true", headers={"data-partition-id": "testing_partition"}, ) assert response.status_code == status.HTTP_204_NO_CONTENT -- GitLab From 9e389132aa0d5cb41beacbd037dd7ef87a711d43 Mon Sep 17 00:00:00 2001 From: "yvernet@slb.com" Date: Mon, 11 Apr 2022 14:17:17 +0200 Subject: [PATCH 2/5] fix imports --- app/routers/bulk/bulk_routes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/routers/bulk/bulk_routes.py b/app/routers/bulk/bulk_routes.py index 7153aba3..42851cb1 100644 --- a/app/routers/bulk/bulk_routes.py +++ b/app/routers/bulk/bulk_routes.py @@ -43,14 +43,14 @@ from app.routers.bulk.utils import (with_dask_blob_storage, get_data_consistency_checks) # imports for session manipulation -from app.persistence.sessions_storage import ( - Session, +from app.bulk_persistence import ( SessionException, SessionState, SessionUpdateMode, SessionInternal, CommitSessionResponse ) + from app.routers.sessions import ( UpdateSessionState, UpdateSessionStateValue, -- GitLab From 61ad1a70a84f83cf2290ef80096664f861025015 Mon Sep 17 00:00:00 2001 From: "yvernet@slb.com" Date: Mon, 11 Apr 2022 14:17:31 +0200 Subject: [PATCH 3/5] review comment --- app/routers/ddms_v3/markerset_ddms_v3.py | 8 +++----- app/routers/ddms_v3/well_ddms_v3.py | 2 +- app/routers/ddms_v3/wellbore_ddms_v3.py | 2 +- app/routers/ddms_v3/wellbore_trajectory_ddms_v3.py | 2 +- app/routers/ddms_v3/welllog_ddms_v3.py | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/routers/ddms_v3/markerset_ddms_v3.py b/app/routers/ddms_v3/markerset_ddms_v3.py index 65979d9a..cae80e2e 100644 --- a/app/routers/ddms_v3/markerset_ddms_v3.py +++ b/app/routers/ddms_v3/markerset_ddms_v3.py @@ -47,13 +47,11 @@ router = APIRouter(route_class=TracingRoute) async def get_wellbore_markerset_osdu( wellboremarkersetid: WellboreMarkerSetId, request: Request, ctx: Context = Depends(get_ctx) ) -> WellboreMarkerSet: - # TODO version is dropped here, it would be better to either return an error or return the version not the latest - wellboremarkersetid, _ = split_record_id_version(wellboremarkersetid) + # Note: version is dropped here + record_id, _ = split_record_id_version(wellboremarkersetid) storage_client = await get_storage_record_service(ctx) - wellboreMarkerset_record = await storage_client.get_record( - id=wellboremarkersetid, data_partition_id=ctx.partition_id - ) + wellboreMarkerset_record = await storage_client.get_record(id=record_id, data_partition_id=ctx.partition_id) DMSV3RouterUtils.raise_if_not_osdu_right_entity_kind(wellboreMarkerset_record, request.state) return from_record(WellboreMarkerSet, wellboreMarkerset_record) diff --git a/app/routers/ddms_v3/well_ddms_v3.py b/app/routers/ddms_v3/well_ddms_v3.py index a4f359c2..086f5cad 100644 --- a/app/routers/ddms_v3/well_ddms_v3.py +++ b/app/routers/ddms_v3/well_ddms_v3.py @@ -46,7 +46,7 @@ router = APIRouter(route_class=TracingRoute) }, ) async def get_well_osdu(wellid: WellId, ctx: Context = Depends(get_ctx)) -> Well: - # TODO version is dropped here, it would be better to either return an error or return the version not the latest + # Note: version is dropped here record_id, _ = split_record_id_version(wellid) storage_client = await get_storage_record_service(ctx) well_record = await storage_client.get_record(id=record_id, data_partition_id=ctx.partition_id) diff --git a/app/routers/ddms_v3/wellbore_ddms_v3.py b/app/routers/ddms_v3/wellbore_ddms_v3.py index 5058dc54..22e91e5c 100644 --- a/app/routers/ddms_v3/wellbore_ddms_v3.py +++ b/app/routers/ddms_v3/wellbore_ddms_v3.py @@ -44,7 +44,7 @@ router = APIRouter(route_class=TracingRoute) }, ) async def get_wellbore_osdu(wellboreid: WellboreId, ctx: Context = Depends(get_ctx)) -> Wellbore: - # TODO version is dropped here, it would be better to either return an error or return the version not the latest + # Note: version is dropped here record_id, _ = split_record_id_version(wellboreid) storage_client = await get_storage_record_service(ctx) well_record = await storage_client.get_record(id=record_id, data_partition_id=ctx.partition_id) diff --git a/app/routers/ddms_v3/wellbore_trajectory_ddms_v3.py b/app/routers/ddms_v3/wellbore_trajectory_ddms_v3.py index 00e30d6d..6f502d27 100644 --- a/app/routers/ddms_v3/wellbore_trajectory_ddms_v3.py +++ b/app/routers/ddms_v3/wellbore_trajectory_ddms_v3.py @@ -51,7 +51,7 @@ async def get_wellbore_trajectory_osdu( wellboretrajectoryid: WellboreTrajectoryId, request: Request, ctx: Context = Depends(get_ctx) ) -> WellboreTrajectory: storage_client = await get_storage_record_service(ctx) - # TODO version is dropped here, it would be better to either return an error or return the version not the latest + # Note: version is dropped here wellboretrajectoryid, _ = split_record_id_version(wellboretrajectoryid) wellboreTrajectory_record = await storage_client.get_record( diff --git a/app/routers/ddms_v3/welllog_ddms_v3.py b/app/routers/ddms_v3/welllog_ddms_v3.py index a65512f7..6de8ab60 100644 --- a/app/routers/ddms_v3/welllog_ddms_v3.py +++ b/app/routers/ddms_v3/welllog_ddms_v3.py @@ -55,7 +55,7 @@ async def get_welllog_osdu( welllogid: WellLogId, request: Request, ctx: Context = Depends(get_ctx) ) -> WellLog: storage_client = await get_storage_record_service(ctx) - # TODO version is dropped here, it would be better to either return an error or return the version not the latest + # Note: version is dropped here welllogid, _ = split_record_id_version(welllogid) welllog_record = await storage_client.get_record( -- GitLab From 06925eee6fafcc00f8f8b1eccde13ff1c4a5319e Mon Sep 17 00:00:00 2001 From: "yvernet@slb.com" Date: Mon, 11 Apr 2022 14:46:51 +0200 Subject: [PATCH 4/5] update test expected response code 400->422 --- tests/integration/functional/tests/test_crud_v3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/functional/tests/test_crud_v3.py b/tests/integration/functional/tests/test_crud_v3.py index 7bfa9849..b9f1d985 100644 --- a/tests/integration/functional/tests/test_crud_v3.py +++ b/tests/integration/functional/tests/test_crud_v3.py @@ -111,7 +111,7 @@ def test_crud_get_as_record(delfi_id, kind, with_wdms_env): # Get it as osdu wellbore with delfi id result = build_request(f'crud.osdu_{kind}.get_osdu_{kind}').call(with_wdms_env) - result.assert_status_code(400) + result.assert_status_code(422) @pytest.mark.tag('basic', 'crud', 'smoke') -- GitLab From 564170d7bdca890ba1e6eab1a5b326f4dc6ba00f Mon Sep 17 00:00:00 2001 From: "yvernet@slb.com" Date: Mon, 11 Apr 2022 14:47:05 +0200 Subject: [PATCH 5/5] update openapi spec --- spec/generated/openapi.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/spec/generated/openapi.json b/spec/generated/openapi.json index 8a44ce3e..8cc470ce 100644 --- a/spec/generated/openapi.json +++ b/spec/generated/openapi.json @@ -17037,6 +17037,7 @@ "name": "wellboremarkersetid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:work-product-component\\-\\-WellboreMarkerSet:[\\w\\-\\.\\:\\%]+$", "title": "Wellboremarkersetid", "type": "string" } @@ -17091,6 +17092,7 @@ "name": "wellboremarkersetid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:work-product-component\\-\\-WellboreMarkerSet:[\\w\\-\\.\\:\\%]+$", "title": "Wellboremarkersetid", "type": "string" } @@ -17154,6 +17156,7 @@ "name": "wellboremarkersetid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:work-product-component\\-\\-WellboreMarkerSet:[\\w\\-\\.\\:\\%]+$", "title": "Wellboremarkersetid", "type": "string" } @@ -17217,6 +17220,7 @@ "name": "wellboremarkersetid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:work-product-component\\-\\-WellboreMarkerSet:[\\w\\-\\.\\:\\%]+$", "title": "Wellboremarkersetid", "type": "string" } @@ -17463,6 +17467,7 @@ "name": "wellboreid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:master-data\\-\\-Wellbore:[\\w\\-\\.\\:\\%]+$", "title": "Wellboreid", "type": "string" } @@ -17517,6 +17522,7 @@ "name": "wellboreid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:master-data\\-\\-Wellbore:[\\w\\-\\.\\:\\%]+$", "title": "Wellboreid", "type": "string" } @@ -17580,6 +17586,7 @@ "name": "wellboreid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:master-data\\-\\-Wellbore:[\\w\\-\\.\\:\\%]+$", "title": "Wellboreid", "type": "string" } @@ -17643,6 +17650,7 @@ "name": "wellboreid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:master-data\\-\\-Wellbore:[\\w\\-\\.\\:\\%]+$", "title": "Wellboreid", "type": "string" } @@ -18838,6 +18846,7 @@ "name": "wellboretrajectoryid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:work-product-component\\-\\-WellboreTrajectory:[\\w\\-\\.\\:\\%]+$", "title": "Wellboretrajectoryid", "type": "string" } @@ -18902,6 +18911,7 @@ "name": "wellboretrajectoryid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:work-product-component\\-\\-WellboreTrajectory:[\\w\\-\\.\\:\\%]+$", "title": "Wellboretrajectoryid", "type": "string" } @@ -18965,6 +18975,7 @@ "name": "wellboretrajectoryid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:work-product-component\\-\\-WellboreTrajectory:[\\w\\-\\.\\:\\%]+$", "title": "Wellboretrajectoryid", "type": "string" } @@ -19028,6 +19039,7 @@ "name": "wellboretrajectoryid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:work-product-component\\-\\-WellboreTrajectory:[\\w\\-\\.\\:\\%]+$", "title": "Wellboretrajectoryid", "type": "string" } @@ -19990,6 +20002,7 @@ "name": "welllogid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:work-product-component\\-\\-WellLog:[\\w\\-\\.\\:\\%]+$", "title": "Welllogid", "type": "string" } @@ -20054,6 +20067,7 @@ "name": "welllogid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:work-product-component\\-\\-WellLog:[\\w\\-\\.\\:\\%]+$", "title": "Welllogid", "type": "string" } @@ -20117,6 +20131,7 @@ "name": "welllogid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:work-product-component\\-\\-WellLog:[\\w\\-\\.\\:\\%]+$", "title": "Welllogid", "type": "string" } @@ -20180,6 +20195,7 @@ "name": "welllogid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:work-product-component\\-\\-WellLog:[\\w\\-\\.\\:\\%]+$", "title": "Welllogid", "type": "string" } @@ -20354,6 +20370,7 @@ "name": "wellid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:master-data\\-\\-Well:[\\w\\-\\.\\:\\%]+$", "title": "Wellid", "type": "string" } @@ -20408,6 +20425,7 @@ "name": "wellid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:master-data\\-\\-Well:[\\w\\-\\.\\:\\%]+$", "title": "Wellid", "type": "string" } @@ -20471,6 +20489,7 @@ "name": "wellid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:master-data\\-\\-Well:[\\w\\-\\.\\:\\%]+$", "title": "Wellid", "type": "string" } @@ -20534,6 +20553,7 @@ "name": "wellid", "required": true, "schema": { + "pattern": "^[\\w\\-\\.]+:master-data\\-\\-Well:[\\w\\-\\.\\:\\%]+$", "title": "Wellid", "type": "string" } -- GitLab