Skip to content

Commit

Permalink
Merge pull request #96 from kbaseapps/fix-report-fetch-test
Browse files Browse the repository at this point in the history
UIP-49 Fix report fetch test
  • Loading branch information
briehl authored Jun 11, 2024
2 parents 808f85c + 9c2a61f commit 21185eb
Show file tree
Hide file tree
Showing 18 changed files with 517 additions and 322 deletions.
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ COPY ./ /kb/module
RUN mkdir -p /kb/module/work
WORKDIR /kb/module

RUN pip install --upgrade pip && pip install -r requirements.txt
RUN pip install --upgrade pip && \
pip install -r requirements.txt && \
pip install -r requirements-dev.txt

ENV PYTHONPATH="/kb/module/lib:$PYTHONPATH"

Expand Down
4 changes: 2 additions & 2 deletions lib/NarrativeService/NarrativeServiceImpl.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from NarrativeService.DynamicServiceCache import DynamicServiceClient
from NarrativeService.NarrativeListUtils import NarrativeListUtils, NarratorialUtils
from NarrativeService.NarrativeManager import NarrativeManager
from NarrativeService.ReportFetcher import ReportFetcher
from NarrativeService.reportfetcher import ReportFetcher
from NarrativeService.SearchServiceClient import SearchServiceClient
from NarrativeService.sharing.sharemanager import ShareRequester

Expand Down Expand Up @@ -860,7 +860,7 @@ def get_narrative_doc(self, ctx, params):
"""
Intended to return data of previous versions of a given narrative in the same format returned from Search.
Formats a call to workspace service to fit the appropriate schema that is intended for use in UI displays
in the narrative navigator. Raises error is "narrative_upa" param is not in specified <workspace_id/obj_id/version> format.
in the narrative navigator. Raises error is "narrative_upa" param is not in specified <workspace_id/obj_id/version> format.
Note that this method is currently to support the UI only, and does not return the full result of a search call,
and the following fields are omitted: boolean copied, boolean is_narratorial, boolean is_temporary, string obj_name, string obj_type_module,
string obj_type_version, list<string> tags.
Expand Down
51 changes: 0 additions & 51 deletions lib/NarrativeService/ReportFetcher.py

This file was deleted.

72 changes: 72 additions & 0 deletions lib/NarrativeService/reportfetcher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from NarrativeService.ServiceUtils import ServiceUtils

from lib.installed_clients.WorkspaceClient import Workspace

REPORT_TYPE: str = "KBaseReport.Report"
class ReportFetcher:
def __init__(self, ws_client: Workspace) -> None:
self.ws_client = ws_client

def find_report_from_object(self, upa: str) -> dict[str, list|str]:
"""
Given the UPA of an object, this attempts to find any reports that it references.
If the object doesn't have any referencing reports, but it was a copy of another
object, this tries to find the copy's reports.
"""
# TODO: make sure upa's real.

# first, fetch object references (without data)
ref_list = self.ws_client.list_referencing_objects([{"ref": upa}])[0]
# scan it for a report.
# if we find at least one, return them
# if we find 0, test if it's a copy, and search upstream.
report_upas = [
ServiceUtils.object_info_to_object(ref_info)["ref"]
for ref_info in ref_list
if len(ref_info) and REPORT_TYPE in ref_info[2]
]
if len(report_upas):
return self.build_output(upa, report_upas)
return self.find_report_from_copy_source(upa)

def find_report_from_copy_source(self, upa: str) -> dict[str, list|str]:
"""
Fetch the info about this object. If it's a copy, run find_report_from_object on its source.
If it's not, return an error state, or just an empty list for the upas.
"""
obj_data = self.ws_client.get_objects2({"objects": [{"ref": upa}], "no_data": 1})["data"][0]
if obj_data.get("copy_source_inaccessible", 0) == 1:
err = "No report found. This object is a copy, and its source is inaccessible."
return self.build_output(upa, [], inaccessible=1, error=err)
if "copied" in obj_data:
return self.find_report_from_object(obj_data["copied"])
return self.build_output(upa, [])

def build_output(
self,
upa: str,
report_upas: list[str] | None=None,
inaccessible: int=0,
error: str | None=None
) -> dict[str, list|str|int]:
"""
Builds the output dictionary for the report fetching.
Definitely has keys:
report_upas - list[str] - list of report object UPAs, if they exist
object_upa - str - the UPA of the object referencing the reports.
Might have keys:
inaccessible - int (1) if the given object in object_upa was copied, and its source
(which might reference reports) is inaccessible
error - str - if an error occurred.
"""
if report_upas is None:
report_upas = []
ret_val = {
"report_upas": report_upas,
"object_upa": upa
}
if inaccessible != 0:
ret_val["inaccessible"] = inaccessible
if error is not None:
ret_val["error"] = error
return ret_val
45 changes: 29 additions & 16 deletions lib/NarrativeService/sharing/sharemanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,36 @@
from installed_clients.baseclient import ServerError
from NarrativeService import feeds

# from storage.mongo import (
# save_share_request,
# find_existing_share_request
# )
SERVICE_TOKEN_KEY = "service-token"
WS_TOKEN_KEY = "ws-admin-token"
SERVICE_TOKEN_KEY = "service-token" # noqa: S105
WS_TOKEN_KEY = "ws-admin-token" # noqa: S105


class ShareRequester:
def __init__(self, params, config):
def __init__(self, params: dict[str, str], config: dict[str, str | int]) -> None:
"""This class handles requesting that a Narrative is shared with another
user.
params is a dict with keys:
* ws_id - the (int) workspace to share with
* share_level - the requested sharing level
* user - the user to be shared with (not necessarily the user requesting the share)
config is a dict with expected keys
* SERVICE_TOKEN_KEY
* WS_TOKEN_KEY
* workspace-url
"""
self.validate_request_params(params)
self.ws_id = params["ws_id"]
self.user = params["user"]
self.share_level = params["share_level"]
self.config = config

def request_share(self):
def request_share(self) -> dict[str, str | int]:
"""
params is a dict with keys:
ws_id - the (int) workspace to share with
share_level - the requested sharing level
user - the user to be shared with (not necessarily the user requesting the share)
This requests that a narrative is shared with a user, and returns a small dictionary
with sharing request results. The request is made via the Feeds service, which notifies
the owner(s) of the requested narrative that someone else wants to view it.
"""
service_token = self.config.get(SERVICE_TOKEN_KEY)
if service_token is None:
Expand Down Expand Up @@ -65,16 +73,21 @@ def request_share(self):
"level": self.share_level
},
"level": "request",
"users": [{"id": u, "type": "user"} for u in requestees + [self.user]]
"users": [{"id": u, "type": "user"} for u in [*requestees, self.user]]
}

feeds.make_notification(note, self.config["feeds-url"], service_token)

# Store that we made the request, uh, somewhere
# save_share_request(self.ws_id, self.user, self.level, note_id)
return ret_value

def validate_request_params(self, params):
def validate_request_params(self, params: dict[str, str]) -> None:
"""Checks the given set of parameters for missing values or, in the case of share_level,
incorrect values.
Expected keys should be ws_id, share_level, and user. share_level should be "a", "n", or "r".
If value is missing, or if share_level is incorrect, this raises a ValueError.
"""
reqd = ["ws_id", "share_level", "user"]
for r in reqd:
if r not in params or params[r] is None:
Expand Down
Empty file added lib/__init__.py
Empty file.
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
coverage==7.5.3
pytest-cov==5.0.0
pytest==8.2.1
pytest-mock==3.14.0
requests-mock==1.12.1
ruff==0.4.6
2 changes: 1 addition & 1 deletion test/DataFetch_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from NarrativeService.data.fetcher import DataFetcher
from NarrativeService.NarrativeServiceImpl import NarrativeService
from NarrativeService.NarrativeServiceServer import MethodContext
from workspace_mock import EmptyWorkspaceMock, WorkspaceMock
from .workspace_mock import EmptyWorkspaceMock, WorkspaceMock


class WsMock:
Expand Down
2 changes: 1 addition & 1 deletion test/NarrativeManager_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from unittest import mock

from NarrativeService.NarrativeManager import NarrativeManager
from workspace_mock import WorkspaceMock
from .workspace_mock import WorkspaceMock


class NarrativeManagerTestCase(unittest.TestCase):
Expand Down
Loading

0 comments on commit 21185eb

Please sign in to comment.