Skip to content

Commit

Permalink
dbio: allow reassign on creation, make sure fact is clear in status
Browse files Browse the repository at this point in the history
  • Loading branch information
RayPlante committed Nov 21, 2024
1 parent a3823af commit 4c22476
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 2 deletions.
2 changes: 1 addition & 1 deletion python/nistoar/midas/dbio/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1036,7 +1036,7 @@ def _new_record_data(self, id):
return a dictionary containing data that will constitue a new ProjectRecord with the given
identifier assigned to it. Generally, this record should not be committed yet.
"""
return {"id": id}
return {"id": id, "status": {"created_by": self.user_id}}

def exists(self, gid: str) -> bool:
"""
Expand Down
20 changes: 20 additions & 0 deletions python/nistoar/midas/dbio/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,31 @@ def create_record(self, name, data=None, meta=None) -> ProjectRecord:
:raises AlreadyExists: if a record owned by the user already exists with the given name
"""
shoulder = self._get_id_shoulder(self.who)

foruser = None
if meta and meta.get("foruser"):
# format of value: either "newuserid" or "olduserid:newuserid"
foruser = meta.get("foruser", "").split(":")
if not foruser or len(foruser) > 2:
foruser = None
else:
foruser = foruser[-1]

if self.dbcli.user_id == ANONYMOUS:
foruser = None
self.log.warning("A new record requested for an anonymous user")

prec = self.dbcli.create_record(name, shoulder)
self._set_default_perms(prec.acls)

prec.status._data["created_by"] = self.who.id # don't do this: violates encapsulation
if foruser:
try:
prec.reassign(foruser)
except NotAuthorized as ex:
self.log.warning("%s: %s not authorized to reassign owner to %s",
prec.id, self.dbcli.user_id, foruser)

if meta:
meta = self._moderate_metadata(meta, shoulder)
if prec.meta:
Expand Down
8 changes: 8 additions & 0 deletions python/nistoar/midas/dbio/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
_modified_p = "modified"
_created_p = "created"
_message_p = "message"
_creatby_p = "created_by"

# Common record actions
#
Expand Down Expand Up @@ -115,6 +116,13 @@ def created_date(self) -> str:
return "pending"
return datetime.fromtimestamp(math.floor(self.created)).isoformat()

@property
def created_by(self) -> str:
"""
a label indicating who originally created the record. This may be a user ID or an Agent ID.
"""
return self._data.get(_creatby_p, "unspecified")

@property
def since(self) -> float:
"""
Expand Down
23 changes: 22 additions & 1 deletion python/nistoar/midas/dbio/wsgi/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,11 +545,32 @@ def do_POST(self, path):
except self.FatalError as ex:
print("fatal Error")
return self.send_fatal_error(ex)

path = path.rstrip('/')
if not path:
# create a record

# support foruser query parameter to set owner
foruser = None
if 'QUERY_STRING' in self._env:
params = parse_qs(self._env['QUERY_STRING'])
foruser = params.get('foruser')
if foruser:
if len(foruser) > 1:
return send_error_resp(400, "Bad foruser param",
"foruser parameter may only be applied once")
foruser = None if len(foruser) == 0 else foruser[0]
if foruser:
input.setdefault("meta", OrderedDict())
if foruser.lower() == "true" and input.get("owner"):
input["meta"]["foruser"] = input['owner']
else:
input["meta"]["foruser"] = foruser

return self.create_record(input)

elif path == ':selected':
# respond to an advanced search
try:
return self.adv_select_records(input)
except SyntaxError as syntax:
Expand Down
13 changes: 13 additions & 0 deletions python/tests/nistoar/midas/dbio/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def test_create_record(self):
self.assertEqual(prec.status.action, "create")
self.assertEqual(prec.status.message, "draft created")
self.assertEqual(prec.status.state, "edit")
self.assertEqual(prec.status.created_by, "midas/nstr1")

self.assertEqual(prec.acls._perms.get("read"), [ self.project.who.actor, "grp0:public"])
self.assertEqual(prec.acls._perms.get("write"), [ self.project.who.actor ])
Expand Down Expand Up @@ -148,6 +149,18 @@ def test_create_record_withdata(self):
self.assertEqual(prec.data, {"color": "red"})
self.assertEqual(prec.meta, {"temper": "dark", "agent_vehicle": 'midas'})

