diff --git a/changelog.d/20240717_125912_derek_globus_app_example_doc_updates.rst b/changelog.d/20240717_125912_derek_globus_app_example_doc_updates.rst new file mode 100644 index 000000000..bbb5ab0f2 --- /dev/null +++ b/changelog.d/20240717_125912_derek_globus_app_example_doc_updates.rst @@ -0,0 +1,6 @@ + +Documentation +~~~~~~~~~~~~~ + +- Added a new experimental "Updated Examples" section which rewrites and reorders + many examples to aid in discovery. (:pr:`NUMBER`) diff --git a/docs/experimental/examples/endpoints_and_collections/create_guest_collection/index.rst b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/index.rst new file mode 100644 index 000000000..80eaff68e --- /dev/null +++ b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/index.rst @@ -0,0 +1,9 @@ + +Creating a Guest Collection +=========================== + +TODO + +.. literalinclude:: user_guest_collection.py + :caption: ``user_guest_collection.py`` [:download:`download `] + :language: python diff --git a/docs/experimental/examples/endpoints_and_collections/create_guest_collection/user_guest_collection.py b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/user_guest_collection.py new file mode 100644 index 000000000..dd6aca773 --- /dev/null +++ b/docs/experimental/examples/endpoints_and_collections/create_guest_collection/user_guest_collection.py @@ -0,0 +1,45 @@ +import globus_sdk +from globus_sdk.experimental.globus_app import UserApp + +# Tutorial Client ID - +NATIVE_CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2" +USER_APP = UserApp("my-simple-transfer", client_id=NATIVE_CLIENT_ID) + +# Globus Tutorial Collection 1 +# https://app.globus.org/file-manager/collections/6c54cade-bde5-45c1-bdea-f4bd71dba2cc +ENDPOINT_HOSTNAME = "https://b7a4f1.75bc.data.globus.org" +MAPPED_COLLECTION_ID = "6c54cade-bde5-45c1-bdea-f4bd71dba2cc" + + +def main(): + gcs_client = globus_sdk.GCSClient(ENDPOINT_HOSTNAME, app=USER_APP) + + # Comment out this line if the mapped collection is high assurance + attach_data_access_scope(gcs_client, MAPPED_COLLECTION_ID) + + collection_request = globus_sdk.GuestCollectionDocument( + public=True, + collection_base_path="/", + display_name="example_guest_collection", + mapped_collection_id=MAPPED_COLLECTION_ID, + ) + + collection = gcs_client.create_collection(collection_request) + print(f"Created guest collection. Collection ID: {collection['id']}") + + +def attach_data_access_scope(gcs_client, collection_id): + """Compose and attach a ``data_access`` scope for the supplied collection""" + endpoint_scopes = gcs_client.get_gcs_endpoint_scopes(gcs_client.endpoint_client_id) + collection_scopes = gcs_client.get_gcs_collection_scopes(collection_id) + + manage_collections = globus_sdk.Scope(endpoint_scopes.manage_collections) + data_access = globus_sdk.Scope(collection_scopes.data_access, optional=True) + + manage_collections.add_dependency(data_access) + + gcs_client.add_app_scope(manage_collections) + + +if __name__ == "__main__": + main() diff --git a/docs/experimental/examples/endpoints_and_collections/identifying_entity_type.rst b/docs/experimental/examples/endpoints_and_collections/identifying_entity_type.rst new file mode 100644 index 000000000..425adb8b7 --- /dev/null +++ b/docs/experimental/examples/endpoints_and_collections/identifying_entity_type.rst @@ -0,0 +1,5 @@ + +Identifying Entity Type +======================= + +TODO diff --git a/docs/experimental/examples/endpoints_and_collections/index.rst b/docs/experimental/examples/endpoints_and_collections/index.rst new file mode 100644 index 000000000..3e62606e2 --- /dev/null +++ b/docs/experimental/examples/endpoints_and_collections/index.rst @@ -0,0 +1,9 @@ + +Endpoints and Collections +========================= + +.. toctree:: + :maxdepth: 1 + + identifying_entity_type + create_guest_collection/index diff --git a/docs/experimental/examples/flows/create.rst b/docs/experimental/examples/flows/create.rst new file mode 100644 index 000000000..dcc387dbf --- /dev/null +++ b/docs/experimental/examples/flows/create.rst @@ -0,0 +1,5 @@ + +Creating a Flow +=============== + +TODO diff --git a/docs/experimental/examples/flows/delete.rst b/docs/experimental/examples/flows/delete.rst new file mode 100644 index 000000000..cf29a89a0 --- /dev/null +++ b/docs/experimental/examples/flows/delete.rst @@ -0,0 +1,5 @@ + +Deleting a Flow +=============== + +TODO diff --git a/docs/experimental/examples/flows/index.rst b/docs/experimental/examples/flows/index.rst new file mode 100644 index 000000000..7798c4230 --- /dev/null +++ b/docs/experimental/examples/flows/index.rst @@ -0,0 +1,11 @@ + +Flows +===== + +.. toctree:: + :maxdepth: 1 + + create + list + run + delete diff --git a/docs/experimental/examples/flows/list.rst b/docs/experimental/examples/flows/list.rst new file mode 100644 index 000000000..70f0c2e6e --- /dev/null +++ b/docs/experimental/examples/flows/list.rst @@ -0,0 +1,5 @@ + +Listing Flows +============= + +TODO diff --git a/docs/experimental/examples/flows/run.rst b/docs/experimental/examples/flows/run.rst new file mode 100644 index 000000000..87851ff1f --- /dev/null +++ b/docs/experimental/examples/flows/run.rst @@ -0,0 +1,5 @@ + +Running a Flow +============== + +TODO diff --git a/docs/experimental/examples/index.rst b/docs/experimental/examples/index.rst new file mode 100644 index 000000000..ac0791d5b --- /dev/null +++ b/docs/experimental/examples/index.rst @@ -0,0 +1,22 @@ + +Updated Examples +================ + +This experimental doc restructures the existing :ref:`examples` section by: + +* Grouping examples by common topic +* Consolidating & updating examples into a more uniform active format +* Updating references to leverage the latest SDK constructs (notably GlobusApp) + +While in ``experimental`` it should be considered a work in progress and subject to +change. Once complete, it'll be merged into the main documentation, replacing the +existing examples section. + +.. toctree:: + :maxdepth: 2 + + transferring_data/index + endpoints_and_collections/index + flows/index + projects/index + oauth2/index diff --git a/docs/experimental/examples/oauth2/authorizers.rst b/docs/experimental/examples/oauth2/authorizers.rst new file mode 100644 index 000000000..518f266df --- /dev/null +++ b/docs/experimental/examples/oauth2/authorizers.rst @@ -0,0 +1,5 @@ + +Using Authorizers +================= + +TODO diff --git a/docs/experimental/examples/oauth2/customizing_token_storage.rst b/docs/experimental/examples/oauth2/customizing_token_storage.rst new file mode 100644 index 000000000..70f8e124f --- /dev/null +++ b/docs/experimental/examples/oauth2/customizing_token_storage.rst @@ -0,0 +1,5 @@ + +Customizing Token Storage +========================= + +TODO diff --git a/docs/experimental/examples/oauth2/globus_app.rst b/docs/experimental/examples/oauth2/globus_app.rst new file mode 100644 index 000000000..4c896cf88 --- /dev/null +++ b/docs/experimental/examples/oauth2/globus_app.rst @@ -0,0 +1,5 @@ + +Using a Globus App +================== + +TODO diff --git a/docs/experimental/examples/oauth2/index.rst b/docs/experimental/examples/oauth2/index.rst new file mode 100644 index 000000000..1f2dc2a0b --- /dev/null +++ b/docs/experimental/examples/oauth2/index.rst @@ -0,0 +1,12 @@ + +OAuth2 at Globus +================ + +.. toctree:: + :maxdepth: 1 + + globus_app + authorizers + login_flows + customizing_token_storage + three_legged_oauth diff --git a/docs/experimental/examples/oauth2/login_flows.rst b/docs/experimental/examples/oauth2/login_flows.rst new file mode 100644 index 000000000..26b1fedf5 --- /dev/null +++ b/docs/experimental/examples/oauth2/login_flows.rst @@ -0,0 +1,5 @@ + +Running Login Flows +=================== + +TODO diff --git a/docs/experimental/examples/oauth2/three_legged_oauth.rst b/docs/experimental/examples/oauth2/three_legged_oauth.rst new file mode 100644 index 000000000..c2430dc89 --- /dev/null +++ b/docs/experimental/examples/oauth2/three_legged_oauth.rst @@ -0,0 +1,5 @@ + +Performing Three-Legged OAuth in Flask +======================================== + +TODO diff --git a/docs/experimental/examples/projects/create.rst b/docs/experimental/examples/projects/create.rst new file mode 100644 index 000000000..c7632cd21 --- /dev/null +++ b/docs/experimental/examples/projects/create.rst @@ -0,0 +1,5 @@ + +Creating a Project +================== + +TODO diff --git a/docs/experimental/examples/projects/delete.rst b/docs/experimental/examples/projects/delete.rst new file mode 100644 index 000000000..16c790cf3 --- /dev/null +++ b/docs/experimental/examples/projects/delete.rst @@ -0,0 +1,5 @@ + +Deleting a Project +================== + +TODO diff --git a/docs/experimental/examples/projects/index.rst b/docs/experimental/examples/projects/index.rst new file mode 100644 index 000000000..20a45c0f1 --- /dev/null +++ b/docs/experimental/examples/projects/index.rst @@ -0,0 +1,10 @@ + +Auth Projects +============= + +.. toctree:: + :maxdepth: 1 + + create + list + delete diff --git a/docs/experimental/examples/projects/list.rst b/docs/experimental/examples/projects/list.rst new file mode 100644 index 000000000..13df18c8f --- /dev/null +++ b/docs/experimental/examples/projects/list.rst @@ -0,0 +1,5 @@ + +Listing Projects +================ + +TODO diff --git a/docs/experimental/examples/transferring_data/index.rst b/docs/experimental/examples/transferring_data/index.rst new file mode 100644 index 000000000..1dbc2733e --- /dev/null +++ b/docs/experimental/examples/transferring_data/index.rst @@ -0,0 +1,11 @@ + +Transferring Data +================= + +.. toctree:: + :maxdepth: 1 + + submit_transfer/index + schedule_transfer/index + task_deadlines + recursive_ls diff --git a/docs/experimental/examples/transferring_data/recursive_ls.rst b/docs/experimental/examples/transferring_data/recursive_ls.rst new file mode 100644 index 000000000..41174ae38 --- /dev/null +++ b/docs/experimental/examples/transferring_data/recursive_ls.rst @@ -0,0 +1,5 @@ + +Recursively Listing a Filesystem +================================ + +TODO diff --git a/docs/experimental/examples/transferring_data/schedule_transfer/index.rst b/docs/experimental/examples/transferring_data/schedule_transfer/index.rst new file mode 100644 index 000000000..8284e8bb2 --- /dev/null +++ b/docs/experimental/examples/transferring_data/schedule_transfer/index.rst @@ -0,0 +1,5 @@ + +Scheduling a Transfer +===================== + +TODO diff --git a/docs/experimental/examples/transferring_data/submit_transfer/index.rst b/docs/experimental/examples/transferring_data/submit_transfer/index.rst new file mode 100644 index 000000000..c41f1c0ad --- /dev/null +++ b/docs/experimental/examples/transferring_data/submit_transfer/index.rst @@ -0,0 +1,78 @@ + +Initiating a Transfer +===================== + +Moving data within the Globus Ecosystem is performed by submitting a ``Transfer Task`` +against the Globus Transfer service. + +The below examples demonstrate how to do that using a globus sdk ``TransferClient``. +They are split into two categories: + +#. :ref:`transferring-between-known-collections` - both source and destination + collections are known in advance and are likely be hardcoded into your script. + +#. :ref:`transferring-between-unknown-collections` - either the source or + destination collection will be determined at runtime (e.g. by script argument). + +We differentiate these examples because certain collections have special auth +requirements which must either be defined up front or fixed reactively if omitted. +Certain collections (mapped non-high assurance ones) require that a special scope +("data_access") to be attached to the transfer request to grant Transfer access to that +collection's data. +If both collections are known this can be done proactively with a call to +the ``add_app_data_access_scope`` method. If, however, one or more collections are +unknown, the script must reactively solve the ``ConsentRequired`` error that is raised +when the transfer is submitted. + + +.. _transferring-between-known-collections: + +Transferring data between two known collections +----------------------------------------------- + +.. note:: + The script references two globus hosted "tutorial" collections. Replace these ids & + paths with your own collection ids and paths to move your own data. + +.. note:: + Some collections require you to attach a "data_access" scope to your transfer + request. You should evaluate whether this is necessary for both your source and + destination collections and omit the ``transfer_client.add_app_data_access_scope`` + calls as needed. + + A collection requires "data_access" if it is (1) a mapped collection and (2) is + not high assurance. + +.. literalinclude:: submit_transfer_collections_known.py + :caption: ``submit_transfer_collections_known.py`` [:download:`download `] + :language: python + + +.. _transferring-between-unknown-collections: + +Transferring data where at least one collection is unknown +---------------------------------------------------------- + +In the case where your script does not know the full set of collections that it will +be interacting with, you may need to reactively solve the ``ConsentRequired`` errors +instead of proactively attaching the "data_access" scope. + +This script demonstrates how to do that by: + +#. Attempting to submit the transfer without any "data_access" scopes. +#. Intercepting any raised ``ConsentRequired`` errors if the request fails. +#. Attaching any scope requirements detailed in the error. +#. Retrying the transfer which implicitly puts your user through a consent flow to + resolve their auth state. + +.. note:: + The script references two globus hosted "tutorial" collections. Replace these ids & + paths with your own collection ids and paths to move your own data. + +.. note:: + Given that this script reactively fixes auth states, it can involve two user login + interactions instead of the one required by the above proactive approach. + +.. literalinclude:: submit_transfer_collections_unknown.py + :caption: ``submit_transfer_collections_unknown.py`` [:download:`download `] + :language: python diff --git a/docs/experimental/examples/transferring_data/submit_transfer/submit_transfer_collections_known.py b/docs/experimental/examples/transferring_data/submit_transfer/submit_transfer_collections_known.py new file mode 100644 index 000000000..f35d1ea00 --- /dev/null +++ b/docs/experimental/examples/transferring_data/submit_transfer/submit_transfer_collections_known.py @@ -0,0 +1,38 @@ +import globus_sdk +from globus_sdk.experimental.globus_app import UserApp + +# Tutorial Client ID - +NATIVE_CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2" +USER_APP = UserApp("my-simple-transfer", client_id=NATIVE_CLIENT_ID) + +# Globus Tutorial Collection 1 +# https://app.globus.org/file-manager/collections/6c54cade-bde5-45c1-bdea-f4bd71dba2cc +SRC_COLLECTION = "6c54cade-bde5-45c1-bdea-f4bd71dba2cc" +SRC_PATH = "/share/godata/file1.txt" + +# Globus Tutorial Collection 2 +# https://app.globus.org/file-manager/collections/31ce9ba0-176d-45a5-add3-f37d233ba47d +DST_COLLECTION = "31ce9ba0-176d-45a5-add3-f37d233ba47d" +DST_PATH = "/~/example-transfer-script-destination.txt" + + +def main(): + transfer_client = globus_sdk.TransferClient(app=USER_APP) + + # Comment out each of these lines if the referenced collection is either + # (1) A guest collection or (2) high assurance. + transfer_client.add_app_data_access_scope(SRC_COLLECTION) + transfer_client.add_app_data_access_scope(DST_COLLECTION) + + transfer_request = globus_sdk.TransferData( + source_endpoint=SRC_COLLECTION, + destination_endpoint=DST_COLLECTION, + ) + transfer_request.add_item(SRC_PATH, DST_PATH) + + task = transfer_client.submit_transfer(transfer_request) + print(f"Submitted transfer. Task ID: {task['task_id']}.") + + +if __name__ == "__main__": + main() diff --git a/docs/experimental/examples/transferring_data/submit_transfer/submit_transfer_collections_unknown.py b/docs/experimental/examples/transferring_data/submit_transfer/submit_transfer_collections_unknown.py new file mode 100644 index 000000000..4e9ca714e --- /dev/null +++ b/docs/experimental/examples/transferring_data/submit_transfer/submit_transfer_collections_unknown.py @@ -0,0 +1,42 @@ +import globus_sdk +from globus_sdk.experimental.globus_app import UserApp + +# Tutorial Client ID - +NATIVE_CLIENT_ID = "61338d24-54d5-408f-a10d-66c06b59f6d2" +USER_APP = UserApp("my-simple-transfer", client_id=NATIVE_CLIENT_ID) + +# Globus Tutorial Collection 1 +# https://app.globus.org/file-manager/collections/6c54cade-bde5-45c1-bdea-f4bd71dba2cc +SRC_COLLECTION = "6c54cade-bde5-45c1-bdea-f4bd71dba2cc" +SRC_PATH = "/share/godata/file1.txt" + +# Globus Tutorial Collection 2 +# https://app.globus.org/file-manager/collections/31ce9ba0-176d-45a5-add3-f37d233ba47d +DST_COLLECTION = "31ce9ba0-176d-45a5-add3-f37d233ba47d" +DST_PATH = "/~/example-transfer-script-destination.txt" + + +def main(): + transfer_client = globus_sdk.TransferClient(app=USER_APP) + + transfer_request = globus_sdk.TransferData( + source_endpoint=SRC_COLLECTION, + destination_endpoint=DST_COLLECTION, + ) + transfer_request.add_item(SRC_PATH, DST_PATH) + + try: + task = transfer_client.submit_transfer(transfer_request) + except globus_sdk.TransferAPIError as err: + if not err.info.consent_required: + raise + + print("Additional consent required.") + transfer_client.add_app_scope(err.info.consent_required.required_scopes) + + task = transfer_client.submit_transfer(transfer_request) + print(f"Submitted transfer. Task ID: {task['task_id']}.") + + +if __name__ == "__main__": + main() diff --git a/docs/experimental/examples/transferring_data/task_deadlines.rst b/docs/experimental/examples/transferring_data/task_deadlines.rst new file mode 100644 index 000000000..09f9342e6 --- /dev/null +++ b/docs/experimental/examples/transferring_data/task_deadlines.rst @@ -0,0 +1,5 @@ + +Setting Task Deadlines +====================== + +TODO diff --git a/docs/index.rst b/docs/index.rst index f065f5f81..5b152c4c4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -45,6 +45,8 @@ Table of Contents testing/index experimental/index + experimental/examples/index + .. toctree:: :caption: Additional Info