Skip to content

Commit

Permalink
fix(frontend): Unbreak credentials input on single-provider blocks (#…
Browse files Browse the repository at this point in the history
…8636)

- Resolves #8635

- fix(frontend): Fix type mismatch of `CredentialsField` schema between frontend and backend
   - Fix usages of `credentialsSchema.credentials_provider`

- refactor(backend): Create `CredentialsFieldSchemaExtra` model in backend so it can be mirrored directly in frontend
   - Add check to enforce multi-provider `CredentialsField` always has `discriminator`

- dx: Add type checking shortcut `yarn type-check` / `npm run type-check` for frontend
  • Loading branch information
Pwuts authored Nov 13, 2024
1 parent e907ffd commit aaa0b79
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 23 deletions.
34 changes: 20 additions & 14 deletions autogpt_platform/backend/backend/data/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,15 @@ class CredentialsMetaInput(BaseModel, Generic[CP, CT]):
type: CT


class CredentialsFieldSchemaExtra(BaseModel, Generic[CP, CT]):
# TODO: move discrimination mechanism out of CredentialsField (frontend + backend)
credentials_provider: list[CP]
credentials_scopes: Optional[list[str]]
credentials_types: list[CT]
discriminator: Optional[str] = None
discriminator_mapping: Optional[dict[str, CP]] = None


def CredentialsField(
provider: CP | list[CP],
supported_credential_types: set[CT],
Expand All @@ -166,24 +175,21 @@ def CredentialsField(
`CredentialsField` must and can only be used on fields named `credentials`.
This is enforced by the `BlockSchema` base class.
"""
json_extra = {
k: v
for k, v in {
"credentials_provider": (
[provider] if isinstance(provider, str) else provider
),
"credentials_scopes": list(required_scopes) or None, # omit if empty
"credentials_types": list(supported_credential_types),
"discriminator": discriminator,
"discriminator_mapping": discriminator_mapping,
}.items()
if v is not None
}
if not isinstance(provider, str) and len(provider) > 1 and not discriminator:
raise TypeError("Multi-provider CredentialsField requires discriminator!")

field_schema_extra = CredentialsFieldSchemaExtra[CP, CT](
credentials_provider=[provider] if isinstance(provider, str) else provider,
credentials_scopes=list(required_scopes) or None, # omit if empty
credentials_types=list(supported_credential_types),
discriminator=discriminator,
discriminator_mapping=discriminator_mapping,
)

return Field(
title=title,
description=description,
json_schema_extra=json_extra,
json_schema_extra=field_schema_extra.model_dump(exclude_none=True),
**kwargs,
)

Expand Down
1 change: 1 addition & 0 deletions autogpt_platform/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"start": "next start",
"lint": "next lint && prettier --check .",
"format": "prettier --write .",
"type-check": "tsc --noEmit",
"test": "playwright test",
"test-ui": "playwright test --ui",
"gentests": "playwright codegen http://localhost:3000",
Expand Down
24 changes: 16 additions & 8 deletions autogpt_platform/frontend/src/hooks/useCredentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,15 @@ export default function useCredentials(): CredentialsData | null {
]) ||
null;

if (
!discriminatorValue &&
credentialsSchema.credentials_provider.length > 1
) {
throw new Error("Multi-provider credential input requires discriminator!");
}

const providerName =
discriminatorValue || credentialsSchema.credentials_provider;
discriminatorValue || credentialsSchema.credentials_provider[0];
const provider = allProviders ? allProviders[providerName] : null;

// If block input schema doesn't have credentials, return null
Expand All @@ -60,13 +67,14 @@ export default function useCredentials(): CredentialsData | null {

// No provider means maybe it's still loading
if (!provider) {
return {
provider: credentialsSchema.credentials_provider,
schema: credentialsSchema,
supportsApiKey,
supportsOAuth2,
isLoading: true,
};
// return {
// provider: credentialsSchema.credentials_provider,
// schema: credentialsSchema,
// supportsApiKey,
// supportsOAuth2,
// isLoading: true,
// };
return null;
}

// Filter by OAuth credentials that have sufficient scopes for this block
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ export type CredentialsProviderName =
(typeof PROVIDER_NAMES)[keyof typeof PROVIDER_NAMES];

export type BlockIOCredentialsSubSchema = BlockIOSubSchemaMeta & {
credentials_provider: CredentialsProviderName;
/* Mirror of backend/data/model.py:CredentialsFieldSchemaExtra */
credentials_provider: CredentialsProviderName[];
credentials_scopes?: string[];
credentials_types: Array<CredentialsType>;
discriminator?: string;
Expand Down

0 comments on commit aaa0b79

Please sign in to comment.