feat(scaffolder): implement scaffolderServiceRef for credentials aware communication (#33044)

* add scaffolderServiceRef to scaffolder-node

Signed-off-by: benjdlambert <ben@blam.sh>

* make ScaffolderApi methods required, add tests

Signed-off-by: benjdlambert <ben@blam.sh>

* use request objects for single-string params in ScaffolderService

Signed-off-by: benjdlambert <ben@blam.sh>

* add scaffolderServiceMock test utility

Signed-off-by: benjdlambert <ben@blam.sh>

* adjust review comments

Signed-off-by: benjdlambert <ben@blam.sh>

* add scaffolderApiMock test utility to scaffolder-react

Signed-off-by: benjdlambert <ben@blam.sh>

* use items/totalItems response shape for listTasks

Signed-off-by: benjdlambert <ben@blam.sh>

* pass credentials through for autocomplete

Signed-off-by: benjdlambert <ben@blam.sh>

---------

Signed-off-by: benjdlambert <ben@blam.sh>
This commit is contained in:
Ben Lambert
2026-02-27 17:24:49 +01:00
committed by GitHub
parent 1b3dea2092
commit f598909f0f
26 changed files with 1188 additions and 112 deletions
+93 -90
View File
@@ -58,7 +58,7 @@ export type LogEvent = {
// @public
export interface ScaffolderApi {
// (undocumented)
autocomplete?(
autocomplete(
request: {
token: string;
provider: string;
@@ -79,94 +79,6 @@ export interface ScaffolderApi {
status?: ScaffolderTaskStatus;
}>;
// (undocumented)
dryRun?(
request: ScaffolderDryRunOptions,
options?: ScaffolderRequestOptions,
): Promise<ScaffolderDryRunResponse>;
// (undocumented)
getIntegrationsList(
options: ScaffolderGetIntegrationsListOptions,
): Promise<ScaffolderGetIntegrationsListResponse>;
// (undocumented)
getTask(
taskId: string,
options?: ScaffolderRequestOptions,
): Promise<ScaffolderTask>;
// (undocumented)
getTemplateParameterSchema(
templateRef: string,
options?: ScaffolderRequestOptions,
): Promise<TemplateParameterSchema>;
listActions(options?: ScaffolderRequestOptions): Promise<ListActionsResponse>;
// (undocumented)
listTasks?(
request: {
filterByOwnership: 'owned' | 'all';
limit?: number;
offset?: number;
},
options?: ScaffolderRequestOptions,
): Promise<{
tasks: ScaffolderTask[];
totalTasks?: number;
}>;
listTemplatingExtensions?(
options?: ScaffolderRequestOptions,
): Promise<ListTemplatingExtensionsResponse>;
retry?(
taskId: string,
options?: ScaffolderRequestOptions,
): Promise<{
id: string;
}>;
scaffold(
request: ScaffolderScaffoldOptions,
options?: ScaffolderRequestOptions,
): Promise<ScaffolderScaffoldResponse>;
// (undocumented)
streamLogs(
request: ScaffolderStreamLogsOptions,
options?: ScaffolderRequestOptions,
): Observable<LogEvent>;
}
// @public
export class ScaffolderClient implements ScaffolderApi {
constructor(options: {
discoveryApi: {
getBaseUrl(pluginId: string): Promise<string>;
};
fetchApi: {
fetch: typeof fetch;
};
identityApi?: {
getBackstageIdentity(): Promise<{
type: 'user';
userEntityRef: string;
ownershipEntityRefs: string[];
}>;
};
scmIntegrationsApi: ScmIntegrationRegistry;
useLongPollingLogs?: boolean;
});
autocomplete(input: {
token: string;
provider: string;
resource: string;
context: Record<string, string>;
}): Promise<{
results: {
title?: string;
id: string;
}[];
}>;
cancelTask(
taskId: string,
options?: ScaffolderRequestOptions,
): Promise<{
status?: ScaffolderTaskStatus;
}>;
// (undocumented)
dryRun(
request: ScaffolderDryRunOptions,
options?: ScaffolderRequestOptions,
@@ -201,7 +113,98 @@ export class ScaffolderClient implements ScaffolderApi {
listTemplatingExtensions(
options?: ScaffolderRequestOptions,
): Promise<ListTemplatingExtensionsResponse>;
retry?(
retry(
taskId: string,
options?: ScaffolderRequestOptions,
): Promise<{
id: string;
}>;
scaffold(
request: ScaffolderScaffoldOptions,
options?: ScaffolderRequestOptions,
): Promise<ScaffolderScaffoldResponse>;
// (undocumented)
streamLogs(
request: ScaffolderStreamLogsOptions,
options?: ScaffolderRequestOptions,
): Observable<LogEvent>;
}
// @public
export class ScaffolderClient implements ScaffolderApi {
constructor(options: {
discoveryApi: {
getBaseUrl(pluginId: string): Promise<string>;
};
fetchApi: {
fetch: typeof fetch;
};
identityApi?: {
getBackstageIdentity(): Promise<{
type: 'user';
userEntityRef: string;
ownershipEntityRefs: string[];
}>;
};
scmIntegrationsApi: ScmIntegrationRegistry;
useLongPollingLogs?: boolean;
});
autocomplete(
input: {
token: string;
provider: string;
resource: string;
context: Record<string, string>;
},
options?: ScaffolderRequestOptions,
): Promise<{
results: {
title?: string;
id: string;
}[];
}>;
cancelTask(
taskId: string,
options?: ScaffolderRequestOptions,
): Promise<{
status?: ScaffolderTaskStatus;
}>;
// (undocumented)
dryRun(
request: ScaffolderDryRunOptions,
options?: ScaffolderRequestOptions,
): Promise<ScaffolderDryRunResponse>;
// (undocumented)
getIntegrationsList(
options: ScaffolderGetIntegrationsListOptions,
): Promise<ScaffolderGetIntegrationsListResponse>;
// (undocumented)
getTask(
taskId: string,
options?: ScaffolderRequestOptions,
): Promise<ScaffolderTask>;
// (undocumented)
getTemplateParameterSchema(
templateRef: string,
options?: ScaffolderRequestOptions,
): Promise<TemplateParameterSchema>;
listActions(options?: ScaffolderRequestOptions): Promise<ListActionsResponse>;
// (undocumented)
listTasks(
request: {
filterByOwnership: 'owned' | 'all';
limit?: number;
offset?: number;
},
options?: ScaffolderRequestOptions,
): Promise<{
tasks: ScaffolderTask[];
totalTasks?: number;
}>;
listTemplatingExtensions(
options?: ScaffolderRequestOptions,
): Promise<ListTemplatingExtensionsResponse>;
retry(
taskId: string,
options?: ScaffolderRequestOptions,
): Promise<{
@@ -381,7 +381,7 @@ export class ScaffolderClient implements ScaffolderApi {
/**
* {@inheritdoc ScaffolderApi.retry}
*/
async retry?(
async retry(
taskId: string,
options?: ScaffolderRequestOptions,
): Promise<{ id: string }> {
@@ -393,22 +393,28 @@ export class ScaffolderClient implements ScaffolderApi {
/**
* {@inheritdoc ScaffolderApi.retry}
*/
async autocomplete({
token,
resource,
provider,
context,
}: {
token: string;
provider: string;
resource: string;
context: Record<string, string>;
}): Promise<{ results: { title?: string; id: string }[] }> {
async autocomplete(
{
token,
resource,
provider,
context,
}: {
token: string;
provider: string;
resource: string;
context: Record<string, string>;
},
options?: ScaffolderRequestOptions,
): Promise<{ results: { title?: string; id: string }[] }> {
return await this.requestRequired(
await this.apiClient.autocomplete({
path: { provider, resource },
body: { token, context },
}),
await this.apiClient.autocomplete(
{
path: { provider, resource },
body: { token, context },
},
options,
),
);
}
+5 -5
View File
@@ -285,12 +285,12 @@ export interface ScaffolderApi {
*
* @param taskId - the id of the task
*/
retry?(
retry(
taskId: string,
options?: ScaffolderRequestOptions,
): Promise<{ id: string }>;
listTasks?(
listTasks(
request: {
filterByOwnership: 'owned' | 'all';
limit?: number;
@@ -311,7 +311,7 @@ export interface ScaffolderApi {
/**
* Returns a structure describing the available templating extensions.
*/
listTemplatingExtensions?(
listTemplatingExtensions(
options?: ScaffolderRequestOptions,
): Promise<ListTemplatingExtensionsResponse>;
@@ -320,12 +320,12 @@ export interface ScaffolderApi {
options?: ScaffolderRequestOptions,
): Observable<LogEvent>;
dryRun?(
dryRun(
request: ScaffolderDryRunOptions,
options?: ScaffolderRequestOptions,
): Promise<ScaffolderDryRunResponse>;
autocomplete?(
autocomplete(
request: {
token: string;
provider: string;