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

Implement the X-Globus-Client-Info header as a feature of transport objects #990

Merged
merged 5 commits into from
Jun 25, 2024

Conversation

sirosen
Copy link
Member

@sirosen sirosen commented Jun 21, 2024

Why

The goal of this PR is to start emitting a X-Globus-Client-Info header
which encodes the SDK version, and can easily be expanded to include other
product name/version combinations. (e.g. compute SDK, globus-cli, etc)

X-Globus-Client-Info is a recent spec we've put together in collaboration
with our frontend dev team and supersedes the use of a custom User-Agent to
encode app information (app_name). We needed some new art in this space as
setting a custom User-Agent is not an appropriate solution for the browser
context.

The app_name value and User-Agent behavior is not deprecated at this
time. In future SDK versions, we may choose to deprecate that behavior if we
feel it is in our interest to only provide this data via the new header.

What

  • Add a new class to implement basic encoding of the header,
    GlobusClientInfo. This class is documented and provides the public
    interface for this component. However, it is only exported at the
    globus_sdk.transport layer, as most users will not need to interact with
    it directly.

  • Add a private, custom subclass, DefaultGlobusClientInfo, which seeds
    product=python-sdk,version={globus_sdk.__version__} on init.

  • RequestsTransport has been enhanced to include
    self.globus_clientinfo = DefaultGlobusClientInfo() on init, meaning that
    it seeds and stores this information in a publicly accessible attribute.
    When RequestsTransport builds headers for a request, it now also includes
    self.globus_clientinfo.

Notes

  • DefaultGlobusClientInfo behavior could simply be baked into the base
    GlobusClientInfo -- input and debate welcome. There are various
    equally-valid ways of seeding this information. The goals are to ensure that
    this data is set for all SDK calls, that the implementation is easy to
    understand, and that it is easy to extend, modify, and even disable.

  • A clear target use-case here is globus-cli. The intent here is that the
    CLI can update such that each client builder adds a call to

    newclient.transport.globus_clientinfo.add(
        {"product": "cli", "version": globus_cli.__version__}
    )

    A better/easier hooking interface could be added in the future, if one can be
    designed based on our experience in that context.


📚 Documentation preview 📚: https://globus-sdk-python--990.org.readthedocs.build/en/990/

@kurtmckee
Copy link
Member

CI is failing.

This is a simple implementation of the spec as a dedicated object. It
serializes into headers when requests are sent.

A default implementation is provided which seeds the data with the SDK
product and version info.
- Add a test parser to the testsuite
- Add a test which validates that this parser picks up on the
  SDK-provided data (with no errors) and can handle added data
- Move narrative doc from module docstring into the relevant class
  docstring, for easier inclusion in our HTML docs.

- Enhance said narrative doc to better explain the header in a
  user-facing manner, and ensure correct ReST formatting.

- Add a changelog which minimally explains the feature.
@sirosen
Copy link
Member Author

sirosen commented Jun 21, 2024

I made a change (as a fixup, so you can't see it in the history) to switch from ._globus_clientinfo to .globus_clientinfo and didn't catch one location. 🤦

I've just fixup'ed my fixup and pushed, so the state should be clean now.


def __init__(self) -> None:
super().__init__()
self.add({"product": "python-sdk", "version": __version__})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like this information should always be tacked on at serialization time.

That is to say: devs who are using the SDK will always have critical SDK client information present.

Therefore I think that there should be no DefaultGlobusClientInfo class, but instead the SDK client information should be hard-coded to serialize into the HTTP header (and, probably, be the first client info in the serialized header, in case the remainder of the header is b0rked for any reason and cannot be deserialized).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, let's merge it into the superclass so that it's part of the base implementation here. I buy a lot of the thinking here.

The place where I want to exercise some caution is around baking it in so deeply that removing, inspecting, or manipulating it requires subclassing the transport class itself. I'd therefore prefer to keep it as structured data, but structured data which is populated by default.

src/globus_sdk/transport/requests.py Outdated Show resolved Hide resolved
src/globus_sdk/transport/requests.py Outdated Show resolved Hide resolved
tests/unit/transport/test_clientinfo.py Show resolved Hide resolved
- Coalesce into a single class (rather than base+default)
- Fix various typos around the header spelling
- Rename the transport attribute to `globus_client_info`
- Update tests to match implementation updates
- Update docs to match implementation updates
@sirosen sirosen merged commit 89642d8 into globus:main Jun 25, 2024
16 checks passed
@sirosen sirosen deleted the globus-clientinfo branch June 25, 2024 23:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants