Files
OSIT-AE-API-FastAPI/app/routers/lookup_v3.py

97 lines
3.8 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Path, Query, Response, status
from typing import List, Optional
import json
import logging
from app.lib_general_v3 import AccountContext, get_account_context
from app.methods.lookup_methods import get_lookup_list_v3, resolve_lookup_v3
from app.methods.site_methods import load_site_obj
from app.models.response_models import Resp_Body_Base, mk_resp
from app.object_definitions.lookups import lu_obj_li
log = logging.getLogger(__name__)
router = APIRouter()
@router.get("/{lu_type}/list", response_model=Resp_Body_Base)
async def get_v3_lookup_list(
lu_type: str = Path(..., min_length=2, max_length=50),
for_type: Optional[str] = Query(None, min_length=2, max_length=50),
for_id: Optional[int] = Query(None),
site_id: Optional[str] = Query(None, min_length=8, max_length=22),
include_disabled: bool = Query(False),
account_ctx: AccountContext = Depends(get_account_context),
response: Response = Response
):
"""
Returns a hierarchical, ranked, and deduplicated list of lookup records.
Supports Object/Account overrides, negative shadowing, and Site Whitelist policies.
"""
v3_key = f"lu_v3_{lu_type}"
if v3_key not in lu_obj_li:
return mk_resp(data=False, status_code=400, response=response, status_message=f"Lookup type '{lu_type}' not supported in V3.")
# Phase 2: Whitelist Policy Injection
whitelist = None
if site_id:
if site_obj := load_site_obj(site_id=site_id, model_as_dict=True):
# Check if this site belongs to the current account context
# NOTE: site_obj.get('account_id') returns the RANDOM string ID in V3 models
if site_obj.get('account_id') == account_ctx.account_id_random:
cfg = site_obj.get('cfg_json')
if isinstance(cfg, str):
try: cfg = json.loads(cfg)
except: cfg = {}
if isinstance(cfg, dict):
policy = cfg.get('lookup_policy', {})
whitelist = policy.get(lu_type)
else:
return mk_resp(data=False, status_code=403, response=response, status_message="Site does not belong to the authorized account.")
results = get_lookup_list_v3(
lu_type=lu_type,
account_ctx=account_ctx,
for_type=for_type,
for_id=for_id,
include_disabled=include_disabled,
whitelist=whitelist
)
if not results and not include_disabled:
return mk_resp(data=[], status_code=200, response=response, status_message="No active records found.")
return mk_resp(data=results)
@router.get("/{lu_type}/resolve", response_model=Resp_Body_Base)
async def resolve_v3_lookup(
lu_type: str = Path(..., min_length=2, max_length=50),
q: str = Query(..., min_length=1, description="The code, group, or identity to resolve."),
site_id: Optional[str] = Query(None, min_length=8, max_length=22),
account_ctx: AccountContext = Depends(get_account_context),
response: Response = Response
):
"""
Resolves an identity string to the highest-priority hierarchical match.
"""
v3_key = f"lu_v3_{lu_type}"
if v3_key not in lu_obj_li:
return mk_resp(data=False, status_code=400, response=response, status_message=f"Lookup type '{lu_type}' not supported in V3.")
# TODO: Add whitelist support for resolve if needed.
# For now, resolve uses the full ranked list.
identity_fields = lu_obj_li[v3_key].get("searchable_fields", ["group"])
result = resolve_lookup_v3(
lu_type=lu_type,
query=q,
account_ctx=account_ctx,
identity_fields=identity_fields
)
if not result:
return mk_resp(data=None, status_code=404, response=response, status_message=f"Could not resolve '{q}' for lookup '{lu_type}'.")
return mk_resp(data=result)