Skip to content

Changed tokens to be handled externally, CI fixes, and fixed package structure. #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,13 @@ jobs:
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide, we further relax this
flake8 . --count --exit-zero --max-complexity=30 --max-line-length=130 --statistics

- name: Build
run: |
pip install build
python -m build
- name: Install
run: |
pip install .
- name: Test with pytest
run: |
if [ -f tests/requirements.txt ]; then pip install -r tests/requirements.txt; fi
Expand Down
81 changes: 45 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ Alternatively, you can clone this repository and install manually:
```bash
git clone [email protected]:cs3org/cs3-python-client.git
cd cs3-python-client
pip install -e .
export PYTHONPATH="path/to/cs3-python-client/:$PYTHONPATH"
pip install .
```


Expand Down Expand Up @@ -112,129 +111,136 @@ lock_expiration = 1800

To use `cs3client`, you first need to import and configure it. Here's a simple example of how to set up and start using the client. For configuration see [Configuration](#configuration). For more in depth examples see `cs3-python-client/examples/`.

### Initilization
### Initilization and Authentication
```python
import logging
import configparser
from cs3client import CS3Client
from cs3resource import Resource
from cs3client.cs3client import CS3Client

config = configparser.ConfigParser()
with open("default.conf") as fdef:
config.read_file(fdef)

log = logging.getLogger(__name__)

client = CS3Client(config, "cs3client", log)
# client.auth.set_token("<your_token_here>")
# OR

# Set client secret
client.auth.set_client_secret("<your_client_secret_here>")
# Checks if token is expired if not return ('x-access-token', <token>)
# if expired, request a new token from reva
auth_token = client.auth.get_token()

# OR if you already have a reva token
# Checks if token is expired if not return (x-access-token', <token>)
# if expired, throws an AuthenticationException (so you can refresh your reva token)
token = "<your_reva_token>"
auth_token = client.auth.check_token(token)
```

### File Example
```python
# mkdir
directory_resource = Resource.from_file_ref_and_endpoint(f"/eos/user/r/rwelande/test_directory")
res = client.file.make_dir(directory_resource)
res = client.file.make_dir(client.auth.get_token(), directory_resource)

# touchfile
touch_resource = Resource.from_file_ref_and_endpoint("/eos/user/r/rwelande/touch_file.txt")
res = client.file.touch_file(touch_resource)
res = client.file.touch_file(client.auth.get_token(), touch_resource)

# setxattr
resource = Resource.from_file_ref_and_endpoint("/eos/user/r/rwelande/text_file.txt")
res = client.file.set_xattr(resource, "iop.wopi.lastwritetime", str(1720696124))
res = client.file.set_xattr(client.auth.get_token(), resource, "iop.wopi.lastwritetime", str(1720696124))

# rmxattr
res = client.file.remove_xattr(resource, "iop.wopi.lastwritetime")
res = client.file.remove_xattr(client.auth.get_token(), resource, "iop.wopi.lastwritetime")

# stat
res = client.file.stat(resource)
res = client.file.stat(client.auth.get_token(), resource)

# removefile
res = client.file.remove_file(touch_resource)
res = client.file.remove_file(client.auth.get_token(), touch_resource)

# rename
rename_resource = Resource.from_file_ref_and_endpoint("/eos/user/r/rwelande/rename_file.txt")
res = client.file.rename_file(resource, rename_resource)
res = client.file.rename_file(client.auth.get_token(), resource, rename_resource)

# writefile
content = b"Hello World"
size = len(content)
res = client.file.write_file(rename_resource, content, size)
res = client.file.write_file(client.auth.get_token(), rename_resource, content, size)

# listdir
list_directory_resource = Resource.from_file_ref_and_endpoint("/eos/user/r/rwelande")
res = client.file.list_dir(list_directory_resource)
res = client.file.list_dir(client.auth.get_token(), list_directory_resource)


# readfile
file_res = client.file.read_file(rename_resource)
file_res = client.file.read_file(client.auth.get_token(), rename_resource)
```

### Share Example
```python
# Create share #
resource = Resource.from_file_ref_and_endpoint("/eos/user/r/<some_username>/text.txt")
resource_info = client.file.stat(resource)
resource_info = client.file.stat(client.auth.get_token(), resource)
user = client.user.get_user_by_claim("username", "<some_username>")
res = client.share.create_share(resource_info, user.id.opaque_id, user.id.idp, "EDITOR", "USER")
res = client.share.create_share(client.auth.get_token(), resource_info, user.id.opaque_id, user.id.idp, "EDITOR", "USER")

# List existing shares #
filter_list = []
filter = client.share.create_share_filter(resource_id=resource_info.id, filter_type="TYPE_RESOURCE_ID")
filter_list.append(filter)
filter = client.share.create_share_filter(share_state="SHARE_STATE_PENDING", filter_type="TYPE_STATE")
filter_list.append(filter)
res, _ = client.share.list_existing_shares()
res, _ = client.share.list_existing_shares(client.auth.get_token(), )

# Get share #
share_id = "58"
res = client.share.get_share(opaque_id=share_id)
res = client.share.get_share(client.auth.get_token(), opaque_id=share_id)

# update share #
res = client.share.update_share(opaque_id=share_id, role="VIEWER")
res = client.share.update_share(client.auth.get_token(), opaque_id=share_id, role="VIEWER")

# remove share #
res = client.share.remove_share(opaque_id=share_id)
res = client.share.remove_share(client.auth.get_token(), opaque_id=share_id)

# List existing received shares #
filter_list = []
filter = client.share.create_share_filter(share_state="SHARE_STATE_ACCEPTED", filter_type="TYPE_STATE")
filter_list.append(filter)
res, _ = client.share.list_received_existing_shares()
res, _ = client.share.list_received_existing_shares(client.auth.get_token())

# get received share #
received_share = client.share.get_received_share(opaque_id=share_id)
received_share = client.share.get_received_share(client.auth.get_token(), opaque_id=share_id)

# update recieved share #
res = client.share.update_received_share(received_share=received_share, state="SHARE_STATE_ACCEPTED")
res = client.share.update_received_share(client.auth.get_token(), received_share=received_share, state="SHARE_STATE_ACCEPTED")

# create public share #
res = client.share.create_public_share(resource_info, role="VIEWER")
res = client.share.create_public_share(client.auth.get_token(), resource_info, role="VIEWER")

# list existing public shares #
filter_list = []
filter = client.share.create_public_share_filter(resource_id=resource_info.id, filter_type="TYPE_RESOURCE_ID")
filter_list.append(filter)
res, _ = client.share.list_existing_public_shares(filter_list=filter_list)

res = client.share.get_public_share(opaque_id=share_id, sign=True)
res = client.share.get_public_share(client.auth.get_token(), opaque_id=share_id, sign=True)
# OR token = "<token>"
# res = client.share.get_public_share(token=token, sign=True)

# update public share #
res = client.share.update_public_share(type="TYPE_PASSWORD", token=token, role="VIEWER", password="hello")
res = client.share.update_public_share(client.auth.get_token(), type="TYPE_PASSWORD", token=token, role="VIEWER", password="hello")

# remove public share #
res = client.share.remove_public_share(token=token)
res = client.share.remove_public_share(client.auth.get_token(), token=token)

```

### User Example
```python
# find_user
res = client.user.find_users("rwel")
res = client.user.find_users(client.auth.get_token(), "rwel")

# get_user
res = client.user.get_user("https://auth.cern.ch/auth/realms/cern", "asdoiqwe")
Expand All @@ -253,21 +259,21 @@ res = client.user.get_user_by_claim("username", "rwelande")
### App Example
```python
# list_app_providers
res = client.app.list_app_providers()
res = client.app.list_app_providers(client.auth.get_token())

# open_in_app
resource = Resource.from_file_ref_and_endpoint("/eos/user/r/rwelande/collabora.odt")
res = client.app.open_in_app(resource)
res = client.app.open_in_app(client.auth.get_token(), resource)
```

### Checkpoint Example
```python
# list file versions
resource = Resource.from_file_ref_and_endpoint("/eos/user/r/rwelande/test.md")
res = client.checkpoint.list_file_versions(resource)
res = client.checkpoint.list_file_versions(client.auth.get_token(), resource)

