Skip to content
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

Add a TimersClient app scope helper for data_access + example doc #1074

Merged
merged 4 commits into from
Oct 25, 2024
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Added
~~~~~

- Add ``TimersClient.add_app_transfer_data_access_scope`` for ``TimersClient``
instances which are integrated with ``GlobusApp``. This method registers the
nested scope dependency for a ``data_access`` requirement for a transfer
timer. (:pr:`NUMBER`)
2 changes: 1 addition & 1 deletion docs/examples/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ Each of these pages contains an example of a piece of SDK functionality.
transfer_relative_deadlines
recursive_ls
endpoint_type_enum
timer_operations/index
timer_management/index
guest_collection_creation
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,11 @@
import datetime

import globus_sdk
from globus_sdk.experimental.globus_app import UserApp

# tutorial client ID
# we recommend replacing this with your own client for any production use-cases
CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2"
NATIVE_CLIENT = globus_sdk.NativeAppAuthClient(CLIENT_ID)


def do_login_flow():
# we will want to request a 'timer' scope for managing timers
scope = globus_sdk.TimersClient.scopes.timer

# run the login flow, finishing with a token exchange
NATIVE_CLIENT.oauth2_start_flow(requested_scopes=scope)
authorize_url = NATIVE_CLIENT.oauth2_get_authorize_url()
print(f"Please go to this URL and login:\n\n{authorize_url}\n")
auth_code = input("Please enter the code here: ").strip()
tokens = NATIVE_CLIENT.oauth2_exchange_code_for_tokens(auth_code)

# pull out the tokens for Globus Timers from the response
return tokens.by_resource_server[globus_sdk.TimersClient.resource_server]


def create_timers_client():
tokens = do_login_flow()
return globus_sdk.TimersClient(
authorizer=globus_sdk.AccessTokenAuthorizer(tokens["access_token"])
)
# Tutorial Client ID - <replace this with your own client>
NATIVE_CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2"
USER_APP = UserApp("manage-timers-example", client_id=NATIVE_CLIENT_ID)


def main():
Expand All @@ -53,7 +31,7 @@ def main():
)
args = parser.parse_args()

client = create_timers_client()
client = globus_sdk.TimersClient(app=USER_APP)

