Skip to content

Commit a9292cc

Browse files
committed
refactor: Use useMigrationBlocksInfo to get reasons in partial import
1 parent a67d79c commit a9292cc

7 files changed

Lines changed: 134 additions & 51 deletions

File tree

src/data/api.mocks.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ mockGetMigrationStatus.migrationStatusData = {
5656
components: 3,
5757
unsupported: 0,
5858
},
59-
unsupportedReasons: [],
6059
},
6160
],
6261
} as api.MigrateTaskStatusData;
@@ -91,7 +90,6 @@ mockGetMigrationStatus.migrationStatusFailedData = {
9190
components: 0,
9291
unsupported: 0,
9392
},
94-
unsupportedReasons: [],
9593
},
9694
],
9795
} as api.MigrateTaskStatusData;
@@ -126,7 +124,6 @@ mockGetMigrationStatus.migrationStatusFailedMultipleData = {
126124
components: 0,
127125
unsupported: 0,
128126
},
129-
unsupportedReasons: [],
130127
},
131128
{
132129
id: 2,
@@ -147,7 +144,6 @@ mockGetMigrationStatus.migrationStatusFailedMultipleData = {
147144
components: 0,
148145
unsupported: 0,
149146
},
150-
unsupportedReasons: [],
151147
},
152148
],
153149
} as api.MigrateTaskStatusData;
@@ -182,7 +178,6 @@ mockGetMigrationStatus.migrationStatusFailedOneLibraryData = {
182178
components: 0,
183179
unsupported: 0,
184180
},
185-
unsupportedReasons: [],
186181
},
187182
{
188183
id: 2,
@@ -203,7 +198,6 @@ mockGetMigrationStatus.migrationStatusFailedOneLibraryData = {
203198
components: 0,
204199
unsupported: 0,
205200
},
206-
unsupportedReasons: [],
207201
},
208202
],
209203
} as api.MigrateTaskStatusData;
@@ -239,7 +233,6 @@ mockGetMigrationStatus.migrationStatusInProgressData = {
239233
components: 0,
240234
unsupported: 0,
241235
},
242-
unsupportedReasons: [],
243236
},
244237
],
245238
} as api.MigrateTaskStatusData;
@@ -277,11 +270,6 @@ mockGetMigrationStatus.migrationStatusPartialData = {
277270
components: 3,
278271
unsupported: 1,
279272
},
280-
unsupportedReasons: [{
281-
blockName: 'Legacy library content',
282-
blockType: 'library_content',
283-
reason: 'The block has children, so it is not supported in content libraries',
284-
}],
285273
},
286274
],
287275
} as api.MigrateTaskStatusData;

src/data/api.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,6 @@ export interface MigrateParameters {
105105
components: number;
106106
unsupported: number;
107107
}
108-
unsupportedReasons: {
109-
blockName: string;
110-
blockType: string;
111-
reason: string;
112-
}[];
113108
}
114109

115110
export interface MigrateTaskStatusData {

src/library-authoring/LibraryContent.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ const LibraryContent = ({ contentType = ContentType.home }: LibraryContentProps)
5656
libraryId,
5757
collectionId,
5858
true,
59+
undefined,
5960
showPlaceholderBlocks,
6061
);
6162
// Fetch unsupported blocks usage_key information from meilisearch index.