def test_create_record_reassign(self):
self.create_service()
self.assertTrue(not self.project.dbcli.name_exists("gurn"))

prec = self.project.create_record("gurn", {"color": "red"}, {"foruser": "harry"})
self.assertEqual(prec.name, "gurn")
self.assertEqual(prec.id, "mdm1:0003")
self.assertEqual(prec.data, {"color": "red"})
self.assertEqual(prec.owner, "harry")
self.assertEqual(prec.meta, {"foruser": "harry", "agent_vehicle": 'midas'})
self.assertEqual(prec.status.created_by, "midas/nstr1")

def test_get_data(self):
self.create_service()
self.assertTrue(not self.project.dbcli.name_exists("gurn"))
Expand Down
52 changes: 52 additions & 0 deletions python/tests/nistoar/midas/dbio/wsgi/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,58 @@ def test_create(self):
self.assertEqual(resp['id'], "mdm1:0003")
self.assertEqual(resp['data'], {"color": "red"})
self.assertEqual(resp['meta'], {})
self.assertEqual(resp['status']['created_by'], "dbio/nstr1")

# test reassign on creation: via meta
self.resp = []
req['wsgi.input'] = StringIO(json.dumps({"name": "else", "owner": "nobody",
"meta": {"foruser": "harry"}}))
hdlr = self.app.create_handler(req, self.start, path, nistr)
self.assertTrue(isinstance(hdlr, prj.ProjectSelectionHandler))
self.assertNotEqual(hdlr.cfg, {})
self.assertEqual(hdlr._path, "")
body = hdlr.handle()
self.assertIn("201 ", self.resp[0])
resp = self.body2dict(body)
self.assertEqual(resp['name'], "else")
self.assertEqual(resp['owner'], "harry")
self.assertEqual(resp['id'], "mdm1:0004")
self.assertEqual(resp['meta'], {"foruser": "harry", "agent_vehicle": "dbio"})
self.assertEqual(resp['status']['created_by'], "dbio/nstr1")

# test reassign on creation: via query parameter
self.resp = []
req['QUERY_STRING'] = "foruser=True"
req['wsgi.input'] = StringIO(json.dumps({"name": "else", "owner": "nobody"}))
hdlr = self.app.create_handler(req, self.start, path, nistr)
self.assertTrue(isinstance(hdlr, prj.ProjectSelectionHandler))
self.assertNotEqual(hdlr.cfg, {})
self.assertEqual(hdlr._path, "")
body = hdlr.handle()
self.assertIn("201 ", self.resp[0])
resp = self.body2dict(body)
self.assertEqual(resp['name'], "else")
self.assertEqual(resp['owner'], "nobody")
self.assertEqual(resp['id'], "mdm1:0005")
self.assertEqual(resp['meta'], {"foruser": "nobody", "agent_vehicle": "dbio"})
self.assertEqual(resp['status']['created_by'], "dbio/nstr1")

# test reassign on creation: via query parameter
self.resp = []
req['QUERY_STRING'] = "foruser=sally"
req['wsgi.input'] = StringIO(json.dumps({"name": "else", "owner": "nobody"}))
hdlr = self.app.create_handler(req, self.start, path, nistr)
self.assertTrue(isinstance(hdlr, prj.ProjectSelectionHandler))
self.assertNotEqual(hdlr.cfg, {})
self.assertEqual(hdlr._path, "")
body = hdlr.handle()
self.assertIn("201 ", self.resp[0])
resp = self.body2dict(body)
self.assertEqual(resp['name'], "else")
self.assertEqual(resp['owner'], "sally")
self.assertEqual(resp['id'], "mdm1:0006")
self.assertEqual(resp['meta'], {"foruser": "sally", "agent_vehicle": "dbio"})
self.assertEqual(resp['status']['created_by'], "dbio/nstr1")

def test_delete(self):
path = ""
Expand Down

0 comments on commit 4c22476

Please sign in to comment.