# restore file version
res = client.checkpoint.restore_file_version(resource, "1722936250.0569fa2f")
res = client.checkpoint.restore_file_version(client.auth.get_token(), resource, "1722936250.0569fa2f")
```

## Documentation
Expand All @@ -282,6 +288,9 @@ make html
## Unit tests

```bash
# install library
pip install .
# run unit tests
pytest --cov-report term --cov=serc tests/
```

Expand Down
File renamed without changes.
25 changes: 13 additions & 12 deletions src/app.py → cs3client/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@

Authors: Rasmus Welander, Diogo Castro, Giuseppe Lo Presti.
Emails: [email protected], [email protected], [email protected]
Last updated: 19/08/2024
Last updated: 28/08/2024
"""

import logging
from auth import Auth
from cs3resource import Resource
import cs3.app.registry.v1beta1.registry_api_pb2 as cs3arreg
import cs3.app.registry.v1beta1.resources_pb2 as cs3arres
import cs3.gateway.v1beta1.gateway_api_pb2 as cs3gw
import cs3.app.provider.v1beta1.resources_pb2 as cs3apr
from cs3.gateway.v1beta1.gateway_api_pb2_grpc import GatewayAPIStub
from statuscodehandler import StatusCodeHandler
from config import Config

from cs3client.cs3resource import Resource
from cs3client.statuscodehandler import StatusCodeHandler
from cs3client.config import Config


class App:
Expand All @@ -28,7 +28,6 @@ def __init__(
config: Config,
log: logging.Logger,
gateway: GatewayAPIStub,
auth: Auth,
status_code_handler: StatusCodeHandler,
) -> None:
"""
Expand All @@ -37,20 +36,21 @@ def __init__(
:param config: Config object containing the configuration parameters.
:param log: Logger instance for logging.
:param gateway: GatewayAPIStub instance for interacting with CS3 Gateway.
:param auth: An instance of the auth class.
:param status_code_handler: An instance of the StatusCodeHandler class.
"""
self._status_code_handler: StatusCodeHandler = status_code_handler
self._gateway: GatewayAPIStub = gateway
self._log: logging.Logger = log
self._config: Config = config
self._auth: Auth = auth

def open_in_app(self, resource: Resource, view_mode: str = None, app: str = None) -> cs3apr.OpenInAppURL:
def open_in_app(
self, auth_token: tuple, resource: Resource, view_mode: str = None, app: str = None
) -> cs3apr.OpenInAppURL:
"""
Open a file in an app, given the resource, view mode (VIEW_MODE_VIEW_ONLY, VIEW_MODE_READ_ONLY,
VIEW_MODE_READ_WRITE, VIEW_MODE_PREVIEW), and app name.

:param auth_token: tuple in the form ('x-access-token', <token> (see auth.get_token/auth.check_token)
:param resource: Resource object containing the resource information.
:param view_mode: View mode of the app.
:param app: App name.
Expand All @@ -63,21 +63,22 @@ def open_in_app(self, resource: Resource, view_mode: str = None, app: str = None
if view_mode:
view_mode_type = cs3gw.OpenInAppRequest.ViewMode.Value(view_mode)
req = cs3gw.OpenInAppRequest(ref=resource.ref, view_mode=view_mode_type, app=app)
res = self._gateway.OpenInApp(request=req, metadata=[self._auth.get_token()])
res = self._gateway.OpenInApp(request=req, metadata=[auth_token])
self._status_code_handler.handle_errors(res.status, "open in app", f"{resource.get_file_ref_str()}")
self._log.debug(f'msg="Invoked OpenInApp" {resource.get_file_ref_str()} trace="{res.status.trace}"')
return res.OpenInAppURL

def list_app_providers(self) -> list[cs3arres.ProviderInfo]:
def list_app_providers(self, auth_token: dict) -> list[cs3arres.ProviderInfo]:
"""
list_app_providers lists all the app providers.

:param auth_token: tuple in the form ('x-access-token', <token> (see auth.get_token/auth.check_token)
:return: List of app providers.
:raises: AuthenticationException (Operation not permitted)
:raises: UnknownException (Unknown error)
"""
req = cs3arreg.ListAppProvidersRequest()
res = self._gateway.ListAppProviders(request=req, metadata=[self._auth.get_token()])
res = self._gateway.ListAppProviders(request=req, metadata=[auth_token])
self._status_code_handler.handle_errors(res.status, "list app providers")
self._log.debug(f'msg="Invoked ListAppProviders" res_count="{len(res.providers)}" trace="{res.status.trace}"')
return res.providers
Loading
Loading