Skip to content
Open
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
3 changes: 3 additions & 0 deletions changelog.d/20251205_152802_aaschaer_domain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Enhancements

* Add Domain field to default output of `globus endpoint search` and `globus endpoint show`
2 changes: 2 additions & 0 deletions src/globus_cli/commands/endpoint/show.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
from globus_cli.endpointish import Endpointish
from globus_cli.login_manager import LoginManager
from globus_cli.parsing import command, endpoint_id_arg
from globus_cli.services.transfer import DOMAIN_FIELD
from globus_cli.termio import Field, display, formatters

STANDARD_FIELDS = [
Field("Display Name", "display_name"),
Field("ID", "id"),
Field("Owner", "owner_string"),
DOMAIN_FIELD,
Field("Description", "description", wrap_enabled=True),
Field("Shareable", "shareable"),
Field("Department", "department"),
Expand Down
31 changes: 31 additions & 0 deletions src/globus_cli/services/transfer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,46 @@ def parse(self, value: t.Any) -> str:
return str(value[0] or value[1])


class _DomainFormatter(formatters.StrFormatter):
def parse(self, value: t.Any) -> str:
if not isinstance(value, list) or len(value) != 2:
raise ValueError("cannot parse domain from malformed data")

tlsftp_server, gcs_manager_url = value

# if tlsftp_server is present, this is a GCS collection
# parse tlsftp_server in the format tlsftp://{domain}:{port}
if tlsftp_server:
assert isinstance(tlsftp_server, str)
return tlsftp_server[len("tlsftp://") :].split(":")[0]

# if gcs_manager_url present, but not tlsftp_server, this is a GCS endpoint
# parse gcs_manager_url in the format https://{domain}
elif gcs_manager_url:
assert isinstance(gcs_manager_url, str)
return gcs_manager_url[len("https://") :]

# entity type with no domain
else:
return str(None)


DOMAIN_FIELD = Field(
"Domain", "[tlsftp_server, gcs_manager_url]", formatter=_DomainFormatter()
)


ENDPOINT_LIST_FIELDS = [
Field("ID", "id"),
Field("Owner", "owner_string"),
Field("Display Name", "[display_name, canonical_name]", formatter=_NameFormatter()),
DOMAIN_FIELD,
]


__all__ = (
"ENDPOINT_LIST_FIELDS",
"DOMAIN_FIELD",
"CustomTransferClient",
"RecursiveLsResponse",
"display_name_or_cname",
Expand Down
24 changes: 17 additions & 7 deletions tests/functional/endpoint/test_endpoint_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ def _make_mapped_collection_search_result(
endpoint_id: str,
display_name: str,
owner_string: str,
manager_fqdn: str,
collection_fqdn: str,
) -> dict[str, t.Any]:
# most of the fields are filled with dummy data
# some of these values are pulled out here either to ensure their integrity
# or to make them more visible to a reader
username = "u_abcdefghijklmnop" # not a real b32 username
manager_fqdn = "a0bc1.23de.data.globus.org"
collection_fqdn = f"m-f45678.{manager_fqdn}"

data = {
"DATA_TYPE": "endpoint",
Expand Down Expand Up @@ -123,6 +123,9 @@ def singular_search_response():
endpoint_id = str(uuid.uuid4())
display_name = "dummy result"
owner_string = "globus@globus.org"
manager_fqdn = "a0bc1.23de.data.globus.org"
collection_fqdn = f"m-f45678.{manager_fqdn}"

return RegisteredResponse(
service="transfer",
path="/v0.10/endpoint_search",
Expand All @@ -131,11 +134,17 @@ def singular_search_response():
"endpoint_id": endpoint_id,
"display_name": display_name,
"owner_string": owner_string,
"collection_fqdn": collection_fqdn,
},
json={
"DATA": [
_make_mapped_collection_search_result(
collection_id, endpoint_id, display_name, owner_string
collection_id,
endpoint_id,
display_name,
owner_string,
manager_fqdn,
collection_fqdn,
)
],
"DATA_TYPE": "endpoint_list",
Expand All @@ -162,20 +171,21 @@ def test_search_shows_collection_id(run_line, singular_search_response):
header_line, separator_line, data_line = lines

# the header line shows the field names in order
header_row = re.split(r"\s+\|\s+", header_line)
assert header_row == ["ID", "Owner", "Display Name"]
header_row = [header.strip() for header in re.split(r"\s+\|\s+", header_line)]
assert header_row == ["ID", "Owner", "Display Name", "Domain"]
# the separator line is a series of dashes
separator_row = re.split(r"\s+\|\s+", separator_line)
assert len(separator_row) == 3
assert len(separator_row) == 4
for separator in separator_row:
assert set(separator) == {"-"} # exactly one character is used

# the data row should have the collection ID, Owner, and Display Name
# the data row should have the collection ID, Owner, Display Name, and Domain
data_row = re.split(r"\s+\|\s+", data_line)
assert data_row == [
meta["collection_id"],
meta["owner_string"],
meta["display_name"],
meta["collection_fqdn"],
]

# final sanity check -- the endpoint ID for a mapped collection doesn't
Expand Down