Skip to content

Implement ShadeClient class for per-instance configuration #2

Description

@codebestia

Description:

While global config is convenient, multi-tenant applications (e.g. a SaaS acting on behalf of multiple merchants) need isolated client instances that carry their own credentials. ShadeClient should accept the same parameters as the global config and bind them to a single instance, which is then passed to resource methods.

Proposed Steps:

  • Create src/shade/client.py with a ShadeClient dataclass/class.
  • Constructor parameters: api_key, environment, api_base, timeout, max_retries.
  • Each resource class should accept an optional client= kwarg and fall back to global config if omitted.
  • Provide a ShadeClient.from_env() factory that reads SHADE_API_KEY and SHADE_ENVIRONMENT from environment variables.

Acceptance Criteria:

  • client = ShadeClient(api_key="sk_test_xxx") creates an isolated client.
  • Resource calls using this client use its credentials, not global config.
  • Two ShadeClient instances with different keys can coexist without interfering.
  • ShadeClient.from_env() reads SHADE_API_KEY from the environment correctly.
  • Missing api_key on the instance when global is also None raises AuthenticationError.

Activity

  1. Whiznificent commented on Jun 19, 2026

    @Whiznificent

    gm maintainer i will like to work on this issue

  2. Whiznificent commented on Jun 19, 2026

    @Whiznificent

    gm maintainer i can fix this issue and have the pr submitted to you by tomorrow asap

  3. daveades commented on Jul 27, 2026

    @daveades
    Contributor

    Hi @codebestia, I'd like to work on this issue.

    How I understand it

    Global config works fine when there is one tenant. It breaks once one process serves many tenants, because their credentials share the same module state. So ShadeClient should stand on its own as a config object that resource methods use per call, with the global config only as a fallback.

    What I will do

    1. Add src/shade/client.py with a ShadeClient dataclass holding api_key, environment, api_base, timeout, max_retries.
    2. Give each resource class an optional client kwarg. Order per call is the passed client first, then global config, then raise AuthenticationError if both are missing. No instance changes the global, so two clients stay independent.
    3. Add a ShadeClient.from_env() classmethod that reads SHADE_API_KEY and SHADE_ENVIRONMENT.

    Tests, one for each acceptance criterion

    1. ShadeClient(api_key="sk_test_xxx") makes an isolated client.
    2. A resource call through that client uses its key, not the global.
    3. Two clients with different keys run at the same time without affecting each other.
    4. from_env() reads SHADE_API_KEY correctly.
    5. Missing api_key with global also None raises AuthenticationError.

    About me

    I do Python backend work with Flask. I am comfortable with dataclasses and handling how config is read from the environment.

    I can open a PR in 1 to 2 days.

  4. grantfox-oss commented on Jul 27, 2026

    @grantfox-oss

    🦊 GrantFox — @daveades has been assigned to this issue as part of the Official Campaign | FWC26 campaign!

    Next steps:

    1. Open a Pull Request referencing this issue (e.g., Closes #2)
    2. Your PR will be reviewed by the ShadeProtocol maintainers

    Good luck! Track your progress on GrantFox.

  5. added a commit that references this issue on Jul 30, 2026
  6. grantfox-oss commented on Jul 30, 2026

    @grantfox-oss

    🎉 This issue has been marked as completed on GrantFox as part of the Official Campaign | FWC26 campaign!

    @daveades's PR #55 was approved and merged by @codebestia.

    🏆 @daveades: You earned 35 FoxPoints for this contribution! Your current tier: Explorer (188 total points). Track your full progress on GrantFox.

    👏 Great work, @daveades! Keep contributing to ShadeProtocol.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions