Commit cc901a54 authored by Yunhua Koglin's avatar Yunhua Koglin
Browse files

Merge remote-tracking branch 'origin/master' into dev

parents 27839a93 60250cea
......@@ -27,14 +27,14 @@ variables:
AWS_TEST_SUBDIR: tests/aws-test/build-aws
AWS_INT_TEST_TYPE: python
OSDU_GCP_PROJECT_NAME: nice-etching-277309
OSDU_GCP_CLUSTER: asm-primary
OSDU_GCP_ZONE: us-central1-c
OSDU_GCP_SERVICE: wellbore
OSDU_GCP_HELM_DEPLOYMENT_DIR: devops/gcp/osdu-helm
OSDU_GCP_SERVICE_PATH: /api/os-wellbore-ddms
OSDU_GCP_HEALTH_PATH: /api/os-wellbore-ddms/healthz
OSDU_GCP_VENDOR: gcp
OSDU_GCP_HELM_PACKAGE_CHARTS: "devops/gcp/deploy devops/gcp/configmap"
OSDU_GCP_HELM_CONFIG_SERVICE_VARS: "--set data.os_wellbore_ddms_data_project_id=$OSDU_GCP_PROJECT --set data.service_host_search=$OSDU_GCP_SERVICE_HOST_SEARCH --set data.sa_key=$OSDU_GCP_INTEGRATION_TESTER"
OSDU_GCP_HELM_DEPLOYMENT_SERVICE_VARS: "--set data.image=$CI_REGISTRY_IMAGE/osdu-gcp:$CI_COMMIT_SHORT_SHA"
OSDU_GCP_HELM_CONFIG_SERVICE: wellbore-config
OSDU_GCP_HELM_DEPLOYMENT_SERVICE: wellbore-deploy
OSDU_GCP_INT_TEST_TYPE: python
include:
- project: "osdu/platform/ci-cd-pipelines"
......@@ -50,7 +50,7 @@ include:
file: "scanners/gitlab-ultimate.yml"
- project: "osdu/platform/ci-cd-pipelines"
file: "cloud-providers/osdu-gcp-stateful-k8s.yml"
file: "cloud-providers/osdu-gcp-gke.yml"
- project: "osdu/platform/ci-cd-pipelines"
file: "cloud-providers/ibm-wellbore.yml"
......@@ -92,7 +92,7 @@ containerize:
- docker push $IMAGE_TAG
- docker push $CI_REGISTRY_IMAGE:latest
osdu-gcp-containerize:
osdu-gcp-containerize-gitlab:
stage: containerize
image: docker:19.03
cache: {}
......@@ -101,8 +101,8 @@ osdu-gcp-containerize:
variables:
- $OSDU_GCP == 'true'
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE/osdu-gcp-$OSDU_GCP_SERVICE:$CI_COMMIT_SHA
IMAGE_TAG_COMMUNITY: $CI_REGISTRY_IMAGE/osdu-gcp-$OSDU_GCP_SERVICE:gcp-community
IMAGE_TAG: $CI_REGISTRY_IMAGE/osdu-gcp:$CI_COMMIT_SHORT_SHA
IMAGE_TAG_LATEST: $CI_REGISTRY_IMAGE/osdu-gcp:latest
script:
- echo ---- ---- ---- SYSTEM DEPENDENCIES ---- ---- ----
- apk update
......@@ -119,11 +119,11 @@ osdu-gcp-containerize:
- commit_branch=$commit_id
- echo $commit_branch
- echo ---- ---- BUILD IMAGE
- docker build -t $IMAGE_TAG -t $IMAGE_TAG_COMMUNITY -t=$CI_REGISTRY_IMAGE:latest --rm . -f ./build/Dockerfile --build-arg PIP_WHEEL_DIR=python-packages --build-arg build_date="$current_utc_date" --build-arg build_number=$commit_id --build-arg commit_id=$commit_id --build-arg build_origin="Gitlab" --build-arg commit_branch=$commit_branch
- docker build -t $IMAGE_TAG -t $IMAGE_TAG_LATEST --rm . -f ./build/Dockerfile --build-arg PIP_WHEEL_DIR=python-packages --build-arg build_date="$current_utc_date" --build-arg build_number=$commit_id --build-arg commit_id=$commit_id --build-arg build_origin="Gitlab" --build-arg commit_branch=$commit_branch
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- echo ---- ---- PUSH IMAGE
- docker push $IMAGE_TAG
- docker push $IMAGE_TAG_COMMUNITY
- docker push $IMAGE_TAG_LATEST
osdu-gcp-containerize-for-release:
stage: containerize
......@@ -132,10 +132,9 @@ osdu-gcp-containerize-for-release:
tags: ["osdu-medium"]
only:
variables:
- $OSDU_GCP == 'true' && $RELEASE =~ /(v)(\d+\.)(\d+\.)\w+/i
- $OSDU_GCP == 'true' && $CI_COMMIT_TAG != null
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE/osdu-gcp-$OSDU_GCP_SERVICE:gcp-$RELEASE
IMAGE_TAG_RELEASE: $CI_REGISTRY_IMAGE/osdu-gcp-$OSDU_GCP_SERVICE:gcp-release
IMAGE_TAG_RELEASE: $CI_REGISTRY_IMAGE/osdu-gcp:$CI_COMMIT_TAG
script:
- echo ---- ---- ---- SYSTEM DEPENDENCIES ---- ---- ----
- apk update
......@@ -152,16 +151,16 @@ osdu-gcp-containerize-for-release:
- commit_branch=$commit_id
- echo $commit_branch
- echo ---- ---- BUILD IMAGE
- docker build -t $IMAGE_TAG -t $IMAGE_TAG_RELEASE -t=$CI_REGISTRY_IMAGE:latest --rm . -f ./build/Dockerfile --build-arg PIP_WHEEL_DIR=python-packages --build-arg build_date="$current_utc_date" --build-arg build_number=$commit_id --build-arg commit_id=$commit_id --build-arg build_origin="Gitlab" --build-arg commit_branch=$commit_branch
- docker build -t $IMAGE_TAG_RELEASE --rm . -f ./build/Dockerfile --build-arg PIP_WHEEL_DIR=python-packages --build-arg build_date="$current_utc_date" --build-arg build_number=$commit_id --build-arg commit_id=$commit_id --build-arg build_origin="Gitlab" --build-arg commit_branch=$commit_branch
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- echo ---- ---- PUSH IMAGE
- docker push $IMAGE_TAG
- docker push $IMAGE_TAG_RELEASE
osdu-gcp-test-python:
extends: .osdu-gcp-variables
stage: integration
image: gcr.io/google.com/cloudsdktool/cloud-sdk
needs: ['osdu-gcp-deploy']
needs: ["osdu-gcp-deploy-deployment"]
only:
variables:
- $OSDU_GCP == 'true' && $OSDU_GCP_INT_TEST_TYPE == 'python'
......@@ -178,17 +177,21 @@ osdu-gcp-test-python:
- cd tests/integration
- echo $OSDU_GCP_INTEGRATION_TESTER | base64 -d > file.json
- gcloud auth activate-service-account --key-file file.json
- gcloud config set project $OSDU_GCP_PROJECT_NAME
- gcloud config set project $OSDU_GCP_PROJECT
- >
python gen_postman_env.py
--token $(gcloud auth print-access-token)
--base_url ${OSDU_GCP_URL}${OSDU_GCP_SERVICE_PATH}
--base_url $OSDU_GCP_WELLBORE_BASE_URL
--cloud_provider $OSDU_GCP_VENDOR
--data_partition $OSDU_GCP_TENANT
--acl_domain $DOMAIN
--legal_tag $OSDU_GCP_LEGAL_TAG
--legal_tag $LEGAL_TAG
- pytest ./functional --environment="./generated/postman_environment.json" --filter-tag=basic
# Disable maven job in gcp common gke pipeline
osdu-gcp-test:
extends:
- .osdu-gcp-variables
# Allow failure on deployments
......
......@@ -14,7 +14,7 @@ The following software have components provided under the terms of this license:
- coverage (from https://coverage.readthedocs.io)
- cryptography (from https://github.com/pyca/cryptography)
- google-api-core (from https://github.com/GoogleCloudPlatform/google-cloud-python)
- google-auth (from https://github.com/GoogleCloudPlatform/google-auth-library-python)
- google-auth (from https://github.com/googleapis/google-auth-library-python)
- google-auth-oauthlib (from https://github.com/GoogleCloudPlatform/google-auth-library-python-oauthlib)
- google-cloud-core (from https://github.com/GoogleCloudPlatform/google-cloud-python)
- google-cloud-monitoring (from https://github.com/GoogleCloudPlatform/google-cloud-python)
......@@ -314,7 +314,7 @@ The following software have components provided under the terms of this license:
- async-timeout (from https://github.com/aio-libs/async_timeout/)
- coverage (from https://coverage.readthedocs.io)
- distributed (from https://distributed.readthedocs.io/en/latest/)
- google-auth (from https://github.com/GoogleCloudPlatform/google-auth-library-python)
- google-auth (from https://github.com/googleapis/google-auth-library-python)
- google-auth-oauthlib (from https://github.com/GoogleCloudPlatform/google-auth-library-python-oauthlib)
- numpy (from http://www.numpy.org)
- pandas (from http://pandas.pydata.org)
......
......@@ -95,7 +95,7 @@ async def post_data(record_id: str,
"In case of JSON the orient must be set accordingly. Support http chunked encoding."
+ REQUIRED_ROLES_WRITE,
operation_id=OPERATION_IDS["chunk_data"],
responses={400: {"error": "Record not found"}}
responses={400: {"description": "Record not found"}}
)
async def post_chunk_data(record_id: str,
session_id: str,
......
......@@ -18,15 +18,17 @@ from odes_search.models import Point, CursorQueryResponse
from app.utils import Context
from .search_v3 import (
SearchQuery,
SearchQueryRequest,
query_request,
query_type,
basic_query_request,
added_relationships_query,
query_request_with_specific_attribute,
OSDU_WELLBORE_KIND,
OSDU_WELLLOG_KIND,
OSDU_WELLBOREMARKERSET_KIND,
WELLBORE_RELATIONSHIP,
REQUIRED_ROLES_READ
REQUIRED_ROLES_READ,
DEFAULT_QUERYREQUEST
)
router = APIRouter()
......@@ -41,8 +43,8 @@ def get_ctx() -> Context:
description=f"""Get all Wellbores IDs object. <p>The wellbore kind is
{OSDU_WELLBORE_KIND} returns all records IDs IDs directly based on existing schemas</p>{REQUIRED_ROLES_READ}""",
response_model=CursorQueryResponse)
async def fastquery_wellbores(body: SearchQuery = None, ctx: Context = Depends(get_ctx)):
return await basic_query_request(query_type, OSDU_WELLBORE_KIND, ctx, body.query)
async def fastquery_wellbores(body: SearchQueryRequest = DEFAULT_QUERYREQUEST, ctx: Context = Depends(get_ctx)):
return await query_request(query_type, OSDU_WELLBORE_KIND, ctx, body)
@router.post('/fastquery/wellbores/{wellbore_id}/welllogs',
summary='Query with cursor, search WellLogs IDs by wellbore ID',
......@@ -50,10 +52,10 @@ async def fastquery_wellbores(body: SearchQuery = None, ctx: Context = Depends(g
specific ID will be returned</p>
<p>The LogSet kind is {OSDU_WELLLOG_KIND} returns all records IDs directly based on existing schemas</p>{REQUIRED_ROLES_READ}""",
response_model=CursorQueryResponse)
async def fastquery_welllogs_bywellbore(wellbore_id: str, body: SearchQuery = None,
ctx: Context = Depends(get_ctx)):
query = added_relationships_query(wellbore_id, WELLBORE_RELATIONSHIP, body.query)
return await basic_query_request(query_type, OSDU_WELLLOG_KIND, ctx, query)
async def fastquery_welllogs_bywellbore(wellbore_id: str, body: SearchQueryRequest = DEFAULT_QUERYREQUEST,
ctx: Context = Depends(get_ctx)):
body.query = added_relationships_query(wellbore_id, WELLBORE_RELATIONSHIP, body.query)
return await query_request(query_type, OSDU_WELLLOG_KIND, ctx, body)
@router.post('/fastquery/wellbore/{wellbore_attribute}/welllogs',
......@@ -62,7 +64,7 @@ async def fastquery_welllogs_bywellbore(wellbore_id: str, body: SearchQuery = No
with this specific attribute will be returned</p>
<p>The LogSet kind is {OSDU_WELLLOG_KIND} returns all records IDs directly based on existing schemas</p>{REQUIRED_ROLES_READ}""",
response_model=CursorQueryResponse)
async def fastquery_welllogs_bywellboreattribute(wellbore_attribute: str, body: SearchQuery = None,
async def fastquery_welllogs_bywellboreattribute(wellbore_attribute: str, body: SearchQuery = SearchQuery(query=None),
ctx: Context = Depends(get_ctx)):
return await query_request_with_specific_attribute(query_type, wellbore_attribute, OSDU_WELLBORE_KIND,
OSDU_WELLLOG_KIND, WELLBORE_RELATIONSHIP, ctx,
......@@ -75,7 +77,7 @@ async def fastquery_welllogs_bywellboreattribute(wellbore_attribute: str, body:
specific ID will be returned</p>
<p>The Marker kind is {OSDU_WELLBOREMARKERSET_KIND} returns all records IDs directly based on existing schemas</p>{REQUIRED_ROLES_READ}""",
response_model=CursorQueryResponse)
async def fastquery_markers_bywellbore(wellbore_id: str, body: SearchQuery = None,
async def fastquery_markers_bywellbore(wellbore_id: str, body: SearchQueryRequest = DEFAULT_QUERYREQUEST,
ctx: Context = Depends(get_ctx)):
query = added_relationships_query(wellbore_id, WELLBORE_RELATIONSHIP, body.query)
return await basic_query_request(query_type, OSDU_WELLBOREMARKERSET_KIND, ctx, query)
body.query = added_relationships_query(wellbore_id, WELLBORE_RELATIONSHIP, body.query)
return await query_request(query_type, OSDU_WELLBOREMARKERSET_KIND, ctx, body)
......@@ -15,12 +15,17 @@
from fastapi import APIRouter, Depends
from odes_search.models import (
QueryRequest,
CursorQueryResponse)
CursorQueryResponse,
CursorQueryRequest,
BaseModel,
Field,
Optional)
from app.clients.search_service_client import get_search_service
from ..common_parameters import REQUIRED_ROLES_READ
from app.routers.common_parameters import REQUIRED_ROLES_READ
from app.utils import Context
import app.routers.search.search_wrapper as search_wrapper
from .search import (
LIMIT,
query_type,
SearchQuery,
get_ctx,
......@@ -34,9 +39,43 @@ router = APIRouter()
OSDU_WELLBORE_KIND = '*:wks:master-data--Wellbore:*'
OSDU_WELLLOG_KIND = '*:wks:work-product-component--WellLog:*'
OSDU_WELLBOREMARKERSET_KIND = '*:wks:work-product-component--WellboreMarkerSet:*'
OSDU_WELLBORETRAJECTORY_KIND = '*:wks:work-product-component--WellboreTrajectory:*'
WELLBORE_RELATIONSHIP = "WellboreID"
class SearchQueryRequest(BaseModel):
# Used by as input, w/o kind, etc...
limit: "Optional[int]" = Field(None, alias="limit")
query: "Optional[str]" = Field(None, alias="query")
cursor: "Optional[str]" = Field(None, alias="cursor")
offset: "Optional[int]" = Field(None, alias="offset")
SearchQueryRequest.update_forward_refs()
DEFAULT_SEARCHQUERYREQUEST = SearchQueryRequest(limit=None, query=None, cursor=None, offset=None)
class SimpleCursorQueryRequest(BaseModel):
# Used by as input, w/o kind, etc...
limit: "Optional[int]" = Field(None, alias="limit")
query: "Optional[str]" = Field(None, alias="query")
cursor: "Optional[str]" = Field(None, alias="cursor")
SimpleCursorQueryRequest.update_forward_refs()
DEFAULT_CURSORQUERYREQUEST = SimpleCursorQueryRequest(limit=None, query=None, cursor=None)
class SimpleOffsetQueryRequest(BaseModel):
limit: "Optional[int]" = Field(None, alias="limit")
query: "Optional[str]" = Field(None, alias="query")
offset: "Optional[int]" = Field(None, alias="offset")
SimpleOffsetQueryRequest.update_forward_refs()
DEFAULT_QUERYREQUEST = SimpleOffsetQueryRequest(limit=None, query=None, offset=None)
def create_relationships_id_str(data_type: str, id: str):
return f'data.{data_type}:\"{id}\"'
......@@ -87,12 +126,73 @@ async def query_request_with_specific_attribute(query_type: str, attribute: str,
query_request=query_request)
def update_query_with_names_based_search(names: str = None, user_query: str = None, name_field = "data.FacilityName") -> str:
if names is None:
return user_query
generated_query = f"{name_field}:{names}"
return added_query(generated_query, user_query)
def escape_forbidden_characters_for_search(input_str: str) -> str:
# Reserved character are listed here https://community.opengroup.org/osdu/documentation/-/blob/master/platform/tutorials/core-services/SearchService.md
# ? and * are allowed for wildcard search
if input_str is None:
return None
reserved_char_list = ['+', '-', '=', '>', '<', '!', '(', ')', '{', '}', '[', ']', '^', '"', '~',
':', '\\', '/']
def escape_char(input_char: str, reserved_char_list: [str]) -> str:
return input_char if input_char not in reserved_char_list else f"\\{input_char}"
result_str = ''.join([escape_char(char, reserved_char_list) for char in input_str])
return result_str
async def query_request_with_cursor(query_type: str, kind: str, ctx: Context, query: SimpleCursorQueryRequest = None):
returned_fields = query_type_returned_fields(query_type)
query_request = CursorQueryRequest(kind=kind,
limit=query.limit or LIMIT,
query=query.query,
returnedFields=[returned_fields],
cursor=query.cursor)
client = await get_search_service(ctx)
return await client.query_with_cursor(
data_partition_id=ctx.partition_id,
cursor_query_request=query_request)
async def query_request_with_offset(query_type: str, kind: str, ctx: Context, query: SimpleOffsetQueryRequest = None):
returned_fields = query_type_returned_fields(query_type)
query_request = QueryRequest(kind=kind,
limit=query.limit or LIMIT,
query=query.query,
returnedFields=[returned_fields],
offset=query.offset)
client = await get_search_service(ctx)
return await client.query(
data_partition_id=ctx.partition_id,
query_request=query_request)
async def query_request(query_type: str, kind: str, ctx: Context, query: SearchQueryRequest = None):
# use offset if not not none else use cursor
query_as_dict = query.dict(exclude_none=True, exclude_unset=True)
if query.offset is not None:
cursor_query = SimpleOffsetQueryRequest(**query_as_dict)
return await query_request_with_offset(query_type, kind, ctx, cursor_query)
cursor_query = SimpleCursorQueryRequest(**query_as_dict)
return await query_request_with_cursor(query_type, kind, ctx, cursor_query)
@router.post('/query/wellbores', summary='Query with cursor, get wellbores',
description=f"""Get all Wellbores object. <p>The wellbore kind is {OSDU_WELLBORE_KIND}
returns all records directly based on existing schemas</p>{REQUIRED_ROLES_READ}""",
response_model=CursorQueryResponse)
async def query_wellbores(body: SearchQuery = None, ctx: Context = Depends(get_ctx)):
return await basic_query_request_with_cursor(query_type, OSDU_WELLBORE_KIND, ctx, body.query)
async def query_wellbores(body: SearchQueryRequest = DEFAULT_QUERYREQUEST, ctx: Context = Depends(get_ctx)):
return await query_request(query_type, OSDU_WELLBORE_KIND, ctx, body)
@router.post('/query/wellbores/{wellboreId}/welllogs', summary='Query with cursor, search WellLogs by wellbore ID',
......@@ -100,10 +200,10 @@ async def query_wellbores(body: SearchQuery = None, ctx: Context = Depends(get_c
specific ID will be returned</p>
<p>The WellLogs kind is {OSDU_WELLLOG_KIND} returns all records directly based on existing schemas</p>{REQUIRED_ROLES_READ}""",
response_model=CursorQueryResponse)
async def query_welllogs_bywellbore(wellboreId: str, body: SearchQuery = None,
async def query_welllogs_bywellbore(wellboreId: str, body: SearchQueryRequest = DEFAULT_QUERYREQUEST,
ctx: Context = Depends(get_ctx)):
query = added_relationships_query(wellboreId, WELLBORE_RELATIONSHIP, body.query)
return await basic_query_request(query_type, OSDU_WELLLOG_KIND, ctx, query)
body.query = added_relationships_query(wellboreId, WELLBORE_RELATIONSHIP, body.query)
return await query_request(query_type, OSDU_WELLLOG_KIND, ctx, body)
@router.post('/query/wellbore/{wellboreAttribute}/welllogs',
......@@ -112,7 +212,7 @@ async def query_welllogs_bywellbore(wellboreId: str, body: SearchQuery = None,
with this specific attribute will be returned</p>
<p>The WellLogs kind is {OSDU_WELLLOG_KIND} returns all records directly based on existing schemas</p>{REQUIRED_ROLES_READ}""",
response_model=CursorQueryResponse)
async def query_welllogs_bywellboreattribute(wellboreAttribute: str, body: SearchQuery = None,
async def query_welllogs_bywellboreattribute(wellboreAttribute: str, body: SearchQuery = SearchQuery(query=None),
ctx: Context = Depends(get_ctx)):
return await query_request_with_specific_attribute(query_type, wellboreAttribute, OSDU_WELLBORE_KIND,
OSDU_WELLLOG_KIND,
......@@ -126,7 +226,7 @@ async def query_welllogs_bywellboreattribute(wellboreAttribute: str, body: Searc
specific ID will be returned</p>
<p>The Wellbore Markerset kind is {OSDU_WELLBOREMARKERSET_KIND} returns all records directly based on existing schemas</p>{REQUIRED_ROLES_READ}""",
response_model=CursorQueryResponse)
async def query_markers_bywellbore(wellboreId: str, body: SearchQuery = None,
async def query_markers_bywellbore(wellboreId: str, body: SearchQueryRequest = DEFAULT_QUERYREQUEST,
ctx: Context = Depends(get_ctx)):
query = added_relationships_query(wellboreId, WELLBORE_RELATIONSHIP, body.query)
return await basic_query_request(query_type, OSDU_WELLBOREMARKERSET_KIND, ctx, query)
body.query = added_relationships_query(wellboreId, WELLBORE_RELATIONSHIP, body.query)
return await query_request(query_type, OSDU_WELLBOREMARKERSET_KIND, ctx, body)
# Copyright 2021 Schlumberger
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from fastapi import APIRouter, Depends
from odes_search.models import CursorQueryResponse
from app.utils import Context
from .search_v3 import (
SearchQueryRequest,
DEFAULT_QUERYREQUEST,
OSDU_WELLBORE_KIND,
OSDU_WELLBORETRAJECTORY_KIND,
OSDU_WELLLOG_KIND,
escape_forbidden_characters_for_search,
update_query_with_names_based_search,
query_request,
added_relationships_query,
WELLBORE_RELATIONSHIP,
get_ctx,
query_type)
from ..common_parameters import REQUIRED_ROLES_READ
router = APIRouter()
@router.post('/query/wellbores', summary='Query with cursor or offset, get wellbores',
description=f"""Get Wellbores object by name. <p>The wellbore kind is {OSDU_WELLBORE_KIND}
returns all records directly based on existing schemas. The query is done on data.FacilityName field</p>{REQUIRED_ROLES_READ}""",
response_model=CursorQueryResponse)
async def query_wellbores_by_name(names: str = None, body: SearchQueryRequest = DEFAULT_QUERYREQUEST,
ctx: Context = Depends(get_ctx)):
names = escape_forbidden_characters_for_search(names)
body.query = update_query_with_names_based_search(names=names, user_query=body.query)
return await query_request(query_type, OSDU_WELLBORE_KIND, ctx, body)
@router.post('/query/wellbores/{wellboreId}/wellboretrajectories',
summary='Query with cursor, search wellbore trajectories by wellbore ID',
description=f"""Get all Wellbore Trajectories objects using its relationship Wellbore ID. <p>All Wellbore Trajectories linked to this
specific ID will be returned</p>
<p>The Wellbore Trajectories kind is {OSDU_WELLBORETRAJECTORY_KIND} returns all records directly based on existing schemas</p>{REQUIRED_ROLES_READ}""",
response_model=CursorQueryResponse)
async def query_trajectories_bywellbore(wellboreId: str, body: SearchQueryRequest = DEFAULT_QUERYREQUEST,
ctx: Context = Depends(get_ctx)):
body.query = added_relationships_query(wellboreId, WELLBORE_RELATIONSHIP, body.query)
return await query_request(query_type, OSDU_WELLBORETRAJECTORY_KIND, ctx, body)
@router.post('/query/welllogs',
summary='Query with cursor, search WellLogs by name and optionally by wellbore ID',
description=f"""Get all WellLogs objects using its name and optionally relationship Wellbore ID.
<p>The WellLogs kind is {OSDU_WELLLOG_KIND} returns all records directly based on existing schemas. The query is done on data.Name field</p>{REQUIRED_ROLES_READ}""",
response_model=CursorQueryResponse)
async def query_welllogs_by_name(names: str = None, wellbore_id: str = None, body: SearchQueryRequest = DEFAULT_QUERYREQUEST,
ctx: Context = Depends(get_ctx)):
if wellbore_id is not None:
body.query = added_relationships_query(wellbore_id, WELLBORE_RELATIONSHIP, body.query)
names = escape_forbidden_characters_for_search(names)
body.query = update_query_with_names_based_search(names=names, user_query=body.query, name_field="data.Name")
return await query_request(query_type, OSDU_WELLLOG_KIND, ctx, body)
# Copyright 2021 Schlumberger
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from fastapi import APIRouter, Depends
from odes_search.models import (
QueryRequest,
CursorQueryResponse,
CursorQueryRequest,
BaseModel,
Field,
Optional)
from app.clients.search_service_client import get_search_service
from app.utils import Context
from .search_v3 import (
added_query,
create_relationships_id_str,
query_type_returned_fields,
OSDU_WELLBORE_KIND,
get_ctx,
query_type)
from ..common_parameters import REQUIRED_ROLES_READ
router = APIRouter()
def update_query_with_names_based_search(names: str = None, user_query: str = None) -> str:
generated_query = f"data.FacilityName:{names}"
return added_query(generated_query, user_query)
def escape_forbidden_characters_for_search(input_str: str) -> str:
# Reserved character are listed here https://community.opengroup.org/osdu/documentation/-/blob/master/platform/tutorials/core-services/SearchService.md
# ? and * are allowed for wildcard search
reserved_char_list = ['+', '-', '=', '>', '<', '!', '(', ')', '{', '}', '[', ']', '^', '"', '~',
':', '\\', '/']
def escape_char(input_char: str, reserved_char_list: [str]) -> str:
return input_char if input_char not in reserved_char_list else f"\\{input_char}"
result_str = ''.join([escape_char(char, reserved_char_list) for char in input_str])
return result_str
def added_relationships_query(id: str, data_type: str, query: str = None):
relationships_id = create_relationships_id_str(data_type, id)
return added_query(relationships_id, query)
class SearchQueryRequest(BaseModel):
# Used by as input, w/o kind, etc...
limit: "Optional[int]" = Field(None, alias="limit")
query: "Optional[str]" = Field(None, alias="query")
cursor: "Optional[str]" = Field(None, alias="cursor")
offset: "Optional[int]" = Field(None, alias="offset")
SearchQueryRequest.update_forward_refs()
DEFAULT_SEARCHQUERYREQUEST = SearchQueryRequest(limit=None, query=None, cursor=None, offset=None)
class SimpleCursorQueryRequest(BaseModel):
# Used by as input, w/o kind, etc...
limit: "Optional[int]" = Field(None, alias="limit")
query: "Optional[str]" = Field(None, alias="query")
cursor: "Optional[str]" = Field(None, alias="cursor")
SimpleCursorQueryRequest.update_forward_refs()
DEFAULT_CURSORQUERYREQUEST = SimpleCursorQueryRequest(limit=None, query=None, cursor=None)
class SimpleOffsetQueryRequest(BaseModel):
limit: "Optional[int]" = Field(None, alias="limit")
query: "Optional[str]" = Field(None, alias="query")
offset: "Optional[int]" = Field(None, alias="offset")
SimpleOffsetQueryRequest.update_forward_refs()
DEFAULT_QUERYREQUEST = SimpleOffsetQueryRequest(limit=None, query=None, offset=None)
async def query_request_with_cursor(query_type: str, kind: str, ctx: Context, query: SimpleCursorQueryRequest = None):
returned_fields = query_type_returned_fields(query_type)
query_request = CursorQueryRequest(kind=kind,
limit=query.limit or 1000,
query=query.query,
returnedFields=[returned_fields],
cursor=query.cursor)
client = await get_search_service(ctx)
return await client.query_with_cursor(
data_partition_id=ctx.partition_id,
cursor_query_request=query_request)
async def query_request_with_offset(query_type: str, kind: str, ctx: Context, query: SimpleOffsetQueryRequest = None):
returned_fields = query_type_returned_fields(query_type)
query_request = QueryRequest(kind=kind,
limit=query.limit or 1000,
query=query.query,
returnedFields=[returned_fields],
offset=query.offset)
client = await get_search_service(ctx)
return await client.query(
data_partition_id=ctx.partition_id,
query_request=query_request)
async def query_request(query_type: str, kind: str, ctx: Context, query: SearchQueryRequest = None):
# use offset if not not none else use cursor
query_as_dict = query.dict(exclude_none=True, exclude_unset=True)
if query.offset is not None:
cursor_query = SimpleOffsetQueryRequest(**query_as_dict)
return await query_request_with_offset(query_type, kind, ctx, cursor_query)
cursor_query = SimpleCursorQueryRequest(**query_as_dict)
return await query_request_with_cursor(query_type, kind, ctx, cursor_query)
@router.post('/query/wellbores/byname', summary='Query with cursor or offset, get wellbores',
description=f"""Get Wellbores object by name. <p>The wellbore kind is {OSDU_WELLBORE_KIND}
returns all records directly based on existing schemas. The query is done on data.FacilityName field</p>{REQUIRED_ROLES_READ}""",