Skip to content

Commit e3653c9

Browse files
authored
✨ Workflow - Source platform discover and import applications wizard (#2545)
Resolves: https://issues.redhat.com/browse/MTA-5249 Resolves: #2289 Adds a wizard to discover and import applications from a source platform: - The wizard captures discover and import filter data from the user - The filters are added to the import task - The set of filters are defined by a schema - The schema is pulled from domain, variant, subject "platform", "cloudfoundry", "filter" - `SchemaDefinedFields` to be used to input the schema data - Task summary data for platforms are displayed in a popover on the source platform table's name column ## Summary by CodeRabbit - **New Features** - Discover & Import wizard for source platforms (filters, review, results and submit flow) and a hook to start platform imports. - Platform name popover showing status and recent tasks with quick links. - **Enhancements** - Platform-aware import flow on Source Platforms page and a new Platforms filter in Applications table. - Schema editor accepts configurable height; complex schemas default to JSON view. - Tasks table shows fallback when application is missing and improved deletion error notifications. - **Localization** - Added "Discover & Import" and full platform discovery wizard translations. --------- Signed-off-by: Scott J Dickerson <[email protected]>
1 parent 5882462 commit e3653c9

29 files changed

+1436
-244
lines changed

client/public/locales/en/translation.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"discardAssessment": "Discard assessment(s)",
3535
"discardReview": "Discard review",
3636
"discoverApplications": "Discover applications",
37+
"discoverImport": "Discover & Import",
3738
"downloadCsvTemplate": "Download CSV template",
3839
"download": "Download {{what}}",
3940
"duplicate": "Duplicate",
@@ -691,6 +692,38 @@
691692
"selectedApplications_other": "Selected applications ({{count}})"
692693
}
693694
},
695+
"platformDiscoverWizard": {
696+
"title": "Discover Applications",
697+
"description": "Discover and import applications from the selected platform.",
698+
"noPlatformSelected": "No platform selected for discovery.",
699+
"toast": {
700+
"submittedOk": "Platform application discovery task submitted",
701+
"submittedFailed": "Platform application discovery task failed"
702+
},
703+
"filterInput": {
704+
"stepTitle": "Filters",
705+
"title": "Configure Discovery Filters",
706+
"description": "Configure the filters for discovering applications on platform {{platformName}} ({{platformKind}}). These filters will determine which applications are discovered and imported.",
707+
"filtersLabel": "{{platformName}} discovery filters",
708+
"noFiltersAvailable": "No discovery filters are available for platform kind {{platformKind}}."
709+
},
710+
"review": {
711+
"stepTitle": "Review",
712+
"title": "Review Platform Discovery",
713+
"description": "Review the platform and discovery filters before starting application discovery on {{platformName}}.",
714+
"discoveryFilters": "Discovery Filters"
715+
},
716+
"results": {
717+
"title": "Platform Discovery Results",
718+
"summary": "Summary of platform application discovery task submissions.",
719+
"noResults": "No results available.",
720+
"noTasksSubmitted": "No tasks were submitted.",
721+
"failedSubmissions_one": "Failed Submission",
722+
"failedSubmissions_other": "Failed Submissions ({{count}})",
723+
"successSubmissions_one": "Successful Submission",
724+
"successSubmissions_other": "Successful Submissions ({{count}})"
725+
}
726+
},
694727
"credentials": {
695728
"delete": {
696729
"application_one": "1 application",

client/src/app/api/models.ts

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ export type New<T extends { id: number }> = Omit<T, "id">;
1616
*/
1717
export type WithUiId<T> = T & { _ui_unique_id: string };
1818

19+
export interface EmptyObject extends Record<string, never> {}
20+
21+
export interface JsonDocument extends Record<string, unknown> {}
22+
1923
export interface HubFilter {
2024
field: string;
2125
operator?: "=" | "!=" | "~" | ">" | ">=" | "<" | "<=";
@@ -328,14 +332,14 @@ export type TaskState =
328332
| "Postponed"
329333
| "SucceededWithErrors"; // synthetic state for ease-of-use in UI;
330334

331-
export interface Task<DataType = TaskData> {
335+
export interface Task<DataType> {
332336
id: number;
333337
createUser?: string;
334338
updateUser?: string;
335339
createTime?: string;
336340

337341
name?: string;
338-
kind?: string;
342+
kind: string;
339343
addon?: string;
340344
extensions?: string[];
341345
state?: TaskState;
@@ -344,7 +348,7 @@ export interface Task<DataType = TaskData> {
344348
policy?: TaskPolicy;
345349
ttl?: TTL;
346350
data?: DataType;
347-
application: Ref;
351+
application?: Ref;
348352
platform?: Ref;
349353
bucket?: Ref;
350354
pod?: string;
@@ -357,15 +361,21 @@ export interface Task<DataType = TaskData> {
357361
attached?: TaskAttachment[];
358362
}
359363

360-
export type EmptyTaskData = Record<string, never>;
364+
export interface AnalysisTask
365+
extends Omit<Task<AnalysisTaskData>, "application" | "platform"> {
366+
kind: "analysis";
367+
application: Ref;
368+
}
361369

362-
export interface ApplicationTask<DataType>
363-
extends Omit<Task<DataType>, "application" | "platform"> {
370+
export interface ApplicationManifestTask
371+
extends Omit<Task<EmptyObject>, "application" | "platform"> {
372+
kind: "application-manifest";
364373
application: Ref;
365374
}
366375

367-
export interface PlatformTask<DataType>
368-
extends Omit<Task<DataType>, "application" | "platform"> {
376+
export interface PlatformApplicationImportTask
377+
extends Omit<Task<JsonDocument>, "application" | "platform"> {
378+
kind: "application-import";
369379
platform: Ref;
370380
}
371381

@@ -379,7 +389,8 @@ export interface TaskDashboard {
379389
kind?: string;
380390
addon?: string;
381391
state: TaskState;
382-
application: Ref;
392+
application?: Ref;
393+
platform?: Ref;
383394
started?: string; // ISO-8601
384395
terminated?: string; // ISO-8601
385396

@@ -419,7 +430,7 @@ export interface TaskAttachment {
419430
activity?: number;
420431
}
421432

422-
export interface TaskData {
433+
export interface AnalysisTaskData {
423434
tagger: {
424435
enabled: boolean;
425436
};
@@ -465,7 +476,7 @@ export interface Taskgroup {
465476
name: string;
466477
kind?: string;
467478
addon?: string;
468-
data: TaskData;
479+
data: AnalysisTaskData;
469480
tasks: TaskgroupTask[];
470481
}
471482

@@ -936,8 +947,6 @@ export interface GroupedStakeholderRef extends Ref {
936947
uniqueId: string;
937948
}
938949

939-
export interface JsonDocument extends Record<string, unknown> {}
940-
941950
export interface SourcePlatform {
942951
id: number;
943952
kind: string;

client/src/app/api/rest.ts

Lines changed: 2 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,7 @@ import {
3434
Tag,
3535
TagCategory,
3636
Target,
37-
Task,
3837
Taskgroup,
39-
TaskQueue,
40-
TaskDashboard,
4138
Ticket,
4239
Tracker,
4340
TrackerProject,
@@ -96,7 +93,6 @@ export const TAG_CATEGORIES = hub`/tagcategories`;
9693
export const TAGS = hub`/tags`;
9794
export const TARGETS = hub`/targets`;
9895
export const TASKGROUPS = hub`/taskgroups`;
99-
export const TASKS = hub`/tasks`;
10096
export const TICKETS = hub`/tickets`;
10197
export const TRACKER_PROJECT_ISSUETYPES = "issuetypes"; // TODO: ????
10298
export const TRACKER_PROJECTS = "projects"; // TODO: ????
@@ -122,8 +118,9 @@ export const HEADERS: Record<string, RawAxiosRequestHeaders> = {
122118

123119
export * from "./rest/analysis";
124120
export * from "./rest/files";
125-
export * from "./rest/schemas";
126121
export * from "./rest/generators";
122+
export * from "./rest/schemas";
123+
export * from "./rest/tasks";
127124

128125
/**
129126
* Provide consistent fetch and processing for server side filtering and sorting with
@@ -378,101 +375,6 @@ export const getApplicationSummaryCSV = (id: string) => {
378375
);
379376
};
380377

381-
// ---------------------------------------
382-
// Tasks
383-
//
384-
export function getTaskById(id: number) {
385-
return axios
386-
.get<Task>(`${TASKS}/${id}`, {
387-
headers: { ...HEADERS.json },
388-
responseType: "json",
389-
})
390-
.then((response) => {
391-
return response.data;
392-
});
393-
}
394-
395-
export function getTaskByIdAndFormat(
396-
id: number,
397-
format: "json" | "yaml",
398-
merged: boolean = false
399-
): Promise<string> {
400-
const isYaml = format === "yaml";
401-
const headers = isYaml ? { ...HEADERS.yaml } : { ...HEADERS.json };
402-
const responseType = isYaml ? "text" : "json";
403-
404-
let url = `${TASKS}/${id}`;
405-
if (merged) {
406-
url += "?merged=1";
407-
}
408-
409-
return axios
410-
.get<Task | string>(url, {
411-
headers: headers,
412-
responseType: responseType,
413-
})
414-
.then((response) => {
415-
return isYaml
416-
? String(response.data ?? "")
417-
: JSON.stringify(response.data, undefined, 2);
418-
});
419-
}
420-
421-
export function getTasksByIds(
422-
ids: number[],
423-
format: "json" | "yaml" = "json"
424-
): Promise<Task[]> {
425-
const isYaml = format === "yaml";
426-
const headers = isYaml ? { ...HEADERS.yaml } : { ...HEADERS.json };
427-
const responseType = isYaml ? "text" : "json";
428-
const filterParam = `id:(${ids.join("|")})`;
429-
430-
return axios
431-
.get<Task[]>(`${TASKS}`, {
432-
headers,
433-
responseType,
434-
params: {
435-
filter: filterParam,
436-
},
437-
})
438-
.then((response) => {
439-
return response.data;
440-
});
441-
}
442-
443-
export const getTasksDashboard = () =>
444-
axios
445-
.get<TaskDashboard[]>(`${TASKS}/report/dashboard`)
446-
.then((response) => response.data);
447-
448-
export const getServerTasks = (params: HubRequestParams = {}) =>
449-
getHubPaginatedResult<Task>(TASKS, params);
450-
451-
export const deleteTask = (id: number) => axios.delete<void>(`${TASKS}/${id}`);
452-
453-
export const cancelTask = (id: number) =>
454-
axios.put<void>(`${TASKS}/${id}/cancel`);
455-
456-
export const cancelTasks = (ids: number[]) =>
457-
axios.put<void>(`${TASKS}/cancel?filter=id:(${ids.join("|")})`);
458-
459-
export const getTaskQueue = (addon?: string): Promise<TaskQueue> =>
460-
axios
461-
.get<TaskQueue>(`${TASKS}/report/queue`, { params: { addon } })
462-
.then(({ data }) => data);
463-
464-
export const createTask = <TaskData, TaskType extends Task<TaskData>>(
465-
task: New<TaskType>
466-
) => axios.post<TaskType>(TASKS, task).then((response) => response.data);
467-
468-
export const updateTask = <TaskData, TaskType extends Task<TaskData>>(
469-
task: Partial<TaskType> & { id: number }
470-
) => axios.patch<TaskType>(`${TASKS}/${task.id}`, task);
471-
472-
export const submitTask = <TaskData, TaskType extends Task<TaskData>>(
473-
task: TaskType
474-
) => axios.put<void>(`${TASKS}/${task.id}/submit`, { id: task.id });
475-
476378
// ---------------------------------------
477379
// Task Groups
478380
//

client/src/app/api/rest/schemas.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ export const getPlatformCoordinatesSchema = (platformKind: string) =>
2323
)
2424
.then(({ data }) => data);
2525

26-
export const getPlatformDiscoveryFiltersSchema = (platformKind: string) =>
26+
export const getPlatformDiscoveryFilterSchema = (platformKind: string) =>
2727
axios
2828
.get<TargetedSchema>(
2929
template(TARGETED_SCHEMA, {
3030
domain: "platform",
3131
variant: platformKind,
32-
subject: "filters",
32+
subject: "filter",
3333
})
3434
)
3535
.then(({ data }) => data);

0 commit comments

Comments
 (0)