diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..bea6287 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,39 @@ +# Python CircleCI 2.0 configuration file +version: 2.1 +jobs: + cordrapy_develop: + docker: + - image: continuumio/miniconda3 + + working_directory: ~/repo-test-cordrapy + + steps: + # Step 1: obtain repo from GitHub + - checkout + # Step 2: Get and run cordra + - run: + name: Get Cordra + command: | + ls + set -e + apt-get --yes update + apt-get --yes upgrade + apt-get --yes install curl unzip default-jre + curl https://www.cordra.org/assets/sw/cordra-2.4.0-distribution.zip > cordra-2.4.0-distribution.zip + unzip cordra-2.4.0-distribution.zip + cd cordra-2.4.0 + rm data/repoInit.json + echo '{"adminPassword": "admin","prefix": ""}' > data/repoInit.json + (./bin/startup &) | grep -q "Startup complete." + cd ../ + pip install -r requirements.txt + pip install Pillow + pip install -e ./ + python tests/CRUD_CordraClient.py + + +workflows: + version: 2 + test: + jobs: + - cordrapy_develop \ No newline at end of file diff --git a/cordra/__init__.py b/cordra/__init__.py index 5417a0b..7384af4 100644 --- a/cordra/__init__.py +++ b/cordra/__init__.py @@ -2,6 +2,8 @@ """ from .cordra import CordraObject, Token +from .cordraClient import CordraClient + def get_version(): """Get the version of the code from egg_info. @@ -22,6 +24,7 @@ def get_version(): __all__ = [ "__version__", + "Token", "CordraObject", - "Token" + "CordraClient" ] \ No newline at end of file diff --git a/cordra/cordra.py b/cordra/cordra.py index e686280..e9dfd52 100644 --- a/cordra/cordra.py +++ b/cordra/cordra.py @@ -1,7 +1,8 @@ import requests import json +from warnings import warn -from requests.packages.urllib3.exceptions import InsecureRequestWarning +from urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) # global variables @@ -13,6 +14,7 @@ token_grant_type = 'password' token_type = 'Bearer' + def endpoint_url(host, endpoint): return host.strip('/') + '/' + endpoint @@ -29,7 +31,7 @@ def check_response(response): try: return response.json() except BaseException: - return response.text + return response.content def set_auth(username, password): @@ -39,6 +41,7 @@ def set_auth(username, password): auth = None return auth + def get_token_value(token): if isinstance(token, str): return token @@ -59,7 +62,11 @@ def set_headers(token): headers = None return headers + class CordraObject: + warn("CordraObject may be moved to a new module with a new name in future releases.") + + @staticmethod def create( host, obj_json, @@ -73,7 +80,8 @@ def create( verify=None, full=False, payloads=None, - acls=None + acls=None, + **kwargs ): '''Create a Cordra object''' @@ -135,6 +143,7 @@ def create( else: return obj_r + @staticmethod def read( host, obj_id, @@ -144,7 +153,8 @@ def read( verify=None, jsonPointer=None, jsonFilter=None, - full=False + full=False, + **kwargs ): '''Retrieve a Cordra object JSON by identifer.''' @@ -165,13 +175,15 @@ def read( verify=verify)) return r + @staticmethod def read_payload_info( host, obj_id, username=None, password=None, token=None, - verify=None + verify=None, + **kwargs ): '''Retrieve a Cordra object payload names by identifer.''' @@ -188,6 +200,7 @@ def read_payload_info( verify=verify)) return r['payloads'] + @staticmethod def read_payload( host, obj_id, @@ -195,7 +208,8 @@ def read_payload( username=None, password=None, token=None, - verify=None + verify=None, + **kwargs ): '''Retrieve a Cordra object payload by identifer and payload name.''' @@ -212,6 +226,7 @@ def read_payload( verify=verify)) return r + @staticmethod def update( host, obj_id, @@ -226,7 +241,8 @@ def update( full=False, payloads=None, payloadToDelete=None, - acls=None + acls=None, + **kwargs ): '''Update a Cordra object''' @@ -289,6 +305,7 @@ def update( return r + @staticmethod def delete( host, obj_id, @@ -296,7 +313,8 @@ def delete( username=None, password=None, token=None, - verify=None + verify=None, + **kwargs ): '''Delete a Cordra object''' @@ -316,6 +334,7 @@ def delete( ) return r + @staticmethod def find( host, query, @@ -325,13 +344,19 @@ def find( verify=None, ids=False, jsonFilter=None, - full=False + full=False, + pageNum=None, + pageSize=None, + **kwargs ): '''Find a Cordra object by query''' params = dict() params['query'] = query params['full'] = full + if pageNum: params["pageNum"] = pageNum + if pageSize: params["pageSize"] = pageSize + if jsonFilter: params['filter'] = str(jsonFilter) if ids: @@ -348,12 +373,16 @@ def find( return r class Token: + warn("Token may be moved to a new module with a new name in future releases.") + + @staticmethod def create( host, username, password, verify=None, - full=False + full=False, + **kwargs ): '''Create an access Token''' @@ -373,11 +402,13 @@ def create( verify=verify)) return r + @staticmethod def read( host, token, verify=None, - full=False + full=False, + **kwargs ): '''Read an access Token''' @@ -396,10 +427,12 @@ def read( )) return r + @staticmethod def delete( host, token, - verify=None + verify=None, + **kwargs ): '''Delete an access Token''' diff --git a/cordra/cordraClient.py b/cordra/cordraClient.py new file mode 100644 index 0000000..46cb808 --- /dev/null +++ b/cordra/cordraClient.py @@ -0,0 +1,158 @@ +# Standard Library packages +from typing import Dict +import json +import os +from copy import deepcopy + +# Local imports +from .cordra import CordraObject, Token, check_response + + +class CordraClient: + """ + Supports CRUD operations with a running Cordra instance allows access to the full + functionality of the Cordra REST API. This includes: + * Authorization using user / password + * Authorization using a secret key + * Provide a token for subsequent authorization + * Delete a token + * Create a cordra object + * Setting the ACL on a cordra object on create + * Updating a cordra object + * Updating a cordra object attribute + * Updating a cordra object payload + * Updating the ACL of a cordra object + * Deleting a cordra object + * Deleting a cordra object attribute + * Deleting a cordra object payload + * Querying cordra + + Attributes: + host: the location of the cordra instance (URL). + credentials_file: the location of a user's credentials. + credentials_token: a credentials token file. + params: parameters that will be passed with each request. + + >>> import cordra + >>> test_object = cordra.CordraClient("testhost") + >>> print(test_object) + Connection via CordraPy to testhost + """ + + host: str #URL + handle: str="prefix" + username: str + password: str + secret_key_path: str #FilePath + params: Dict + + + def __str__(self): + return f"Connection via CordraPy to {self.host}" + + + def __init__(self, **params): + assert "host" in params, "Host must be specified to use CordraClient" + assert ("username" in params and "password" in params) or "secret_key_path" in params, \ + "Client requires `username` and `password` params or a `secret_key_path` param" + self.params = dict() + self.params.update(params) + + if "username" in params: + self.get_auth() + del self.params["username"] + del self.params["password"] + elif "secret_key_path" in params: + raise NotImplementedError + else: + raise Exception + + self.schemas = { + r.get("name"): r.get("schema") + for r in self.find("type:Schema")['results'] + } + + + def get_auth(self): + """Get a token with credentials""" + r = Token.create(**self.params) + + # Set up variables and default auth for future requests + self.params["token"] = r["access_token"] + + + def check_auth(self): + """Checks an access Token""" + r = Token.read(**self.params) + return r + + + def delete_auth(self): + """Delete an access Token""" + r = Token.delete(**self.params) + return r + + + def create(self, obj, obj_type, **kwargs): + """Creates an object""" + + params = deepcopy(self.params) + params.update(kwargs) + + return CordraObject.create(obj_json=obj, obj_type=obj_type, **params) + + + def read(self, obj_id, getObjPayTuple=False, **kwargs): + """Retrieve an object from Cordra by identifer and create a + python CordraObject.""" + + params = deepcopy(self.params) + params.update(kwargs) + + if getObjPayTuple: + params["full"] = True + + obj = CordraObject.read(obj_id=obj_id, **params) + + if getObjPayTuple: + if "payloads" not in obj: + return (obj["content"], None) + + payload_info = deepcopy( obj["payloads"] ) + obj["payloads"] = dict() + payload_info = CordraObject.read_payload_info(obj_id=obj_id, **params) + + for pay in payload_info: + payName = pay.get("name") + obj["payloads"][payName] = CordraObject.read_payload(obj_id=obj_id, payload=payName, **params) + + return (obj["content"], obj["payloads"]) + + return obj + + + def update(self, obj, obj_id, **kwargs): + """Updates an object""" + + params = deepcopy(self.params) + params.update(kwargs) + + return CordraObject.update(obj_id=obj_id, obj_json=obj, **params) + + + def delete(self, obj_id, **kwargs): + """Delete a Cordra object or part of a Cordra Object""" + + params = deepcopy(self.params) + params.update(kwargs) + + return CordraObject.delete(obj_id=obj_id, **params) + + + def find(self, query, **kwargs): + """Find a Cordra object by query""" + + params = deepcopy(self.params) + params.update(kwargs) + + return CordraObject.find(query=query, **params) diff --git a/examples/examples-Client.ipynb b/examples/examples-Client.ipynb new file mode 100644 index 0000000..bc5f521 --- /dev/null +++ b/examples/examples-Client.ipynb @@ -0,0 +1,904 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/sven/7/Work/ThesisMatData/CordraPy/cordra/cordra.py:67: UserWarning: CordraObject may be moved to a new module with a new name in future releases.\n", + " warn(\"CordraObject may be moved to a new module with a new name in future releases.\")\n", + "/home/sven/7/Work/ThesisMatData/CordraPy/cordra/cordra.py:376: UserWarning: Token may be moved to a new module with a new name in future releases.\n", + " warn(\"Token may be moved to a new module with a new name in future releases.\")\n" + ] + } + ], + "source": [ + "import getpass\n", + "import cordra\n", + "from lucenequerybuilder import Q\n", + "from io import StringIO\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "from urllib.error import HTTPError" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Cordra Host Information" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "host = \"https://localhost:8443/\"\n", + "\n", + "localhost = cordra.CordraClient(host=host, username=\"admin\", password=\"admin\", verify=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_keys(['User', 'Document', 'Group'])" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "localhost.schemas.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "obj_type = \"Document\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Create two similar objects" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'id': 'test/f1f0188d7e74ce2e9b39',\n", + " 'name': 'example 1',\n", + " 'description': 'an example of metadata for CSV payload',\n", + " 'author': 'John'}" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file_1 = \"example-data.csv\"\n", + "payloads = {'p1': (file_1, open(file_1,'rb'))}\n", + "\n", + "obj_1 = dict()\n", + "obj_1[\"name\"] = \"example 1\"\n", + "obj_1[\"description\"] = \"an example of metadata for CSV payload\"\n", + "obj_1[\"author\"] = \"John\"\n", + "\n", + "my_acl = dict()\n", + "my_acl[\"readers\"] = [\"public\"]\n", + "\n", + "response = localhost.create(obj_1, obj_type, acls=my_acl, payloads=payloads)\n", + "response" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", + " 'description': 'another example of metadata for CSV payload',\n", + " 'author': 'Tim'}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file_2 = \"example-data.csv\"\n", + "payloads = {'p1': (file_2, open(file_2,'rb'))}\n", + "\n", + "obj_2 = dict()\n", + "obj_2[\"name\"] = \"example 2\"\n", + "obj_2[\"description\"] = \"another example of metadata for CSV payload\"\n", + "obj_2[\"author\"] = \"Tim\"\n", + "\n", + "my_acl = dict()\n", + "my_acl[\"readers\"] = [\"public\"]\n", + "\n", + "response = localhost.create(obj_2, obj_type, acls=my_acl, payloads=payloads)\n", + "response" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Query objects" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Full text query example" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'pageNum': 0,\n", + " 'pageSize': -1,\n", + " 'size': 2,\n", + " 'results': [{'id': 'test/f1f0188d7e74ce2e9b39',\n", + " 'name': 'example 1',\n", + " 'description': 'an example of metadata for CSV payload',\n", + " 'author': 'John'},\n", + " {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", + " 'description': 'another example of metadata for CSV payload',\n", + " 'author': 'Tim'}]}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "q = Q('metadata')\n", + "my_results = localhost.find(str(q))\n", + "my_results" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Field query examples" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'pageNum': 0,\n", + " 'pageSize': -1,\n", + " 'size': 1,\n", + " 'results': [{'id': 'test/f1f0188d7e74ce2e9b39',\n", + " 'name': 'example 1',\n", + " 'description': 'an example of metadata for CSV payload',\n", + " 'author': 'John'}]}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "q = Q('/author','John')\n", + "my_results = localhost.find(str(q))\n", + "obj_id_1 = my_results[\"results\"][0][\"id\"]\n", + "my_results" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'pageNum': 0,\n", + " 'pageSize': -1,\n", + " 'size': 1,\n", + " 'results': [{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", + " 'description': 'another example of metadata for CSV payload',\n", + " 'author': 'Tim'}]}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "q = Q('/author','Tim')\n", + "my_results = localhost.find(str(q))\n", + "obj_id_2 = my_results[\"results\"][0][\"id\"]\n", + "my_results" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Read objects" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'test/d92d43a269b89a3e6a10'" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "obj_id_2" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", + " 'description': 'another example of metadata for CSV payload',\n", + " 'author': 'Tim'}" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_obj = localhost.read(obj_id_2)\n", + "my_obj" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", + " 'description': 'another example of metadata for CSV payload',\n", + " 'author': 'Tim'},\n", + " 'acl': {'readers': ['public']},\n", + " 'metadata': {'createdOn': 1652894477383,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652894477383,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652894477383052},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_obj = localhost.read(obj_id_2,full=True)\n", + "my_obj" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'p1'" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "obj_payload_name = my_obj[\"payloads\"][0][\"name\"]\n", + "obj_payload_name" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'\\ufeff\"SAM0\",\"SAM1\"\\r\\n1.00,2.302389071\\r\\n2.00,3.71503899\\r\\n3.00,9.426125622\\r\\n4.00,11.34529125\\r\\n5.00,11.87704484\\r\\n6.00,19.01325695\\r\\n7.00,21.52353652\\r\\n8.00,28.28670056\\r\\n9.00,29.55737761\\r\\n10.00,25.89582707\\r\\n11.00,31.57982065\\r\\n12.00,36.02452105\\r\\n13.00,39.47686412\\r\\n14.00,44.41192202\\r\\n15.00,43.36098819\\r\\n16.00,48.61525381\\r\\n17.00,53.91222295\\r\\n18.00,54.28420278\\r\\n19.00,53.1378195\\r\\n20.00,55.88015939'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "obj, payload = localhost.read(obj_id_2, getObjPayTuple=True)\n", + "payload = payload[obj_payload_name]\n", + "payload.decode('utf-8')" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
SAM0SAM1
01.02.302389
12.03.715039
23.09.426126
34.011.345291
45.011.877045
56.019.013257
67.021.523537
78.028.286701
89.029.557378
910.025.895827
1011.031.579821
1112.036.024521
1213.039.476864
1314.044.411922
1415.043.360988
1516.048.615254
1617.053.912223
1718.054.284203
1819.053.137819
1920.055.880159
\n", + "
" + ], + "text/plain": [ + " SAM0 SAM1\n", + "0 1.0 2.302389\n", + "1 2.0 3.715039\n", + "2 3.0 9.426126\n", + "3 4.0 11.345291\n", + "4 5.0 11.877045\n", + "5 6.0 19.013257\n", + "6 7.0 21.523537\n", + "7 8.0 28.286701\n", + "8 9.0 29.557378\n", + "9 10.0 25.895827\n", + "10 11.0 31.579821\n", + "11 12.0 36.024521\n", + "12 13.0 39.476864\n", + "13 14.0 44.411922\n", + "14 15.0 43.360988\n", + "15 16.0 48.615254\n", + "16 17.0 53.912223\n", + "17 18.0 54.284203\n", + "18 19.0 53.137819\n", + "19 20.0 55.880159" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df = pd.read_csv(StringIO(payload.decode('utf-8')))\n", + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "df.plot.scatter(x='SAM0', y='SAM1')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Update Objects\n", + "\n", + "This also demonstrates the use of \"Dry Run\". During a \"Dry Run\", Cordra not actually create/update the object. Cordra will return results as if object had been created/updated. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Update part of the object JSON\n", + "\n", + "payloads not affected" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", + " 'description': 'I really need to write a better description for my data.',\n", + " 'author': 'Tim'},\n", + " 'acl': {'readers': ['public']},\n", + " 'metadata': {'createdOn': 1652894477383,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652894477858,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652894477383052},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response = localhost.update(\n", + " \"I really need to write a better description for my data.\",\n", + " obj_id_2,\n", + " jsonPointer=\"/description\",\n", + " dryRun=True,\n", + " full=True)\n", + "response" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Update all of the object JSON\n", + "\n", + "payloads not affected" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'id': 'test/3b11b6a7bdb1d6cf052e', 'SAM1': 'Level of CXCR4 expression'}\n", + "{'id': 'test/d92d43a269b89a3e6a10', 'SAM1': 'Level of CXCR4 expression'}\n" + ] + } + ], + "source": [ + "original = {'id': 'test/3b11b6a7bdb1d6cf052e', \"SAM1\":\"Level of CXCR4 expression\"}\n", + "\n", + "response = localhost.update(\n", + " original,\n", + " obj_id_2,\n", + " dryRun=True,\n", + " full=True)\n", + "\n", + "print(original)\n", + "print(response[\"content\"])\n", + "\n", + "# assert original == response[\"content\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Check ACLs" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "# Create new user\n", + "user1 = localhost.create({\"@context\": \"\", \"@type\": \"\", \"username\": \"user1\", \"password\": \"person.1234\"}, \"User\")\n", + "user1_id = user1[\"id\"]\n", + "\n", + "user2 = localhost.create({\"@context\": \"\", \"@type\": \"\", \"username\": \"user2\", \"password\": \"person.1234\"}, \"User\")\n", + "user2_id = user2[\"id\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[{'id': 'test/48baeedf9a39614b710c', 'type': 'Document', 'content': {'id': 'test/48baeedf9a39614b710c', 'name': 'unaccessible'}, 'metadata': {'createdOn': 1652894478082, 'createdBy': 'admin', 'modifiedOn': 1652894478082, 'modifiedBy': 'admin', 'txnId': 1652894478082055}}, {'readers': ['test/b3daa77b4c04a9551b87']}]\n", + "{'id': 'test/b3daa77b4c04a9551b87', 'username': 'user1', 'password': '', '@context': '', '@type': ''}\n", + "{'id': 'test/a1881c06eec96db9901c', 'username': 'user2', 'password': '', '@context': '', '@type': ''}\n", + "{'message': 'Forbidden'}\n", + "403 Client Error: Forbidden for url: https://localhost:8443/objects/test/48baeedf9a39614b710c?full=False\n" + ] + } + ], + "source": [ + "r = localhost.create({\"name\": \"unaccessible\"}, obj_type, acls={\"readers\":[user1_id],\"writers\":None})\n", + "obj_id = r[0]['id']\n", + "\n", + "print(r)\n", + "print(user1)\n", + "print(user2)\n", + "\n", + "localhost_user1 = cordra.CordraClient(host=host, username=\"user1\", password=\"person.1234\", verify=False)\n", + "localhost_user1.read(obj_id)\n", + "\n", + "localhost_user2 = cordra.CordraClient(host=host, username=\"user2\", password=\"person.1234\", verify=False)\n", + "\n", + "# this should return a 403 error\n", + "try:\n", + " localhost_user2.read(obj_id)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Update ACLs" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'message': 'Forbidden'}\n", + "403 Client Error: Forbidden for url: https://localhost:8443/objects/test/48baeedf9a39614b710c?full=False\n" + ] + } + ], + "source": [ + "r = localhost.update(dict(), obj_id, acls={\"readers\":[user2_id],\"writers\":None})\n", + "\n", + "localhost_user2.read(obj_id)\n", + "\n", + "# this should return a 403 error\n", + "try:\n", + " localhost_user1.read(obj_id)\n", + "except Exception as e: \n", + " print(e)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'message': 'Forbidden'}\n", + "403 Client Error: Forbidden for url: https://localhost:8443/objects/test/48baeedf9a39614b710c?full=False\n" + ] + } + ], + "source": [ + "# Update payloads and acls at the same time\n", + "r = localhost.update({\"@id\": obj_id}, obj_id, payloads={\"file\":\"update\".encode('utf-8')}, acls={\"readers\":[user1_id],\"writers\":None})\n", + "\n", + "localhost_user1.read(obj_id)\n", + "\n", + "# this should return a 403 error\n", + "try:\n", + " localhost_user2.read(obj_id)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Delete objects" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5\n" + ] + } + ], + "source": [ + "all_objects = []\n", + "\n", + "r = localhost.find(\"*\", pageSize=-1, full=True)\n", + "\n", + "all_objects += [ri['id'] for ri in r['results'] if ri['type']!='Schema']\n", + " \n", + "print(len(all_objects))" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "for obj_id in all_objects:\n", + " localhost.delete(obj_id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Delete Token" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'active': False}" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "r = localhost.delete_auth()\n", + "r" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/examples-token.ipynb b/examples/examples-token.ipynb index 71ce6d0..e146b84 100644 --- a/examples/examples-token.ipynb +++ b/examples/examples-token.ipynb @@ -4,12 +4,23 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/sven/7/Work/ThesisMatData/CordraPy/cordra/cordra.py:67: UserWarning: CordraObject may be moved to a new module with a new name in future releases.\n", + " warn(\"CordraObject may be moved to a new module with a new name in future releases.\")\n", + "/home/sven/7/Work/ThesisMatData/CordraPy/cordra/cordra.py:376: UserWarning: Token may be moved to a new module with a new name in future releases.\n", + " warn(\"Token may be moved to a new module with a new name in future releases.\")\n" + ] + } + ], "source": [ "import getpass\n", "import cordra\n", "from lucenequerybuilder import Q\n", - "from io import StringIO\n", + "from io import StringIO, BytesIO\n", "import pandas as pd\n", "import matplotlib.pyplot as plt" ] @@ -27,8 +38,8 @@ "metadata": {}, "outputs": [], "source": [ - "host = \"https://127.0.0.1\"\n", - "obj_type = \"debug\"" + "host = \"https://127.0.0.1:8443\"\n", + "obj_type = \"Document\"" ] }, { @@ -52,7 +63,7 @@ } ], "source": [ - "username = \"testuser1\"\n", + "username = \"admin\"\n", "password = getpass.getpass()" ] }, @@ -71,11 +82,11 @@ { "data": { "text/plain": [ - "{'access_token': '16wmt3rhwp6mk1a0itsl6c576',\n", + "{'access_token': 'pva8e9zmdtg5ysv0orshmmqq',\n", " 'token_type': 'Bearer',\n", " 'active': True,\n", - " 'userId': 'local/bc51a83eea09846dc024',\n", - " 'username': 'testuser1'}" + " 'userId': 'admin',\n", + " 'username': 'admin'}" ] }, "execution_count": 4, @@ -103,7 +114,8 @@ { "data": { "text/plain": [ - "{'name': 'example 1',\n", + "{'id': 'test/f1f0188d7e74ce2e9b39',\n", + " 'name': 'example 1',\n", " 'description': 'an example of metadata for CSV payload',\n", " 'author': 'John'}" ] @@ -144,7 +156,8 @@ { "data": { "text/plain": [ - "{'name': 'example 2',\n", + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", " 'description': 'another example of metadata for CSV payload',\n", " 'author': 'Tim'}" ] @@ -202,30 +215,38 @@ "{'pageNum': 0,\n", " 'pageSize': -1,\n", " 'size': 2,\n", - " 'results': [{'id': 'local/f6d4cf0f7db6230fff12',\n", - " 'type': 'debug',\n", - " 'content': {'name': 'example 1',\n", + " 'results': [{'id': 'test/f1f0188d7e74ce2e9b39',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/f1f0188d7e74ce2e9b39',\n", + " 'name': 'example 1',\n", " 'description': 'an example of metadata for CSV payload',\n", " 'author': 'John'},\n", " 'acl': {'readers': ['public']},\n", - " 'metadata': {'createdOn': 1612797369560,\n", - " 'createdBy': 'local/bc51a83eea09846dc024',\n", - " 'modifiedOn': 1612797369560,\n", - " 'modifiedBy': 'local/bc51a83eea09846dc024',\n", - " 'txnId': 1612797369560040},\n", - " 'payloads': [{'name': 'p1', 'filename': 'example-data.csv', 'size': 385}]},\n", - " {'id': 'local/ff16115bd3c7163a6e8e',\n", - " 'type': 'debug',\n", - " 'content': {'name': 'example 2',\n", + " 'metadata': {'createdOn': 1652894195999,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652894195999,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652894195999027},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]},\n", + " {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", " 'description': 'another example of metadata for CSV payload',\n", " 'author': 'Tim'},\n", " 'acl': {'readers': ['public']},\n", - " 'metadata': {'createdOn': 1612797369664,\n", - " 'createdBy': 'local/bc51a83eea09846dc024',\n", - " 'modifiedOn': 1612797369664,\n", - " 'modifiedBy': 'local/bc51a83eea09846dc024',\n", - " 'txnId': 1612797369664041},\n", - " 'payloads': [{'name': 'p1', 'filename': 'example-data.csv', 'size': 385}]}]}" + " 'metadata': {'createdOn': 1652894196042,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652894196042,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652894196042028},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}]}" ] }, "execution_count": 7, @@ -257,18 +278,22 @@ "{'pageNum': 0,\n", " 'pageSize': -1,\n", " 'size': 1,\n", - " 'results': [{'id': 'local/f6d4cf0f7db6230fff12',\n", - " 'type': 'debug',\n", - " 'content': {'name': 'example 1',\n", + " 'results': [{'id': 'test/f1f0188d7e74ce2e9b39',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/f1f0188d7e74ce2e9b39',\n", + " 'name': 'example 1',\n", " 'description': 'an example of metadata for CSV payload',\n", " 'author': 'John'},\n", " 'acl': {'readers': ['public']},\n", - " 'metadata': {'createdOn': 1612797369560,\n", - " 'createdBy': 'local/bc51a83eea09846dc024',\n", - " 'modifiedOn': 1612797369560,\n", - " 'modifiedBy': 'local/bc51a83eea09846dc024',\n", - " 'txnId': 1612797369560040},\n", - " 'payloads': [{'name': 'p1', 'filename': 'example-data.csv', 'size': 385}]}]}" + " 'metadata': {'createdOn': 1652894195999,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652894195999,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652894195999027},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}]}" ] }, "execution_count": 8, @@ -294,18 +319,22 @@ "{'pageNum': 0,\n", " 'pageSize': -1,\n", " 'size': 1,\n", - " 'results': [{'id': 'local/ff16115bd3c7163a6e8e',\n", - " 'type': 'debug',\n", - " 'content': {'name': 'example 2',\n", + " 'results': [{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", " 'description': 'another example of metadata for CSV payload',\n", " 'author': 'Tim'},\n", " 'acl': {'readers': ['public']},\n", - " 'metadata': {'createdOn': 1612797369664,\n", - " 'createdBy': 'local/bc51a83eea09846dc024',\n", - " 'modifiedOn': 1612797369664,\n", - " 'modifiedBy': 'local/bc51a83eea09846dc024',\n", - " 'txnId': 1612797369664041},\n", - " 'payloads': [{'name': 'p1', 'filename': 'example-data.csv', 'size': 385}]}]}" + " 'metadata': {'createdOn': 1652894196042,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652894196042,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652894196042028},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}]}" ] }, "execution_count": 9, @@ -335,7 +364,7 @@ { "data": { "text/plain": [ - "'local/ff16115bd3c7163a6e8e'" + "'test/d92d43a269b89a3e6a10'" ] }, "execution_count": 10, @@ -355,7 +384,8 @@ { "data": { "text/plain": [ - "{'name': 'example 2',\n", + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", " 'description': 'another example of metadata for CSV payload',\n", " 'author': 'Tim'}" ] @@ -378,18 +408,22 @@ { "data": { "text/plain": [ - "{'id': 'local/ff16115bd3c7163a6e8e',\n", - " 'type': 'debug',\n", - " 'content': {'name': 'example 2',\n", + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", " 'description': 'another example of metadata for CSV payload',\n", " 'author': 'Tim'},\n", " 'acl': {'readers': ['public']},\n", - " 'metadata': {'createdOn': 1612797369664,\n", - " 'createdBy': 'local/bc51a83eea09846dc024',\n", - " 'modifiedOn': 1612797369664,\n", - " 'modifiedBy': 'local/bc51a83eea09846dc024',\n", - " 'txnId': 1612797369664041},\n", - " 'payloads': [{'name': 'p1', 'filename': 'example-data.csv', 'size': 385}]}" + " 'metadata': {'createdOn': 1652894196042,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652894196042,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652894196042028},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}" ] }, "execution_count": 12, @@ -431,7 +465,7 @@ { "data": { "text/plain": [ - "'\\ufeff\"SAM0\",\"SAM1\"\\r\\n1.00,2.302389071\\r\\n2.00,3.71503899\\r\\n3.00,9.426125622\\r\\n4.00,11.34529125\\r\\n5.00,11.87704484\\r\\n6.00,19.01325695\\r\\n7.00,21.52353652\\r\\n8.00,28.28670056\\r\\n9.00,29.55737761\\r\\n10.00,25.89582707\\r\\n11.00,31.57982065\\r\\n12.00,36.02452105\\r\\n13.00,39.47686412\\r\\n14.00,44.41192202\\r\\n15.00,43.36098819\\r\\n16.00,48.61525381\\r\\n17.00,53.91222295\\r\\n18.00,54.28420278\\r\\n19.00,53.1378195\\r\\n20.00,55.88015939'" + "b'\\xef\\xbb\\xbf\"SAM0\",\"SAM1\"\\r\\n1.00,2.302389071\\r\\n2.00,3.71503899\\r\\n3.00,9.426125622\\r\\n4.00,11.34529125\\r\\n5.00,11.87704484\\r\\n6.00,19.01325695\\r\\n7.00,21.52353652\\r\\n8.00,28.28670056\\r\\n9.00,29.55737761\\r\\n10.00,25.89582707\\r\\n11.00,31.57982065\\r\\n12.00,36.02452105\\r\\n13.00,39.47686412\\r\\n14.00,44.41192202\\r\\n15.00,43.36098819\\r\\n16.00,48.61525381\\r\\n17.00,53.91222295\\r\\n18.00,54.28420278\\r\\n19.00,53.1378195\\r\\n20.00,55.88015939'" ] }, "execution_count": 14, @@ -476,102 +510,102 @@ " \n", " \n", " \n", - " 0\n", + " 0\n", " 1.0\n", " 2.302389\n", " \n", " \n", - " 1\n", + " 1\n", " 2.0\n", " 3.715039\n", " \n", " \n", - " 2\n", + " 2\n", " 3.0\n", " 9.426126\n", " \n", " \n", - " 3\n", + " 3\n", " 4.0\n", " 11.345291\n", " \n", " \n", - " 4\n", + " 4\n", " 5.0\n", " 11.877045\n", " \n", " \n", - " 5\n", + " 5\n", " 6.0\n", " 19.013257\n", " \n", " \n", - " 6\n", + " 6\n", " 7.0\n", " 21.523537\n", " \n", " \n", - " 7\n", + " 7\n", " 8.0\n", " 28.286701\n", " \n", " \n", - " 8\n", + " 8\n", " 9.0\n", " 29.557378\n", " \n", " \n", - " 9\n", + " 9\n", " 10.0\n", " 25.895827\n", " \n", " \n", - " 10\n", + " 10\n", " 11.0\n", " 31.579821\n", " \n", " \n", - " 11\n", + " 11\n", " 12.0\n", " 36.024521\n", " \n", " \n", - " 12\n", + " 12\n", " 13.0\n", " 39.476864\n", " \n", " \n", - " 13\n", + " 13\n", " 14.0\n", " 44.411922\n", " \n", " \n", - " 14\n", + " 14\n", " 15.0\n", " 43.360988\n", " \n", " \n", - " 15\n", + " 15\n", " 16.0\n", " 48.615254\n", " \n", " \n", - " 16\n", + " 16\n", " 17.0\n", " 53.912223\n", " \n", " \n", - " 17\n", + " 17\n", " 18.0\n", " 54.284203\n", " \n", " \n", - " 18\n", + " 18\n", " 19.0\n", " 53.137819\n", " \n", " \n", - " 19\n", + " 19\n", " 20.0\n", " 55.880159\n", " \n", @@ -609,7 +643,7 @@ } ], "source": [ - "df = pd.read_csv(StringIO(payload))\n", + "df = pd.read_csv(BytesIO(payload))\n", "df" ] }, @@ -620,7 +654,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAX4AAAEGCAYAAABiq/5QAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAWH0lEQVR4nO3df5Cd9V3o8fdnyZJgE01IAqYJMdRgtb0DEffWenOtFWxFrKGKrdDqTRWNztg77bV3EpyO2uu92oJjdXRqNbYdU6ctYFMMduoVLhSr91raDYYUBAtUeglEEkJoyUi2SffjH8+zzcnhnD272X3OefY879fMzjnn+XGezz45+8n3fJ/v9/NEZiJJao6RQQcgSeovE78kNYyJX5IaxsQvSQ1j4pekhlk06ABmYtWqVblhw4ZBhyFJC8revXufzszV7csXROLfsGED4+Pjgw5DkhaUiPhyp+V29UhSw5j4JalhTPyS1DAmfklqGBO/JDWMiV+SaujIsQnue/xZjhybmPf3XhDDOSWpSfbse4Idu/czOjLCiclJbrz6YrZsWjtv72+LX5Jq5MixCXbs3s/xE5M8N3GS4ycm2b57/7y2/E38klQjB44+z+jI6al5dGSEA0efn7djmPglqUbWrTiHE5OTpy07MTnJuhXnzNsxTPySVCMrly7mxqsvZsnoCMsWL2LJ6Ag3Xn0xK5cunrdjeHFXkjo4cmyCA0efZ92Kc+Y16c7Elk1r2bxxVWXHN/FLUpuqR9XMxMqliyv7D8euHklD6UzHwfdjVM2g2eKXNHTm0mKfGlVznFMXWKdG1fS7y6cqtvglDZW5ttjna1RNlTNv58rEL2mozHUc/HyMqtmz7wk233AXP/2Be9h8w13ctu+JWf0OVbOrR9JQmY8W+1xG1bR+45jqLtq+ez+bN66qTVeRLX5JQ2W+xsGvXLqYSy5YPuv9+jHzdq5s8UsaOlWPg59OP2bezpUtfklD6Uxb7PNx3Kpn3s6VLX5JmmeD/MYxEyZ+SapAlTNv58quHklqGBO/JDWMiV+SGsbEL0kNU+nF3Yh4DHgO+DpwMjPHIuJc4GZgA/AY8MbMPFplHJKkU/rR4v/BzNyUmWPl6+uBOzPzIuDO8rUkqU8G0dVzFbCrfL4LeP0AYpCkxqo68Sdwe0TsjYht5bLzM/MgQPl4XqcdI2JbRIxHxPjhw4crDlOSmqPqCVybM/PJiDgPuCMiHprpjpm5E9gJMDY2llUFKElNU2mLPzOfLB8PAbcCrwCeiog1AOXjoSpjkLQw1flGJgtdZS3+iHgRMJKZz5XPXwv8JnAbsBV4T/m4p6oYJC1MdbjZ+TCrsqvnfODWiJg6zkcz839HxOeBWyLiOuD/A2+oMAZJC8xCuJHJQldZ4s/MLwGXdFh+BLi8quNKWtiacLPzQXPmrqRaWQg3MlnoTPySamUh3MhkobMev6TaqfuNTBY6E7+kWqrzjUwWOrt6JKlhTPyS1DAmfklqGBO/JDWMiV+SGsbEL0kNY+KXpIYx8UtSw5j4JXVkPfzh5cxdSS9gPfzhZotf0mla6+E/N3GS4ycm2b57vy3/IWLil3SaqXr4rabq4c+GXUX1ZVePpNPMRz18u4rqzRa/pNPMtR6+XUX1Z4tf0gvMpR6+t06sPxO/pI7OtB6+t06sP7t6JM0rb51Yf7b4Jc07b51YbyZ+SZXw1on1ZVePJDWMiV+SGsbEL0kNY+KXpIYx8UtSw1Se+CPirIj4x4j4ZPn6woi4JyIejoibI+LsqmOQJJ3Sjxb/24AHW17fAPxeZl4EHAWu60MMkqRSpYk/ItYBPwp8oHwdwGXAx8tNdgGvrzIGqaksi6xuqp7A9fvAdmBZ+Xol8GxmnixfHwA61mqNiG3ANoD169dXHKY0XCyLrOlU1uKPiNcBhzJzb+viDptmp/0zc2dmjmXm2OrVqyuJURpGlkVWL1W2+DcDWyLiSmAJ8M0U3wCWR8SistW/DniywhikxrEssnqprMWfmb+amesycwNwDXBXZr4Z+DTwk+VmW4E9VcUgNZFlkdXLIMbx7wB+JSIeoejz/+AAYpCGlmWR1Utkduxir5WxsbEcHx8fdBjSgnLk2IRlkRsuIvZm5lj7cssyS0PKssjqxpINktQwJn5JahgTvyQ1jIlfkhrGxC9JDWPil2rKImuqisM5pRqyyJqqZItfqhmLrKlqJn6pZqaKrLWaKrImzQcTv1QzFllT1Uz8Us1YZE1V8+KuVENbNq1l88ZVFllTJUz8Uk1ZZE1VsatHkhrGxC9JDWPil6SGMfFLUsOY+CWpYUz8ktQwJn5JahgTvyQ1jIlfqoj19FVXZzxzNyK+MzMfms9gpGFhPX3V2Vxa/LfPWxTSELGevupu2hZ/RPxBt1XA8vkPR1r4purpH+dUaeWpevrW3lEd9Orq+VngHUCnpsq18x+OtPBZT1911yvxfx64PzP/X/uKiHhXJRFJC9xUPf3tbX38tvZVF70S/08CxzutyMwLp9sxIpYAnwEWl8f5eGb+RkRcCNwEnAvcC/xMZn5ttoFLdWY9fdXZtBd3M/OZzPy3M3zvCeCyzLwE2ARcERGvBG4Afi8zLwKOAted4ftL0xr0cMqVSxdzyQXLTfqqnV4Xd/d3WwVkZl7cbd/MTOBY+XK0/EngMuBN5fJdwLuA9888ZKk3h1NK3fXq6pmkSNYfBf4KeH42bx4RZwF7gY3A+4BHgWcz82S5yQGg419jRGwDtgGsX79+NodVw7UOp5waWbN99342b1xl61uid1fPJorRO0spkv9vAS8HnsjML/d688z8evke64BXAN/VabMu++7MzLHMHFu9enWvQ0nfMDWcstXUcEpJM5jAlZkPZeZvZOalFK3+DwP/bTYHycxngbuBVwLLI2Lqm8Y64MlZRSz14HBKaXo9E39ErI2Id0TE3wM/TZH0e/bJR8TqiFhePj8H+CHgQeDTFKOFALYCe84wdg25M704OzWccsnoCMsWL2LJ6IjDKaUWvS7u/i2wDLgFeAvwTLnq7Ig4NzOf6bYvsAbYVfbzjwC3ZOYnI+KfgJsi4n8B/wh8cI6/g4bQXC/OOpxS6i6KwTddVkY8xqk++NYNp0b1vKS60E4ZGxvL8fHxfhxKNXDk2ASbb7iL4ydOddcsGR3h/+64zAQuzUJE7M3Msfbl07b4M3NDZRFJXVjrRqrWrKtzRsS3R8Q7I+L+KgKSvDgrVWtGiT8i1kTE2yPic8ADFN8ULNKmSnhxVqpWr4u7v0CR4NdRXOD9eWBPZv6PPsSmBvPirFSdXjN33wf8A/CmzBwHiIjuV4OlebRy6WITvlSBXon/xcAbgPdGxPkUrf7RyqOSJFWmV8mGpzPz/Zn5KuBy4CvAoYh4MCJ+uy8RSgMy6OqeUlV69fH/R+DxzPzXzDwQEYeBJ8r9lvYjQGkQrO6pYdZrVM+fAF8DiIhXAe+mKKW8l6IbSBo63ixdw65X4j+rpSzDTwE7M3N3Zv4aRallaehY3VPDrmfib6mkeTlwV8u6XheGpQXJCWQadr0S/8eAv42IPRQ3Yfk7gIjYSHGhVxo6TiDTsOtVq+e3IuJOikqbt+epim4jwH+tOjhpUJxApmHWs7smMz/bYdkXqwlHqg8nkGlYzbpImyRpYTPxS1LDmPglqWFM/JLUMCZ+SWoYE78kNYyJX5IaxsQvSQ1j4ldlrGcv1ZOF1lQJ69lL9WWLX/POevZSvZn4Ne+sZy/Vm4lf88569lK9VZb4I+KCiPh0eWP2ByLibeXycyPijoh4uHxcUVUMGgzr2Uv1FqdK7M/zG0esAdZk5r0RsYziPr2vB94CPJOZ74mI64EVmbljuvcaGxvL8fHxSuJUdY4cm7CevTRAEbE3M8fal1c2qiczDwIHy+fPRcSDwFrgKuDV5Wa7gLuBaRO/Fibr2Uv11Jc+/ojYAHw3cA9wfvmfwtR/Duf1IwZJUqHyxB8RS4HdwNsz86uz2G9bRIxHxPjhw4erC1BdOQFLGk6VTuCKiFGKpP+RzPxEufipiFiTmQfL6wCHOu2bmTuBnVD08VcZp17ICVjS8KpyVE8AHwQezMz3tqy6DdhaPt8K7KkqBp0ZJ2BJw63Krp7NwM8Al0XEvvLnSuA9wGsi4mHgNeVr1YgTsKThVuWonr8Hosvqy6s6rubOCVjScHPmrl7ACVjScLM6pzrasmktmzeucgKWNIRM/OrKCVjScLKrR5IaxsQvSQ1j4pekhjHxS1LDmPglqWFM/JLUMCZ+SWoYE78kNYyJX5IaxsQvSQ1j4pekhjHxS1LDmPglqWFM/JLUMCZ+SWoYE78kNYyJX5IaxsQvSQ1j4pekhjHxD7Ejxya47/FnOXJsYtChSKoRb7Y+pPbse4Idu/czOjLCiclJbrz6YrZsWjvosCTVgC3+GjvTFvuRYxPs2L2f4ycmeW7iJMdPTLJ9935b/pIAW/y1NZcW+4GjzzM6MsJxJr+xbHRkhANHn2fl0sVVhSxpgbDFX0NzbbGvW3EOJyYnT1t2YnKSdSvOqSJcSQuMib+GplrsraZa7DOxculibrz6YpaMjrBs8SKWjI5w49UX29qXBFTY1RMRHwJeBxzKzP9QLjsXuBnYADwGvDEzj1YVw0I1Hy32LZvWsnnjKg4cfZ51K84x6Uv6hipb/H8GXNG27Hrgzsy8CLizfK0289ViX7l0MZdcsNykL+k0lbX4M/MzEbGhbfFVwKvL57uAu4EdVcWwkNlil1SVfo/qOT8zDwJk5sGIOK/bhhGxDdgGsH79+j6FN7+OHJuYU+JeuXSxCV/SvKvtcM7M3AnsBBgbG8sBhzNrTqCSVFf9HtXzVESsASgfD/X5+H3hBCpJddbvxH8bsLV8vhXY0+fj98Vch2NKUpUqS/wR8THgH4CXRsSBiLgOeA/wmoh4GHhN+XroOIFKUp1VOarn2i6rLq/qmHUxNRxze1sfvxdqJdVBbS/uLnQOx5RUVyb+CjkcU1IdWatHkhrGxC9JDWPil6SGMfFLUsOY+CWpYUz8ktQwJn5JahgTvyQ1jIlfkhrGxC9JDWPil6SGMfFLUsOY+CWpYUz80zhybIL7Hn/WWyZKGiqWZe7Cm6VLGla2+DvwZumShpmJvwNvli5pmJn4O/Bm6ZKG2VAn/jO9ODt1s/QloyMsW7yIJaMj3ixd0tAY2ou7c704683SJQ2roUz8rRdnj1N02WzfvZ/NG1fNKoF7s3RJw2gou3q8OCtJ3Q1l4vfirCR1N5SJ34uzktTdUPbxgxdnJamboU384MVZSepkIF09EXFFRPxzRDwSEdcPIgZJaqq+J/6IOAt4H/AjwMuAayPiZf2OQ5KaahAt/lcAj2TmlzLza8BNwFUDiEOSGmkQiX8t8HjL6wPlstNExLaIGI+I8cOHD/ctOEkadoNI/NFhWb5gQebOzBzLzLHVq1f3ISxJaoZBjOo5AFzQ8nod8OR0O+zdu/fpiPhypVGduVXA04MOYhrGNzfGNzfGNzdzje/bOi2MzBc0tisVEYuALwKXA08AnwfelJkP9DWQeRIR45k5Nug4ujG+uTG+uTG+uakqvr63+DPzZES8Ffgb4CzgQws16UvSQjSQCVyZ+SngU4M4tiQ13VDW6umznYMOoAfjmxvjmxvjm5tK4ut7H78kabBs8UtSw5j4JalhTPwzEBEXRMSnI+LBiHggIt7WYZtXR8RXImJf+fPrfY7xsYj4Qnns8Q7rIyL+oCyMtz8iLu1jbC9tOS/7IuKrEfH2tm36ev4i4kMRcSgi7m9Zdm5E3BERD5ePK7rsu7Xc5uGI2NrH+H4nIh4q//1ujYjlXfad9rNQYXzviognWv4Nr+yyb+VFGrvEd3NLbI9FxL4u+/bj/HXMKX37DGamPz1+gDXApeXzZRTzEF7Wts2rgU8OMMbHgFXTrL8S+GuKmdOvBO4ZUJxnAf8KfNsgzx/wKuBS4P6WZTcC15fPrwdu6LDfucCXyscV5fMVfYrvtcCi8vkNneKbyWehwvjeBfz3Gfz7Pwq8BDgbuK/9b6mq+NrW/y7w6wM8fx1zSr8+g7b4ZyAzD2bmveXz54AH6VBfqOauAj6chc8CyyNizQDiuBx4NDMHOhM7Mz8DPNO2+CpgV/l8F/D6Drv+MHBHZj6TmUeBO4Ar+hFfZt6emSfLl5+lmPU+EF3O30z0pUjjdPFFRABvBD4238edqWlySl8+gyb+WYqIDcB3A/d0WP19EXFfRPx1RLy8r4EV9Y5uj4i9EbGtw/oZFcfrg2vo/gc3yPMHcH5mHoTiDxM4r8M2dTmPP0fxDa6TXp+FKr217Ir6UJduijqcv+8HnsrMh7us7+v5a8spffkMmvhnISKWAruBt2fmV9tW30vRfXEJ8IfAX/Y5vM2ZeSnFfQ5+OSJe1bZ+RsXxqhQRZwNbgL/osHrQ52+m6nAe3wmcBD7SZZNen4WqvB/4dmATcJCiO6XdwM8fcC3Tt/b7dv565JSuu3VYNqtzaOKfoYgYpfgH+khmfqJ9fWZ+NTOPlc8/BYxGxKp+xZeZT5aPh4BbKb5St5p1cbwK/Ahwb2Y+1b5i0Oev9NRU91f5eKjDNgM9j+WFvNcBb86yw7fdDD4LlcjMpzLz65k5Cfxpl+MO+vwtAn4CuLnbNv06f11ySl8+gyb+GSj7BD8IPJiZ7+2yzbeW2xERr6A4t0f6FN+LImLZ1HOKi4D3t212G/BfytE9rwS+MvWVso+6trQGef5a3AZMjZDYCuzpsM3fAK+NiBVlV8Zry2WVi4grgB3Alsz8ty7bzOSzUFV8rdeMfrzLcT8PXBQRF5bfAK+hOO/98kPAQ5l5oNPKfp2/aXJKfz6DVV65HpYf4D9TfJXaD+wrf64Efgn4pXKbtwIPUIxS+Czwn/oY30vK495XxvDOcnlrfEFxy8tHgS8AY30+h99Ekci/pWXZwM4fxX9AB4ETFC2o64CVwJ3Aw+XjueW2Y8AHWvb9OeCR8udn+xjfIxR9u1OfwT8ut30x8KnpPgt9iu/Py8/WfooEtqY9vvL1lRSjWB7tZ3zl8j+b+sy1bDuI89ctp/TlM2jJBklqGLt6JKlhTPyS1DAmfklqGBO/JDWMiV+SGsbEL7WIiHeW1RL3l9UZv7dcvjoiTkTEL7Zt/1hE/F3bsn1tVSF/taxE+c8R8cP9+U2k7gZyz12pjiLi+yhmxV6amRPlzOGzy9VvoJhfcC3wJ227LouICzLz8Yj4rrb3fBnFJKWXU4wX/z8R8R2Z+fUqfxdpOrb4pVPWAE9n5gRAZj6d5fR9ioT/DmBdRLQXxLoF+KmW7VpnJ18F3JSZE5n5LxQTbvpSQkHqxsQvnXI7cEFEfDEi/igifgCKm2YA35qZn+P0JD/l4xT1XwB+DPirlnV1qEYpncbEL5WyKBL3PcA24DBwc0S8haKr5pZys5soWvWtngGORsQ1FHXVW+vo1KEapXQa+/ilFmXf+93A3RHxBYpCWWuB8yPizeVmL46Ii/L0eu43U9RCekvbW9ahKqp0Glv8UimKewNf1LJoE0Xj6EWZuTYzN2TmBuDdFN8CWt1Kcdu89iqJtwHXRMTiiLgQuAj4XCW/gDRDtvilU5YCfxjFTcxPUlyIfZSiUmOr3RRdPv9zakEWt8+7AaCsLj21/IGIuAX4p/I9f9kRPRo0q3NKUsPY1SNJDWPil6SGMfFLUsOY+CWpYUz8ktQwJn5JahgTvyQ1zL8DJGoXf2HL/Z8AAAAASUVORK5CYII=\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -662,18 +696,22 @@ { "data": { "text/plain": [ - "{'id': 'local/ff16115bd3c7163a6e8e',\n", - " 'type': 'debug',\n", - " 'content': {'name': 'example 2',\n", + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", " 'description': 'I really need to write a better description for my data.',\n", " 'author': 'Tim'},\n", " 'acl': {'readers': ['public']},\n", - " 'metadata': {'createdOn': 1612797369664,\n", - " 'createdBy': 'local/bc51a83eea09846dc024',\n", - " 'modifiedOn': 1612797370136,\n", - " 'modifiedBy': 'local/bc51a83eea09846dc024',\n", - " 'txnId': 1612797369664041},\n", - " 'payloads': [{'name': 'p1', 'filename': 'example-data.csv', 'size': 385}]}" + " 'metadata': {'createdOn': 1652894196042,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652894196352,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652894196042028},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}" ] }, "execution_count": 17, @@ -711,16 +749,20 @@ { "data": { "text/plain": [ - "{'id': 'local/ff16115bd3c7163a6e8e',\n", - " 'type': 'debug',\n", - " 'content': {'SAM1': 'Level of CXCR4 expression'},\n", + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'SAM1': 'Level of CXCR4 expression'},\n", " 'acl': {'readers': ['public']},\n", - " 'metadata': {'createdOn': 1612797369664,\n", - " 'createdBy': 'local/bc51a83eea09846dc024',\n", - " 'modifiedOn': 1612797370153,\n", - " 'modifiedBy': 'local/bc51a83eea09846dc024',\n", - " 'txnId': 1612797369664041},\n", - " 'payloads': [{'name': 'p1', 'filename': 'example-data.csv', 'size': 385}]}" + " 'metadata': {'createdOn': 1652894196042,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652894196364,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652894196042028},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}" ] }, "execution_count": 18, @@ -785,8 +827,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "\n", - "401 Client Error: Unauthorized for url: https://127.0.0.1/objects/local/ff16115bd3c7163a6e8e?full=True\n" + "{'message': 'Unauthenticated'}\n", + "401 Client Error: Unauthorized for url: https://127.0.0.1:8443/objects/test/d92d43a269b89a3e6a10?full=True\n" ] } ], @@ -807,18 +849,22 @@ { "data": { "text/plain": [ - "{'id': 'local/ff16115bd3c7163a6e8e',\n", - " 'type': 'debug',\n", - " 'content': {'name': 'example 2',\n", + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", " 'description': 'another example of metadata for CSV payload',\n", " 'author': 'Tim'},\n", " 'acl': {},\n", - " 'metadata': {'createdOn': 1612797369664,\n", - " 'createdBy': 'local/bc51a83eea09846dc024',\n", - " 'modifiedOn': 1612797370197,\n", - " 'modifiedBy': 'local/bc51a83eea09846dc024',\n", - " 'txnId': 1612797370170042},\n", - " 'payloads': [{'name': 'p1', 'filename': 'example-data.csv', 'size': 385}]}" + " 'metadata': {'createdOn': 1652894196042,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652894196383,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652894196378029},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}" ] }, "execution_count": 21, diff --git a/examples/examples.ipynb b/examples/examples.ipynb index 7452f32..5c8e97a 100644 --- a/examples/examples.ipynb +++ b/examples/examples.ipynb @@ -4,12 +4,23 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/sven/7/Work/ThesisMatData/CordraPy/cordra/cordra.py:67: UserWarning: CordraObject may be moved to a new module with a new name in future releases.\n", + " warn(\"CordraObject may be moved to a new module with a new name in future releases.\")\n", + "/home/sven/7/Work/ThesisMatData/CordraPy/cordra/cordra.py:376: UserWarning: Token may be moved to a new module with a new name in future releases.\n", + " warn(\"Token may be moved to a new module with a new name in future releases.\")\n" + ] + } + ], "source": [ "import getpass\n", "import cordra\n", "from lucenequerybuilder import Q\n", - "from io import StringIO\n", + "from io import StringIO, BytesIO\n", "import pandas as pd\n", "import matplotlib.pyplot as plt" ] @@ -27,8 +38,8 @@ "metadata": {}, "outputs": [], "source": [ - "host = \"https://localhost\"\n", - "obj_type = \"debug\"" + "host = \"https://127.0.0.1:8443\"\n", + "obj_type = \"Document\"" ] }, { @@ -52,7 +63,7 @@ } ], "source": [ - "username = \"testuser1\"\n", + "username = \"admin\"\n", "password = getpass.getpass()" ] }, @@ -71,7 +82,8 @@ { "data": { "text/plain": [ - "{'name': 'example 1',\n", + "{'id': 'test/f1f0188d7e74ce2e9b39',\n", + " 'name': 'example 1',\n", " 'description': 'an example of metadata for CSV payload',\n", " 'author': 'John'}" ] @@ -113,7 +125,8 @@ { "data": { "text/plain": [ - "{'name': 'example 2',\n", + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", " 'description': 'another example of metadata for CSV payload',\n", " 'author': 'Tim'}" ] @@ -172,10 +185,12 @@ "{'pageNum': 0,\n", " 'pageSize': -1,\n", " 'size': 2,\n", - " 'results': [{'name': 'example 1',\n", + " 'results': [{'id': 'test/f1f0188d7e74ce2e9b39',\n", + " 'name': 'example 1',\n", " 'description': 'an example of metadata for CSV payload',\n", " 'author': 'John'},\n", - " {'name': 'example 2',\n", + " {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", " 'description': 'another example of metadata for CSV payload',\n", " 'author': 'Tim'}]}" ] @@ -209,18 +224,22 @@ "{'pageNum': 0,\n", " 'pageSize': -1,\n", " 'size': 1,\n", - " 'results': [{'id': 'local/891476bbf9a80dae850c',\n", - " 'type': 'debug',\n", - " 'content': {'name': 'example 1',\n", + " 'results': [{'id': 'test/f1f0188d7e74ce2e9b39',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/f1f0188d7e74ce2e9b39',\n", + " 'name': 'example 1',\n", " 'description': 'an example of metadata for CSV payload',\n", " 'author': 'John'},\n", " 'acl': {'readers': ['public']},\n", - " 'metadata': {'createdOn': 1612797334222,\n", - " 'createdBy': 'local/bc51a83eea09846dc024',\n", - " 'modifiedOn': 1612797334222,\n", - " 'modifiedBy': 'local/bc51a83eea09846dc024',\n", - " 'txnId': 1612797334222035},\n", - " 'payloads': [{'name': 'p1', 'filename': 'example-data.csv', 'size': 385}]}]}" + " 'metadata': {'createdOn': 1652912293301,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652912293301,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652912293301944},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}]}" ] }, "execution_count": 7, @@ -246,18 +265,22 @@ "{'pageNum': 0,\n", " 'pageSize': -1,\n", " 'size': 1,\n", - " 'results': [{'id': 'local/9c53077e1b8e609d087c',\n", - " 'type': 'debug',\n", - " 'content': {'name': 'example 2',\n", + " 'results': [{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", " 'description': 'another example of metadata for CSV payload',\n", " 'author': 'Tim'},\n", " 'acl': {'readers': ['public']},\n", - " 'metadata': {'createdOn': 1612797334352,\n", - " 'createdBy': 'local/bc51a83eea09846dc024',\n", - " 'modifiedOn': 1612797334352,\n", - " 'modifiedBy': 'local/bc51a83eea09846dc024',\n", - " 'txnId': 1612797334352036},\n", - " 'payloads': [{'name': 'p1', 'filename': 'example-data.csv', 'size': 385}]}]}" + " 'metadata': {'createdOn': 1652912293368,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652912293368,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652912293368945},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}]}" ] }, "execution_count": 8, @@ -287,7 +310,7 @@ { "data": { "text/plain": [ - "'local/9c53077e1b8e609d087c'" + "'test/d92d43a269b89a3e6a10'" ] }, "execution_count": 9, @@ -307,7 +330,8 @@ { "data": { "text/plain": [ - "{'name': 'example 2',\n", + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", " 'description': 'another example of metadata for CSV payload',\n", " 'author': 'Tim'}" ] @@ -330,18 +354,22 @@ { "data": { "text/plain": [ - "{'id': 'local/9c53077e1b8e609d087c',\n", - " 'type': 'debug',\n", - " 'content': {'name': 'example 2',\n", + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", " 'description': 'another example of metadata for CSV payload',\n", " 'author': 'Tim'},\n", " 'acl': {'readers': ['public']},\n", - " 'metadata': {'createdOn': 1612797334352,\n", - " 'createdBy': 'local/bc51a83eea09846dc024',\n", - " 'modifiedOn': 1612797334352,\n", - " 'modifiedBy': 'local/bc51a83eea09846dc024',\n", - " 'txnId': 1612797334352036},\n", - " 'payloads': [{'name': 'p1', 'filename': 'example-data.csv', 'size': 385}]}" + " 'metadata': {'createdOn': 1652912293368,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652912293368,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652912293368945},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}" ] }, "execution_count": 11, @@ -383,7 +411,7 @@ { "data": { "text/plain": [ - "'\\ufeff\"SAM0\",\"SAM1\"\\r\\n1.00,2.302389071\\r\\n2.00,3.71503899\\r\\n3.00,9.426125622\\r\\n4.00,11.34529125\\r\\n5.00,11.87704484\\r\\n6.00,19.01325695\\r\\n7.00,21.52353652\\r\\n8.00,28.28670056\\r\\n9.00,29.55737761\\r\\n10.00,25.89582707\\r\\n11.00,31.57982065\\r\\n12.00,36.02452105\\r\\n13.00,39.47686412\\r\\n14.00,44.41192202\\r\\n15.00,43.36098819\\r\\n16.00,48.61525381\\r\\n17.00,53.91222295\\r\\n18.00,54.28420278\\r\\n19.00,53.1378195\\r\\n20.00,55.88015939'" + "b'\\xef\\xbb\\xbf\"SAM0\",\"SAM1\"\\r\\n1.00,2.302389071\\r\\n2.00,3.71503899\\r\\n3.00,9.426125622\\r\\n4.00,11.34529125\\r\\n5.00,11.87704484\\r\\n6.00,19.01325695\\r\\n7.00,21.52353652\\r\\n8.00,28.28670056\\r\\n9.00,29.55737761\\r\\n10.00,25.89582707\\r\\n11.00,31.57982065\\r\\n12.00,36.02452105\\r\\n13.00,39.47686412\\r\\n14.00,44.41192202\\r\\n15.00,43.36098819\\r\\n16.00,48.61525381\\r\\n17.00,53.91222295\\r\\n18.00,54.28420278\\r\\n19.00,53.1378195\\r\\n20.00,55.88015939'" ] }, "execution_count": 13, @@ -428,102 +456,102 @@ " \n", " \n", " \n", - " 0\n", + " 0\n", " 1.0\n", " 2.302389\n", " \n", " \n", - " 1\n", + " 1\n", " 2.0\n", " 3.715039\n", " \n", " \n", - " 2\n", + " 2\n", " 3.0\n", " 9.426126\n", " \n", " \n", - " 3\n", + " 3\n", " 4.0\n", " 11.345291\n", " \n", " \n", - " 4\n", + " 4\n", " 5.0\n", " 11.877045\n", " \n", " \n", - " 5\n", + " 5\n", " 6.0\n", " 19.013257\n", " \n", " \n", - " 6\n", + " 6\n", " 7.0\n", " 21.523537\n", " \n", " \n", - " 7\n", + " 7\n", " 8.0\n", " 28.286701\n", " \n", " \n", - " 8\n", + " 8\n", " 9.0\n", " 29.557378\n", " \n", " \n", - " 9\n", + " 9\n", " 10.0\n", " 25.895827\n", " \n", " \n", - " 10\n", + " 10\n", " 11.0\n", " 31.579821\n", " \n", " \n", - " 11\n", + " 11\n", " 12.0\n", " 36.024521\n", " \n", " \n", - " 12\n", + " 12\n", " 13.0\n", " 39.476864\n", " \n", " \n", - " 13\n", + " 13\n", " 14.0\n", " 44.411922\n", " \n", " \n", - " 14\n", + " 14\n", " 15.0\n", " 43.360988\n", " \n", " \n", - " 15\n", + " 15\n", " 16.0\n", " 48.615254\n", " \n", " \n", - " 16\n", + " 16\n", " 17.0\n", " 53.912223\n", " \n", " \n", - " 17\n", + " 17\n", " 18.0\n", " 54.284203\n", " \n", " \n", - " 18\n", + " 18\n", " 19.0\n", " 53.137819\n", " \n", " \n", - " 19\n", + " 19\n", " 20.0\n", " 55.880159\n", " \n", @@ -561,7 +589,7 @@ } ], "source": [ - "df = pd.read_csv(StringIO(payload))\n", + "df = pd.read_csv(BytesIO(payload))\n", "df" ] }, @@ -572,7 +600,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -614,18 +642,22 @@ { "data": { "text/plain": [ - "{'id': 'local/9c53077e1b8e609d087c',\n", - " 'type': 'debug',\n", - " 'content': {'name': 'example 2',\n", + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", " 'description': 'I really need to write a better description for my data.',\n", " 'author': 'Tim'},\n", " 'acl': {'readers': ['public']},\n", - " 'metadata': {'createdOn': 1612797334352,\n", - " 'createdBy': 'local/bc51a83eea09846dc024',\n", - " 'modifiedOn': 1612797334802,\n", - " 'modifiedBy': 'local/bc51a83eea09846dc024',\n", - " 'txnId': 1612797334352036},\n", - " 'payloads': [{'name': 'p1', 'filename': 'example-data.csv', 'size': 385}]}" + " 'metadata': {'createdOn': 1652912293368,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652912293670,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652912293368945},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}" ] }, "execution_count": 16, @@ -664,16 +696,20 @@ { "data": { "text/plain": [ - "{'id': 'local/9c53077e1b8e609d087c',\n", - " 'type': 'debug',\n", - " 'content': {'SAM1': 'Level of CXCR4 expression'},\n", + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'SAM1': 'Level of CXCR4 expression'},\n", " 'acl': {'readers': ['public']},\n", - " 'metadata': {'createdOn': 1612797334352,\n", - " 'createdBy': 'local/bc51a83eea09846dc024',\n", - " 'modifiedOn': 1612797334842,\n", - " 'modifiedBy': 'local/bc51a83eea09846dc024',\n", - " 'txnId': 1612797334352036},\n", - " 'payloads': [{'name': 'p1', 'filename': 'example-data.csv', 'size': 385}]}" + " 'metadata': {'createdOn': 1652912293368,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652912293704,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652912293368945},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}" ] }, "execution_count": 17, @@ -740,8 +776,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "\n", - "401 Client Error: Unauthorized for url: https://localhost/objects/local/9c53077e1b8e609d087c?full=True\n" + "{'message': 'Unauthenticated'}\n", + "401 Client Error: Unauthorized for url: https://127.0.0.1:8443/objects/test/d92d43a269b89a3e6a10?full=True\n" ] } ], @@ -762,18 +798,22 @@ { "data": { "text/plain": [ - "{'id': 'local/9c53077e1b8e609d087c',\n", - " 'type': 'debug',\n", - " 'content': {'name': 'example 2',\n", + "{'id': 'test/d92d43a269b89a3e6a10',\n", + " 'type': 'Document',\n", + " 'content': {'id': 'test/d92d43a269b89a3e6a10',\n", + " 'name': 'example 2',\n", " 'description': 'another example of metadata for CSV payload',\n", " 'author': 'Tim'},\n", " 'acl': {},\n", - " 'metadata': {'createdOn': 1612797334352,\n", - " 'createdBy': 'local/bc51a83eea09846dc024',\n", - " 'modifiedOn': 1612797334920,\n", - " 'modifiedBy': 'local/bc51a83eea09846dc024',\n", - " 'txnId': 1612797334892037},\n", - " 'payloads': [{'name': 'p1', 'filename': 'example-data.csv', 'size': 385}]}" + " 'metadata': {'createdOn': 1652912293368,\n", + " 'createdBy': 'admin',\n", + " 'modifiedOn': 1652912293743,\n", + " 'modifiedBy': 'admin',\n", + " 'txnId': 1652912293738946},\n", + " 'payloads': [{'name': 'p1',\n", + " 'filename': 'example-data.csv',\n", + " 'mediaType': 'application/octet-stream',\n", + " 'size': 385}]}" ] }, "execution_count": 20, @@ -798,6 +838,13 @@ "execution_count": 21, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{}\n" + ] + }, { "data": { "text/plain": [ @@ -819,6 +866,13 @@ "execution_count": 22, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{}\n" + ] + }, { "data": { "text/plain": [ @@ -835,12 +889,57 @@ "response" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Delete parts of objects" + ] + }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'id': 'test/90c5eb8ac8911ec5f400', 'name': 'hello', 'otherName': 'bye'}\n", + "https://localhost:8443/objects/test/90c5eb8ac8911ec5f400\n", + "\n", + "{'id': 'test/90c5eb8ac8911ec5f400', 'name': 'hello', 'otherName': 'bye'}\n" + ] + } + ], + "source": [ + "from cordra.cordra import set_auth\n", + "import requests\n", + "import json\n", + "\n", + "r = requests.post(\"https://localhost:8443/objects/\", params={\"type\": \"Document\"}, json={\"name\": \"hello\", \"otherName\": \"bye\"},\n", + " auth=set_auth(username,password), verify=False)\n", + "\n", + "print(r.json())\n", + "\n", + "id1 = r.json()[\"id\"]\n", + "\n", + "url = f\"https://localhost:8443/objects/{id1}\"\n", + "print(url)\n", + "\n", + "r = requests.delete(url, params={\"jsonPointer\": \"/otherName\"},\n", + " auth=set_auth(username,password),verify=False)\n", + "\n", + "print(r)\n", + "\n", + "print(requests.get(url, auth=set_auth(username,password),verify=False).json())\n", + "\n", + "# r = requests.put(url, params={\"jsonPointer\": \"/otherName\"}, data='null',\n", + "# auth=set_auth(username,password),verify=False)\n", + "\n", + "# print(r)\n", + "# print(requests.get(url, auth=set_auth(username,password),verify=False).json())" + ] } ], "metadata": { diff --git a/tests/CRUD_CordraClient.py b/tests/CRUD_CordraClient.py new file mode 100644 index 0000000..9068bf8 --- /dev/null +++ b/tests/CRUD_CordraClient.py @@ -0,0 +1,347 @@ +"""Check the CordraClient Class and its functionality. Should be able to fully reproduce the +functionality of the Cordra REST API. This includes the following tests: +* Authorize using user / password +* Authorize using a secret key +* Maintain a token for subsequent authorization +* Delete an authorization token +* Create a cordra object and read back an identical object +* Create a cordra object with a json payload and read back an identical json object +* Create a cordra object with a csv payload and read back an identical csv object +* Create a cordra object with an image payload and read back an identical image +* Update a cordra object and read back an object with the changes +* Update a json payload and read back an identical json object +* Update a csv payload and read back an identical csv object +* Update an image payload and read back an identical image +* Set the ACL on a cordra object on create and verify only specified readers have access +* Update the ACL of a cordra object and verify only current specified readers have access +* Delete a cordra object with a user and verify it doesn't exist with admin +* Delete a cordra object attribute and read the object to verify deletion +* Delete a cordra object payload and read the object to verify deletion +* Query cordra for all created objects and delete them +""" + +# from cordra import CordraObject +from cordra import CordraClient, CordraObject +from io import BytesIO +from PIL import Image +import os +import json +import requests +import copy + + +# ---------------- # +# Helper Functions # +# ---------------- # +def auth_wrapper(func): + '''Reusability of the auth code''' + def inner(): + client = CordraClient( + host="https://localhost:8443/", + username="admin", password="admin", + verify=False + ) + func(client) + client.delete_auth() + + return inner + + +def acl_wrapper(func): + '''Generating clients for admin and two users.''' + def inner(): + client = CordraClient( + host="https://localhost:8443/", + username="admin", password="admin", + verify=False + ) + + client_user1 = CordraClient( + host="https://localhost:8443/", + username="user1", password="user.1234", + verify=False + ) + + client_user2 = CordraClient( + host="https://localhost:8443/", + username="user2", password="user.1234", + verify=False + ) + + user1_id = client.find('/username:"user1"')["results"][0]["id"] + user2_id = client.find('/username:"user2"')["results"][0]["id"] + + func(client, user1_id, client_user1, user2_id, client_user2) + client.delete_auth() + client_user1.delete_auth() + client_user2.delete_auth() + + return inner + + +# ----- # +# Setup # +# ----- # + +@auth_wrapper +def setup(client_admin): + '''Creating users with the admin account''' + + client_admin.create({"username": "user1", "password": "user.1234"}, "User") + client_admin.create({"username": "user2", "password": "user.1234"}, "User") + + +@auth_wrapper +def teardown(client_admin): + '''Deleting all objects and users with the admin account. Excludes schemas. + Query cordra for all created objects and delete them.''' + + r = client_admin.find("*", pageSize=-1, full=True) + all_objectIds = [ri['id'] for ri in r['results'] if ri['type']!='Schema'] + + for obj_id in all_objectIds: + client_admin.delete(obj_id) + + +# ----- # +# Tests # +# ----- # +tests = [] + +@auth_wrapper +def test(client): + '''Authorize using user / password. + Maintain a token for subsequent authorization. + Delete an authorization token.''' + + client.check_auth() + +tests.append(test) + + +def test(): + '''Authorize using a secret key''' + #TODO: implement secret key in cordraClient then write this test + pass + +tests.append(test) + + +@auth_wrapper +def test(client): + '''Create a cordra object and read back an identical object. + Update a cordra object and read back an object with the changes.''' + + # Create object + obj1 = {"@type": "Document", "name": "test1"} + r = client.create(obj1, "Document") + obj1_id = r["id"] + obj1["id"] = obj1_id + assert obj1 == r + + obj1_clone = client.read(obj1_id) + assert obj1 == obj1_clone + + # Update object + obj1.update({"name": "test1-update"}) + r = client.update(obj1, obj1_id) + assert obj1 == r + + obj1_clone = client.read(obj1_id) + assert obj1 == obj1_clone + +tests.append(test) + + + +@auth_wrapper +def test(client): + '''Create a cordra object with a json payload and read back an identical json object. + Update a json payload and read back an identical json object.''' + + obj1 = {"@type": "Document", "name": "test1"} + pay1 = {"some_info": 123} + + # Create the obj and pay by encoding payload into bytes object + r = client.create(obj1, "Document", payloads={"json": json.dumps(pay1)}) + obj1_id = r["id"] + obj1["id"] = obj1_id + assert obj1 == r + + obj1_clone, pay1_clone = client.read(obj1_id, getObjPayTuple=True) + assert obj1 == obj1_clone + assert pay1 == pay1_clone["json"] + + # Update the payload + pay1 = {"other_info": 456} + r = client.update(obj1, obj1_id, payloads={"json": json.dumps(pay1)}) + + obj1_clone, pay1_clone = client.read(obj1_id, getObjPayTuple=True) + assert obj1 == obj1_clone + assert pay1 == pay1_clone["json"] + +tests.append(test) + + +@auth_wrapper +def test(client): + '''Create a cordra object with a csv payload and read back an identical csv object. + Update a csv payload and read back an identical csv object.''' + + # Create csv + csv = "" + for i in range(10): + csv += f"{2*i},{2*i+1}\n" + + csv = csv.encode() + + # Write csv to cordra + obj1 = {"@type": "Document", "name": "test1"} + r = client.create(obj1, "Document", payloads={"csv": csv}) + obj1_id = r["id"] + obj1["id"] = obj1_id + assert obj1 == r + + obj1_clone, pay1_clone = client.read(obj1_id, getObjPayTuple=True) + assert obj1 == obj1_clone + assert csv == pay1_clone["csv"] + + # Update csv in cordra + csv = "\n".join(csv.decode("utf-8").split("\n")[:5]).encode() + client.update(obj1, obj1_id, payloads={"csv": csv}) + + obj1_clone, pay1_clone = client.read(obj1_id, getObjPayTuple=True) + assert obj1 == obj1_clone + assert csv == pay1_clone["csv"] + +tests.append(test) + + +@auth_wrapper +def test(client): + '''Create a cordra object with an image payload and read back an identical image. + Update an image payload and read back an identical image.''' + + # Create image + stream = BytesIO() + A = Image.radial_gradient("L").resize((11,11)) + A.save(stream, format="PNG") # Write a png image to bytes object + image = stream.getvalue() + + # Write image to cordra + obj1 = {"@type": "Document", "name": "test1"} + r = client.create(obj1, "Document", payloads={"image": image}) + obj1_id = r["id"] + obj1["id"] = obj1_id + assert obj1 == r + + obj1_clone, pay1_clone = client.read(obj1_id, getObjPayTuple=True) + assert obj1 == obj1_clone + assert image == pay1_clone["image"] + + # Update image + stream = BytesIO() + A = A.resize((9,9)) + A.save(stream, format="PNG") # Write a png image to bytes object + image = stream.getvalue() + + r = client.update(obj1, obj1_id, payloads={"image": image}) + + obj1_clone, pay1_clone = client.read(obj1_id, getObjPayTuple=True) + assert obj1 == obj1_clone + assert image == pay1_clone["image"] + +tests.append(test) + + +@acl_wrapper +def test(client, user1_id, client_user1, user2_id, client_user2): + '''Set the ACL on a cordra object on create and verify only specified readers have access. + Update the ACL of a cordra object and verify only current specified readers have access.''' + + # Create object and only allow user1 access + r = client.create( + {"name": "unaccessible"}, + "Document", + acls={"readers":[user1_id],"writers":None} + ) + obj_id = r[0]['id'] + + client_user1.read(obj_id) + + # this should return a 403 error + try: + client_user2.read(obj_id) + except Exception as e: + print("Successful error: ", e) + + # Update access to user2 + r = client.update(dict(), obj_id, acls={"readers":[user2_id],"writers":None}) + + client_user2.read(obj_id) + + # this should return a 403 error + try: + client_user1.read(obj_id) + except Exception as e: + print("Successful error: ", e) + +tests.append(test) + + +@acl_wrapper +def test(client, user1_id, client_user1, user2_id, client_user2): + '''Delete a cordra object with a user and verify it doesn't exist with admin''' + r = client_user1.create({"deleteThis": True}, "Document") + obj_id = r["id"] + client_user1.delete(obj_id) + + # should return a 404 not found error + try: + client.read(obj_id) + except Exception as e: + print("Successful error: ", e) + +tests.append(test) + + +# @acl_wrapper +# def test(client, user1_id, client_user1, user2_id, client_user2): +# '''Delete a cordra object attribute and read the object to verify deletion''' +# r = client_user1.create({"keepThis": True, "deleteThis": True}, "Document") +# obj_id = r["id"] + +# print(client_user1.params["token"]) +# r = CordraObject.update(host="https://localhost:8443/", obj_id=obj_id, obj_json=None, jsonPointer="/deleteThis", token=client_user1.params["token"], verify=False) + +# print(r) + + +# # r = CordraObject.delete(host="https://localhost:8443/", obj_id=obj_id, jsonPointer="deleteThis", token=client_user1.params["token"], verify=False) + +# # print(r) + +# obj = client.read(obj_id) +# print(obj) + +# assert "keepThis" in obj +# assert "deleteThis" not in obj + +# tests.append(test) + + +# @acl_wrapper +# def test(client, user1_id, client_user1, user2_id, client_user2): +# '''Delete a cordra object payload and read the object to verify deletion''' + +# tests.append(test) + + + +if __name__ == "__main__": + try: + setup() + for test in tests: + test() + finally: + teardown()