src/library-authoring/data/api.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,7 @@ export async function getModulestoreMigrationBlocksInfo(
853853
libraryId: string,
854854
collectionId?: string,
855855
isFailed?: boolean,
856+
taskUuid?: string,
856857
): Promise<BlockMigrationInfo[]> {
857858
const client = getAuthenticatedHttpClient();
858859

@@ -861,6 +862,9 @@ export async function getModulestoreMigrationBlocksInfo(
861862
if (collectionId) {
862863
params.append('target_collection_key', collectionId);
863864
}
865+
if (taskUuid) {
866+
params.append('task_uuid', taskUuid);
867+
}
864868
if (isFailed !== undefined) {
865869
params.append('is_failed', JSON.stringify(isFailed));
866870
}

src/library-authoring/data/apiHooks.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -995,10 +995,16 @@ export const useMigrationBlocksInfo = (
995995
libraryId: string,
996996
collectionId?: string,
997997
isFailed?: boolean,
998+
taskUuid?: string,
998999
enabled = true,
9991000
) => (
10001001
useQuery({
10011002
queryKey: libraryAuthoringQueryKeys.migrationBlocksInfo(libraryId, collectionId, isFailed),
1002-
queryFn: enabled ? () => api.getModulestoreMigrationBlocksInfo(libraryId, collectionId, isFailed) : skipToken,
1003+
queryFn: enabled ? () => api.getModulestoreMigrationBlocksInfo(
1004+
libraryId,
1005+
collectionId,
1006+
isFailed,
1007+
taskUuid,
1008+
) : skipToken,
10031009
})
10041010
);

src/library-authoring/import-course/ImportDetailsPage.test.tsx

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,19 @@ import {
77
} from '@src/testUtils';
88
import { mockGetMigrationStatus } from '@src/data/api.mocks';
99
import { bulkModulestoreMigrateUrl } from '@src/data/api';
10+
import { useGetContentHits } from '@src/search-manager';
1011
import { ImportDetailsPage } from './ImportDetailsPage';
1112
import { LibraryProvider } from '../common/context/LibraryContext';
1213
import { mockContentLibrary } from '../data/api.mocks';
14+
import { getModulestoreMigratedBlocksInfoUrl } from '../data/api';
15+
import { libraryComponentsMock } from '../__mocks__';
1316

1417
mockContentLibrary.applyMock();
1518
mockGetMigrationStatus.applyMock();
1619
const { libraryId } = mockContentLibrary;
1720
const mockNavigate = jest.fn();
21+
const mockUseSearchContext = jest.fn();
22+
const mockFetchNextPage = jest.fn();
1823
let axiosMock;
1924

2025
// Mock the useCourseDetails hook
@@ -27,6 +32,12 @@ jest.mock('react-router-dom', () => ({
2732
useNavigate: () => mockNavigate,
2833
}));
2934

35+
jest.mock('@src/search-manager', () => ({
36+
...jest.requireActual('@src/search-manager'),
37+
useSearchContext: () => mockUseSearchContext(),
38+
useGetContentHits: jest.fn().mockReturnValue({ isPending: true, data: null }),
39+
}));
40+
3041
const render = (migrationTaskId: string) => (
3142
testRender(
3243
<ImportDetailsPage />,
@@ -96,22 +107,58 @@ describe('', () => {
96107
});
97108

98109
it('should render Partial Succeeded state', async () => {
110+
axiosMock.onGet(getModulestoreMigratedBlocksInfoUrl()).reply(200, [
111+
{
112+
sourceKey: 'block-v1:UNIX+UX2+2025_T2+type@library_content+block@test_lib_content',
113+
targetKey: null,
114+
unsupportedReason: 'The "library_content" XBlock (ID: "test_lib_content") has children, so it not supported in content libraries. It has 2 children blocks.',
115+
},
116+
]);
117+
(useGetContentHits as jest.Mock).mockReturnValue({
118+
isPending: false,
119+
data: {
120+
hits: [
121+
{
122+
display_name: 'Randomized Content Block',
123+
usage_key: 'block-v1:UNIX+UX2+2025_T2+type@library_content+block@test_lib_content',
124+
block_type: 'library_content',
125+
},
126+
],
127+
query: '',
128+
processingTimeMs: 0,
129+
limit: 1,
130+
offset: 0,
131+
estimatedTotalHits: 1,
132+
},
133+
});
134+
mockUseSearchContext.mockReturnValue({
135+
totalContentAndCollectionHits: 0,
136+
contentAndCollectionHits: [],
137+
isFetchingNextPage: false,
138+
hasNextPage: false,
139+
fetchNextPage: mockFetchNextPage,
140+
searchKeywords: '',
141+
isFiltered: false,
142+
isPending: false,
143+
hits: libraryComponentsMock,
144+
});
145+
99146
render(mockGetMigrationStatus.migrationIdPartial);
100147
expect(await screen.findByText(/partial import successful/i)).toBeInTheDocument();
101148

102149
expect(screen.getByText(
103150
/85% of course test course has been imported successfully/i,
104151
)).toBeInTheDocument();
105152

106-
expect(screen.getByRole('cell', {
107-
name: /legacy library content/i,
153+
expect(await screen.findByRole('cell', {
154+
name: /randomized content block/i,
155+
})).toBeInTheDocument();
156+
expect(await screen.findByRole('cell', {
157+
name: 'library_content',
108158
})).toBeInTheDocument();
109-
expect(screen.getByRole('cell', {
110-
name: /library_content/i,
159+
expect(await screen.findByRole('cell', {
160+
name: /has children, so it not supported in content libraries/i,
111161
})).toBeInTheDocument();
112-
expect(screen.getByRole('cell', {
113-
name: /the block has children, so it is not supported in content libraries/i,
114-
}));
115162

116163
const viewImportedContentBtn = screen.getByRole('button', {
117164
name: /view imported content/i,

src/library-authoring/import-course/ImportDetailsPage.tsx

Lines changed: 68 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useContext, useState } from 'react';
1+
import { useContext, useMemo, useState } from 'react';
22
import { Helmet } from 'react-helmet';
33
import { useNavigate, useParams } from 'react-router-dom';
44
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
@@ -18,10 +18,12 @@ import { ToastContext } from '@src/generic/toast-context';
1818
import { Paragraph } from '@src/utils';
1919

2020
import { useBulkModulestoreMigrate, useModulestoreMigrationStatus } from '@src/data/apiHooks';
21+
import { useGetContentHits } from '@src/search-manager';
2122
import messages from './messages';
2223
import { SummaryCard } from './stepper/SummaryCard';
2324
import { HelpSidebar } from './HelpSidebar';
2425
import { useLibraryContext } from '../common/context/LibraryContext';
26+
import { useMigrationBlocksInfo } from '../data/apiHooks';
2527

2628
export const ImportDetailsPage = () => {
2729
const intl = useIntl();
@@ -78,6 +80,43 @@ export const ImportDetailsPage = () => {
7880
}
7981
}
8082

83+
const {
84+
data: migrationBlockInfo,
85+
isPending: isPendingMigrationBlockInfo,
86+
} = useMigrationBlocksInfo(
87+
libraryId,
88+
undefined,
89+
true,
90+
migrationTaskId,
91+
migrationStatus === 'Partial Succeeded',
92+
);
93+
// Fetch unsupported blocks usage_key information from meilisearch index.
94+
const { data: unssupportedBlocksData } = useGetContentHits(
95+
[
96+
`usage_key IN [${migrationBlockInfo?.map((block) => `"${block.sourceKey}"`).join(',')}]`,
97+
],
98+
(migrationBlockInfo?.length || 0) > 0,
99+
['usage_key', 'block_type', 'display_name'],
100+
migrationBlockInfo?.length,
101+
true,
102+
);
103+
const unsupportedTableData = useMemo(() => {
104+
if (!migrationBlockInfo || !unssupportedBlocksData) {
105+
return [];
106+
}
107+
108+
const reasons = migrationBlockInfo.reduce((result, block) => ({
109+
...result,
110+
[block.sourceKey]: block.unsupportedReason || '',
111+
}), {} as Record<string, string>);
112+
113+
return unssupportedBlocksData.hits.map(block => ({
114+
blockName: block.display_name,
115+
blockType: block.block_type,
116+
reason: reasons[block.usage_key],
117+
}));
118+
}, [migrationBlockInfo, unssupportedBlocksData]);
119+
81120
if (enableRefeshState && migrationStatus !== 'In Progress') {
82121
setEnableRefreshState(false);
83122
}
@@ -242,32 +281,35 @@ export const ImportDetailsPage = () => {
242281
}}
243282
/>
244283
</div>
245-
<DataTable
246-
isPaginated
247-
initialState={{
248-
pageSize: 10,
249-
}}
250-
itemCount={courseImportDetails.unsupportedReasons.length}
251-
columns={[
252-
{
253-
Header: intl.formatMessage(messages.importPartialReasonTableBlockName),
254-
accessor: 'blockName',
284+
{!isPendingMigrationBlockInfo && unsupportedTableData && (
285+
<DataTable
286+
isPaginated
287+
initialState={{
288+
pageSize: 10,
289+
}}
290+
itemCount={unsupportedTableData.length}
291+
columns={[
292+
{
293+
Header: intl.formatMessage(messages.importPartialReasonTableBlockName),
294+
accessor: 'blockName',
295+
296+
},
297+
{
298+
Header: intl.formatMessage(messages.importPartialReasonTableBlockType),
299+
accessor: 'blockType',
300+
},
301+
{
302+
Header: intl.formatMessage(messages.importPartialReasonTableReason),
303+
accessor: 'reason',
304+
},
305+
]}
306+
data={unsupportedTableData}
307+
>
308+
<DataTable.Table />
309+
<DataTable.TableFooter />
310+
</DataTable>
311+
)}
255312

256-
},
257-
{
258-
Header: intl.formatMessage(messages.importPartialReasonTableBlockType),
259-
accessor: 'blockType',
260-
},
261-
{
262-
Header: intl.formatMessage(messages.importPartialReasonTableReason),
263-
accessor: 'reason',
264-
},
265-
]}
266-
data={courseImportDetails.unsupportedReasons}
267-
>
268-
<DataTable.Table />
269-
<DataTable.TableFooter />
270-
</DataTable>
271313
<div className="w-100 d-flex justify-content-end">
272314
<Button
273315
variant="outline-primary"

0 commit comments

Comments
 (0)