Commit ee8d45dd authored by Kin Jin Ng's avatar Kin Jin Ng
Browse files

Load router extensions modules

parent 82e9e86e
......@@ -183,6 +183,11 @@ class ConfigurationContainer:
default='300',
factory=lambda x: int(x))
extension_modules: EnvVar = EnvVar(
key='EXTENSION_MODULES',
description="""Comma separated list of extension module names to load.""",
default=None)
_environment_dict: Dict = os.environ
_contextual_loader: Callable = None
......
# Wellbore Domain Services Extensions
> :warning: **This is an alpha feature**
> Implementation changes are expected, and will not be announced.
[[_TOC_]]
## Use cases
### Include new routers
1. Add a new directory `<extension package name>/routers` under `app/extensions`
2. Add a new python module `<router module name>.py`
```
/wellbore-domain-services
- app
|- extensions
|- <extension package name>
|- __init__.py
|- routers
|- <router module name>.py
|- __init__.py
```
3. In `<router module name>.py` include the following global variables.
```python
from fastapi import APIRouter
router = APIRouter()
router_prefix = '<extension router prefix>'
router_tags = ['<extension router tags>']
```
4. Include new service endpoints to `router` in `<router module name>.py`.
- Use `@router.[get|post|delete|put|patch|...]` fastapi decorator to include new methods to the router
- Check WDMS routers implementation under `app/routers`
- The same pattern is encouraged to be used in the extension routers.
```python
from fastapi import APIRouter, Depends, status
from app.utils import Context, get_ctx
from app.model.model_curated import *
from app.clients.storage_service_client import get_storage_record_service
from app.model.model_utils import from_record
router = APIRouter() # From previous step
# E.g.: Including a GET method API
@router.get('/markers/{marker_id}',
response_model=marker,
summary="Get the marker using wks:marker:1.0.4 schema",
description="""Get the Marker object using its **id**.""",
operation_id="get_marker",
responses={status.HTTP_404_NOT_FOUND: {"description": "marker not found"}},
response_model_exclude_unset=True)
async def get_marker(
marker_id: str,
ctx: Context = Depends(get_ctx)
) -> marker:
storage_client = await get_storage_record_service(ctx)
marker_record = await storage_client.get_record(id=marker_id, data_partition_id=ctx.partition_id)
return from_record(marker, marker_record)
```
5. Include the full router module name in `EXTENSION_MODULES` environment variable
E.g.: `app.extensions.<extension package name>.routers.<router module name>`
6. Update the deployment scripts accordingly.
7. Run WDMS service, and check in the logs for the messages
```
Loading `app.extensions.<extension package>.routers.<router module>` extension
Done. `app.extensions.<extension package>.routers.<router module>` loaded
```
8. Verify the new API endpoints were added under the provided tag,
and with the given prefix. `https://{base_url}/api/os-wellbore-ddms/docs/{Tag}`
9. Include test
- Unit tests are to be placed under `tests/unit/extensions/<extension package name>`
- Integration tests are to be placed under `tests/integration/functional/extensions/<extension package name>`
#### Troubleshooting
##### A. Wrong router module configuration
Check step #3 above
```shell
Loading `app.extensions.{}.routers.{}` extension
Failed to load `app.extensions.{}.routers.{}` extension.
Module not configured properly. module 'app.extensions.{}.routers.{}' has no attribute 'router_prefix'
```
##### B. Wrong module name
Review steps #2 and #4 above
```shell
Loading `app.extensions.{}.routers.{}` extension
Failed to load `app.extensions.{}.routers.{}` extension.
Module not found.
No module named 'app.extensions.{}.routers.{}'
```
##### C. Trailing comma in `EXTENSION_MODULES` list
Review step #4 above, make sure there is no trailing comma in the `EXTENSION_MODULES` list.
```shell
Loading `` extension
Failed to load `` extension. Empty module name
```
......@@ -11,6 +11,7 @@
# 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.
import sys
from fastapi import FastAPI, Depends
from fastapi.openapi.utils import get_openapi
......@@ -42,6 +43,9 @@ from app.routers.search import search, fast_search
from app.clients import StorageRecordServiceClient, SearchServiceClient
from app.utils import get_http_client_session, OpenApiHandler, get_wdms_temp_dir
import importlib
base_app = FastAPI()
......@@ -189,3 +193,28 @@ wdms_app.add_middleware(CreateBasicContextMiddleware, injector=app_injector)
# adding exception handling
add_exception_handlers(wdms_app)
# Load extensions [alpha version]
discovered_extensions = []
extension_modules = Config.extension_modules.value
if extension_modules:
discovered_extensions = extension_modules.split(',')
for name in discovered_extensions:
try:
print(f'Loading `{name}` extension')
module = importlib.import_module(name)
wdms_app.include_router(module.router, prefix=module.router_prefix, tags=module.router_tags,
dependencies=[
Depends(require_data_partition_id, use_cache=False),
Depends(require_opendes_authorized_user, use_cache=False)])
print(f'\tDone. `{name}` loaded')
except AttributeError as error:
print(f'\tFailed to load `{name}` extension. Module not configured properly. {error}')
except ModuleNotFoundError as error:
print(f'\tFailed to load `{name}` extension. Module not found. {error}')
except ValueError as error:
print(f'\tFailed to load `{name}` extension. {error}')
except:
print(f'\tFailed to load `{name}` extension. {sys.exc_info()[0]}')
\ No newline at end of file
......@@ -23,6 +23,9 @@ data:
USE_PARTITION_SERVICE: {{ .Values.configMap.data.usePartitionService }}
AZ_LOGGER_LEVEL: {{ .Values.configMap.data.loggerLevel }}
ENVIRONMENT_NAME: {{ .Values.configMap.data.environmentName }}
{{ if .Values.configMap.data.extensionModules }}
EXTENSION_MODULES: {{ .Values.configMap.data.extensionModules }}
{{ end }}
kind: ConfigMap
metadata:
annotations:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment