fix(frontend): Unbreak credentials input on single-provider blocks (#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
This commit is contained in:
Reinier van der Leer
2024-11-13 14:48:15 +01:00
committed by GitHub
parent e907ffda6e
commit aaa0b79f08
4 changed files with 39 additions and 23 deletions

View File

@@ -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],
@@ -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,
)

View File

@@ -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",

View File

@@ -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
@@ -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

View File

@@ -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;