Skip to content

Commit

Permalink
Merge feature/dap-add-contact into integration
Browse files Browse the repository at this point in the history
  • Loading branch information
RayPlante committed Apr 2, 2024
2 parents 866e895 + 17f16e3 commit 68e71aa
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 18 deletions.
13 changes: 9 additions & 4 deletions python/nistoar/midas/dap/service/mds3.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,10 +443,15 @@ def _new_data_for(self, recid, meta=None, schemaid=None):
out['components'] = [swcomp] + out['components']

# contact info
# if meta.get("creatorIsContact"):
# # base contact on the currently logged in user
# elif meta.get("contactName"):
if meta.get("contactName"):
if meta.get("creatorisContact"):
cp = OrderedDict()
if self.who.get_prop("userName") and self.who.get_prop("userLastName"):
cp['fn'] = f"{self.who.get_prop('userName')} {self.who.get_prop('userLastName')}"
if self.who.get_prop("email"):
cp['hasEmail'] = self.who.get_prop("email")
if cp:
out['contactPoint'] = cp
elif meta.get("contactName"):
out['contactPoint'] = self._moderate_contactPoint({"fn": meta["contactName"]}, doval=False)

return out
Expand Down
4 changes: 2 additions & 2 deletions python/nistoar/midas/dbio/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,10 +333,10 @@ def _merge_into(self, update: Mapping, base: Mapping, depth: int=-1):

for prop in update:
if prop in base and isinstance(base[prop], Mapping):
if depth > 1 and isinstance(update[prop], Mapping):
if (depth < 0 or depth > 1) and isinstance(update[prop], Mapping):
# the properties from the base and update must both be dictionaries; otherwise,
# update is ignored.
self._merge_into(base[prop], update[prop], depth-1)
self._merge_into(update[prop], base[prop], depth-1)
else:
base[prop] = update[prop]

Expand Down
8 changes: 7 additions & 1 deletion python/nistoar/midas/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,13 @@ def _agent_from_claimset(self, userinfo: dict, agents=None):
subj = subj[:-1*len("@nist.gov")]
elif email.endswith("@nist.gov"):
group = "nist"
return PubAgent(group, PubAgent.USER, subj, agents)

umd = dict((k,v) for k,v in userinfo.items()
if k not in ["userEmail", "sub"])

if not email:
email=None
return PubAgent(group, PubAgent.USER, subj, agents, email=email, **umd)

def handle_request(self, env, start_resp):
path = env.get('PATH_INFO', '/').strip('/').split('/')
Expand Down
42 changes: 38 additions & 4 deletions python/nistoar/pdr/publish/prov.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class PubAgent(object):
AUTO: str = "auto"
UNKN: str = ""

def __init__(self, group: str, actortype: str, actor: str = None, agents: Iterable[str] = None):
def __init__(self, group: str, actortype: str, actor: str = None, agents: Iterable[str] = None,
**kwargs):
self._group = group
if actortype not in [self.USER, self.AUTO, self.UNKN]:
raise ValueError("PubAgent: actortype not one of "+str((self.USER, self.AUTO)))
Expand All @@ -36,6 +37,7 @@ def __init__(self, group: str, actortype: str, actor: str = None, agents: Iterab
if agents:
self._agents = list(agents)
self._actor= actor
self._md = OrderedDict((k,v) for k,v in kwargs.items() if v is not None)

@property
def group(self) -> str:
Expand Down Expand Up @@ -83,7 +85,35 @@ def add_agent(self, agent: str) -> None:
if agent:
self._agents.append(agent)

def to_dict(self) -> Mapping:
def get_prop(self, propname: str, defval=None):
"""
return an actor property with the given name. These arbitrary properties are typically
set at construction time from authentication credentials, but others can be set via
:py:meth:`set_prop`.
:param str propname: the name of the actor property to return
:param Any deval: the value to return if the property is not set (defaults to None)
:return: the property value
:rtype: Any
"""
return self._md.get(propname, defval)

def set_prop(self, propname: str, val):
"""
set an actor property with the given name to a given value. To unset a property, provide
None as the value.
"""
if val is None and propname in self._md:
del self._md[propname]
else:
self._md[propname] = val

def iter_props(self) -> Iterable[tuple]:
"""
iterate through the attached actor properties, returning them a (property, value) tuples
"""
return self._md.items()

def to_dict(self, withmd=False) -> Mapping:
"""
return a dictionary describing this agent that can be converted to JSON directly via the
json module. This implementation returns an OrderedDict which provides a preferred ordering
Expand All @@ -96,18 +126,22 @@ def to_dict(self) -> Mapping:
])
if self._agents:
out['agents'] = self.agents
if withmd and self._md:
out['actor_md'] = deepcopy(self._md)
return out

def serialize(self, indent=None):
def serialize(self, indent=None, withmd=False):
"""
serialize this agent to a JSON string
:param int indent: use the given value as the desired indentation. If None, the output will
include no newline characters (and thus no indentation)
:param bool withmd: if True, include all extra user properties stored in this instance;
default: True
"""
kw = {}
if indent:
kw['indent'] = indent
return json.dumps(self.to_dict(), **kw)
return json.dumps(self.to_dict(withmd), **kw)

def __str__(self):
return "PubAgent(%s:%s)" % (self.group, self.actor)
Expand Down
52 changes: 45 additions & 7 deletions python/tests/nistoar/midas/dap/service/test_mds3.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,21 +110,59 @@ def test_create_record(self):
with self.assertRaises(AlreadyExists):
self.svc.create_record("goob")

def hold_test_create_record_withdata(self):
def test_create_record_withdata(self):
self.create_service()
self.assertTrue(not self.svc.dbcli.name_exists("gurn"))


# goofy data
prec = self.svc.create_record("gurn", {"color": "red"},
{"temper": "dark", "creatorisContact": "goob",
"softwarelink": "http://..." }) # misspelled key
self.assertEqual(prec.name, "gurn")
self.assertEqual(prec.id, "mdsx:0003")
self.assertEqual(prec.id, "mdsy:0003")
self.assertEqual(prec.meta, {"creatorisContact": False, "resourceType": "data"})
for key in "_schema @context _extensionSchemas".split():
for key in "_schema @type author_count file_count reference_count".split():
self.assertIn(key, prec.data)
self.assertEqual(prec.data['color'], "red")
self.assertEqual(prec.data['doi'], "doi:88888/mdsx-0003")
self.assertEqual(prec.data['@id'], "ark:/88434/mdsx-0003")
self.assertNotIn('color', prec.data)
self.assertNotIn('contactPoint', prec.data)
self.assertEqual(prec.data['doi'], "doi:10.88888/mdsy-0003")
self.assertEqual(prec.data['@id'], "ark:/88434/mdsy-0003")
self.assertEqual(prec.data['nonfile_count'], 0)

# some legit metadata but no legit identity info
prec = self.svc.create_record("goob", {"title": "test"},
{"creatorIsContact": "TRUE",
"softwareLink": "https://github.com/usnistgov/goob" })
self.assertEqual(prec.name, "goob")
self.assertEqual(prec.id, "mdsy:0004")
self.assertEqual(prec.meta, {"creatorisContact": True, "resourceType": "data",
"softwareLink": "https://github.com/usnistgov/goob"})
self.assertEqual(prec.data['doi'], "doi:10.88888/mdsy-0004")
self.assertEqual(prec.data['@id'], "ark:/88434/mdsy-0004")
self.assertEqual(prec.data['nonfile_count'], 1)
self.assertNotIn('contactPoint', prec.data)
nerd = self.svc._store.open(prec.id)
links = nerd.nonfiles
self.assertEqual(len(links), 1)
self.assertEqual(links.get(0)['accessURL'], prec.meta['softwareLink'])


# inject some identity info into service and try again
self.svc.who._md.update({"userName": "Gurn", "userLastName": "Cranston", "email": "[email protected]"})
prec = self.svc.create_record("cranston", {"title": "test"},
{"creatorIsContact": True,
"softwareLink": "https://github.com/usnistgov/goob" })
self.assertEqual(prec.name, "cranston")
self.assertEqual(prec.id, "mdsy:0005")
self.assertEqual(prec.meta, {"creatorisContact": True, "resourceType": "data",
"softwareLink": "https://github.com/usnistgov/goob"})
self.assertEqual(prec.data['doi'], "doi:10.88888/mdsy-0005")
self.assertEqual(prec.data['@id'], "ark:/88434/mdsy-0005")
self.assertEqual(prec.data['nonfile_count'], 1)
self.assertIn('contactPoint', prec.data)
self.assertEqual(prec.data['contactPoint'], {"@type": "vcard:Contact", "fn": "Gurn Cranston",
"hasEmail": "mailto:[email protected]"})


def test_moderate_restype(self):
self.create_service()
Expand Down
11 changes: 11 additions & 0 deletions python/tests/nistoar/midas/test_wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,17 @@ def test_authenticate(self):
self.assertEqual(who.group, "nist")
self.assertEqual(who.actor, "fed")
self.assertEqual(who.agents, ["Unit testing agent"])
self.assertIsNone(who.get_prop("email"))

token = jwt.encode({"sub": "fed", "userEmail": "[email protected]", "OU": "61"},
self.config['jwt_auth']['key'], algorithm="HS256")
req['HTTP_AUTHORIZATION'] = "Bearer "+token
who = self.app.authenticate(req)
self.assertEqual(who.group, "nist")
self.assertEqual(who.actor, "fed")
self.assertEqual(who.agents, ["Unit testing agent"])
self.assertEqual(who.get_prop("email"), "[email protected]")
self.assertEqual(who.get_prop("OU"), "61")


def test_create_dmp(self):
Expand Down
18 changes: 18 additions & 0 deletions python/tests/nistoar/pdr/publish/test_prov.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ def test_ctor(self):
self.assertEqual(agent.actor_type, "auto")
self.assertEqual(agent.actor, "pdp")
self.assertEqual(agent.agents, ["thing1", "thing2"])
self.assertEqual(dict(agent.iter_props()), {})

def test_props(self):
agent = prov.PubAgent("ncnr", prov.PubAgent.AUTO, "pdp", ("thing1", "thing2"),
email="[email protected]", lunch="tacos", likes=["mom"])
self.assertEqual(agent.agents, ["thing1", "thing2"])
self.assertEqual(dict(agent.iter_props()),
{"email": "[email protected]", "lunch": "tacos", "likes": ["mom"]})
self.assertEqual(agent.get_prop("email"), "[email protected]")
self.assertEqual(agent.get_prop("lunch"), "tacos")
self.assertEqual(agent.get_prop("likes", "beer"), ["mom"])
self.assertIsNone(agent.get_prop("hairdo"))
self.assertEqual(agent.get_prop("hairdo", "cueball"), "cueball")
agent.set_prop("hairdo", "piece")
self.assertEqual(agent.get_prop("hairdo", "cueball"), "piece")
agent.set_prop("likes", "beer")
self.assertEqual(agent.get_prop("likes"), "beer")


def test_add_agent(self):
agent = prov.PubAgent("ncnr", prov.PubAgent.AUTO, "pdp")
Expand Down

0 comments on commit 68e71aa

Please sign in to comment.