body = globus_sdk.TransferData(
source_endpoint=args.SOURCE_COLLECTION,
Expand Down
89 changes: 89 additions & 0 deletions docs/examples/timer_management/create_timer_data_access.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env python

import argparse
import datetime

import globus_sdk
from globus_sdk.experimental.globus_app import UserApp

# Tutorial Client ID - <replace this with your own client>
NATIVE_CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2"
USER_APP = UserApp("manage-timers-example", client_id=NATIVE_CLIENT_ID)


def uses_data_access(transfer_client, collection_id):
doc = transfer_client.get_endpoint(collection_id)
if doc["entity_type"] != "GCSv5_mapped_collection":
return False
if doc["high_assurance"]:
return False
return True


def main():
parser = argparse.ArgumentParser()
# the source, destination, and path to a file or dir to sync
parser.add_argument("SOURCE_COLLECTION")
parser.add_argument("DESTINATION_COLLECTION")
parser.add_argument("PATH")
parser.add_argument(
"--interval-seconds",
help="How frequently the timer runs, in seconds (default: 1 hour)",
default=3600,
type=int,
)
parser.add_argument(
"--days",
help="How many days to run the timer (default: 2)",
default=2,
type=int,
)
args = parser.parse_args()

timers_client = globus_sdk.TimersClient(app=USER_APP)
transfer_client = globus_sdk.TransferClient(app=USER_APP)

# check if the source or destination use 'data_access' scopes
# if so, register these requirements with the app
if uses_data_access(transfer_client, args.SOURCE_COLLECTION):
timers_client.add_app_transfer_data_access_scope(args.SOURCE_COLLECTION)
if uses_data_access(transfer_client, args.DESTINATION_COLLECTION):
timers_client.add_app_transfer_data_access_scope(args.DESTINATION_COLLECTION)

# from this point onwards, the example is the same as the basic create_timer.py
# script -- we've handled the nuance of data_access
#
# when the timer submission runs, you *may* be prompted to login again, if
# 'data_access' requirements were detected

body = globus_sdk.TransferData(
source_endpoint=args.SOURCE_COLLECTION,
destination_endpoint=args.DESTINATION_COLLECTION,
)
body.add_item(args.PATH, args.PATH)

# the timer will run until the end date, on whatever interval was requested
schedule = globus_sdk.RecurringTimerSchedule(
interval_seconds=args.interval_seconds,
end={
"condition": "time",
"datetime": datetime.datetime.now() + datetime.timedelta(days=args.days),
},
)

timer = timers_client.create_timer(
timer=globus_sdk.TransferTimer(
name=(
"create-timer-example "
f"[created at {datetime.datetime.now().isoformat()}]"
),
body=body,
schedule=schedule,
)
)
print("Finished submitting timer.")
print(f"timer_id: {timer['timer']['job_id']}")


if __name__ == "__main__":
main()
25 changes: 25 additions & 0 deletions docs/examples/timer_management/delete_timer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env python

import argparse

import globus_sdk
from globus_sdk.experimental.globus_app import UserApp

# Tutorial Client ID - <replace this with your own client>
NATIVE_CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2"
USER_APP = UserApp("manage-timers-example", client_id=NATIVE_CLIENT_ID)


def main():
parser = argparse.ArgumentParser()
parser.add_argument("TIMER_ID")
args = parser.parse_args()

client = globus_sdk.TimersClient(app=USER_APP)

client.delete_job(args.TIMER_ID)
print("Finished deleting timer.")


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
Globus Timers Operations
------------------------
.. _timer_management_examples:

These examples demonstrate how to create, list, and delete Timers with the SDK.
Timer Management
----------------

These examples demonstrate how to create, list, and delete timers with the SDK.

Create a timer
~~~~~~~~~~~~~~

This script creates a new timer, on source and destination collections provided
via the command-line. It syncs an input file or directory between the two.

The script assumes that the path being synced is the same on the source and
destination for simplicity.

.. note::
This example does not handle ``data_access`` scope requirements.
See the later example to handle this.
Expand All @@ -17,11 +22,19 @@ via the command-line. It syncs an input file or directory between the two.
:caption: ``create_timer.py`` [:download:`download <create_timer.py>`]
:language: python

List timers
~~~~~~~~~~~

This script lists your current timers.

.. literalinclude:: list_timers.py
:caption: ``list_timers.py`` [:download:`download <list_timers.py>`]
:language: python

Delete a timer
~~~~~~~~~~~~~~

This script creates a new timer, on source and destination collections provided
via the command-line. It syncs an input file or directory between the two.
This script deletes a timer by ID.

.. literalinclude:: delete_timer.py
:caption: ``delete_timer.py`` [:download:`download <delete_timer.py>`]
Expand All @@ -35,12 +48,12 @@ also handles ``data_access`` scope requirements for the source and destination
collections.

Discovering ``data_access`` requirements requires the use of a
``TransferClient`` to look up the collections. Therefore, this example may put
the user through two login flows.
``TransferClient`` to look up the collections.

As in the simpler example, this script creates a new timer, on source and
destination collections provided via the command-line. It syncs an input
file or directory between the two.
file or directory between the two, and assumes that the path is the same on the
source and destination.

.. literalinclude:: create_timer_data_access.py
:caption: ``create_timer_data_access.py`` [:download:`download <create_timer_data_access.py>`]
Expand Down
24 changes: 24 additions & 0 deletions docs/examples/timer_management/list_timers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env python

import globus_sdk
from globus_sdk.experimental.globus_app import UserApp

# Tutorial Client ID - <replace this with your own client>
NATIVE_CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2"
USER_APP = UserApp("manage-timers-example", client_id=NATIVE_CLIENT_ID)


def main():
client = globus_sdk.TimersClient(app=USER_APP)

first = True
for record in client.list_jobs(query_params={"filter_active": True})["jobs"]:
if not first:
print("---")
first = False
print("name:", record["name"])
print("id:", record["job_id"])


if __name__ == "__main__":
main()
4 changes: 4 additions & 0 deletions docs/examples/timer_operations.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:orphan:

The documentation which was found on this page has moved to
:ref:`Timer Management Examples <timer_management_examples>`.
Loading