Skip to content

Commit

Permalink
refactor(platform): Combine per-provider credentials API calls (#8772)
Browse files Browse the repository at this point in the history
- Add `/integrations/credentials` endpoint which lists all credentials for the authenticated user
- Amend credential fetching logic in front end to fetch all at once instead of per provider

- Resolves #8770
- Resolves (hopefully) #8613
  • Loading branch information
Pwuts authored Nov 26, 2024
1 parent c6e838d commit f141455
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 29 deletions.
23 changes: 22 additions & 1 deletion autogpt_platform/backend/backend/server/integrations/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def login(

class CredentialsMetaResponse(BaseModel):
id: str
provider: str
type: CredentialsType
title: str | None
scopes: list[str] | None
Expand Down Expand Up @@ -119,22 +120,42 @@ def callback(
)
return CredentialsMetaResponse(
id=credentials.id,
provider=credentials.provider,
type=credentials.type,
title=credentials.title,
scopes=credentials.scopes,
username=credentials.username,
)


@router.get("/{provider}/credentials")
@router.get("/credentials")
def list_credentials(
user_id: Annotated[str, Depends(get_user_id)],
) -> list[CredentialsMetaResponse]:
credentials = creds_manager.store.get_all_creds(user_id)
return [
CredentialsMetaResponse(
id=cred.id,
provider=cred.provider,
type=cred.type,
title=cred.title,
scopes=cred.scopes if isinstance(cred, OAuth2Credentials) else None,
username=cred.username if isinstance(cred, OAuth2Credentials) else None,
)
for cred in credentials
]


@router.get("/{provider}/credentials")
def list_credentials_by_provider(
provider: Annotated[str, Path(title="The provider to list credentials for")],
user_id: Annotated[str, Depends(get_user_id)],
) -> list[CredentialsMetaResponse]:
credentials = creds_manager.store.get_creds_by_provider(user_id, provider)
return [
CredentialsMetaResponse(
id=cred.id,
provider=cred.provider,
type=cred.type,
title=cred.title,
scopes=cred.scopes if isinstance(cred, OAuth2Credentials) else None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,43 +184,64 @@ export default function CredentialsProvider({
api.isAuthenticated().then((isAuthenticated) => {
if (!isAuthenticated) return;

CREDENTIALS_PROVIDER_NAMES.forEach(
(provider: CredentialsProviderName) => {
api.listCredentials(provider).then((response) => {
const { oauthCreds, apiKeys } = response.reduce<{
api.listCredentials().then((response) => {
const credentialsByProvider = response.reduce(
(acc, cred) => {
if (!acc[cred.provider]) {
acc[cred.provider] = { oauthCreds: [], apiKeys: [] };
}
if (cred.type === "oauth2") {
acc[cred.provider].oauthCreds.push(cred);
} else if (cred.type === "api_key") {
acc[cred.provider].apiKeys.push(cred);
}
return acc;
},
{} as Record<
CredentialsProviderName,
{
oauthCreds: CredentialsMetaResponse[];
apiKeys: CredentialsMetaResponse[];
}>(
(acc, cred) => {
if (cred.type === "oauth2") {
acc.oauthCreds.push(cred);
} else if (cred.type === "api_key") {
acc.apiKeys.push(cred);
}
return acc;
},
{ oauthCreds: [], apiKeys: [] },
);
}
>,
);

setProviders((prev) => ({
...prev,
setProviders((prev) => ({
...prev,
...Object.entries(credentialsByProvider).reduce(
(acc, [provider, { apiKeys, oauthCreds }]) => ({
...acc,
[provider]: {
provider,
providerName: providerDisplayNames[provider],
providerName:
providerDisplayNames[provider as CredentialsProviderName],
savedApiKeys: apiKeys,
savedOAuthCredentials: oauthCreds,
oAuthCallback: (code: string, state_token: string) =>
oAuthCallback(provider, code, state_token),
oAuthCallback(
provider as CredentialsProviderName,
code,
state_token,
),
createAPIKeyCredentials: (
credentials: APIKeyCredentialsCreatable,
) => createAPIKeyCredentials(provider, credentials),
) =>
createAPIKeyCredentials(
provider as CredentialsProviderName,
credentials,
),
deleteCredentials: (id: string, force: boolean = false) =>
deleteCredentials(provider, id, force),
deleteCredentials(
provider as CredentialsProviderName,
id,
force,
),
},
}));
});
},
);
}),
{},
),
}));
});
});
}, [api, createAPIKeyCredentials, deleteCredentials, oAuthCallback]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,12 @@ export default class BaseAutoGPTServerAPI {
);
}

listCredentials(provider: string): Promise<CredentialsMetaResponse[]> {
return this._get(`/integrations/${provider}/credentials`);
listCredentials(provider?: string): Promise<CredentialsMetaResponse[]> {
return this._get(
provider
? `/integrations/${provider}/credentials`
: "/integrations/credentials",
);
}

getCredentials(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ export type NodeExecutionResult = {
/* Mirror of backend/server/integrations/router.py:CredentialsMetaResponse */
export type CredentialsMetaResponse = {
id: string;
provider: CredentialsProviderName;
type: CredentialsType;
title?: string;
scopes?: Array<string>;
Expand Down Expand Up @@ -292,7 +293,7 @@ type BaseCredentials = {
id: string;
type: CredentialsType;
title?: string;
provider: string;
provider: CredentialsProviderName;
};

/* Mirror of autogpt_libs/supabase_integration_credentials_store/types.py:OAuth2Credentials */
Expand Down

0 comments on commit f141455

Please sign in to comment.