diff --git a/api/src/routes/contentMapper.routes.ts b/api/src/routes/contentMapper.routes.ts index 30853461..d9fbfd8c 100644 --- a/api/src/routes/contentMapper.routes.ts +++ b/api/src/routes/contentMapper.routes.ts @@ -36,7 +36,7 @@ router.get( * @route GET /:projectId */ router.get( - "/:projectId", + "/:projectId/contentTypes/:contentTypeUid?", asyncRouter(contentMapperController.getExistingContentTypes) ); @@ -45,7 +45,7 @@ router.get( * @route GET /:projectId */ router.get( - "/:projectId/globalFields", + "/:projectId/globalFields/:globalFieldUid?", asyncRouter(contentMapperController.getExistingGlobalFields) ); @@ -71,10 +71,10 @@ router.put( * Get Single contenttype data * @route GET /:projectId/:contentTypeUid */ -router.get( - "/:projectId/:contentTypeUid", - asyncRouter(contentMapperController.getSingleContentTypes) -); +// router.get( +// "/:projectId/:contentTypeUid", +// asyncRouter(contentMapperController.getSingleContentTypes) +// ); /** * Remove content mapper @@ -95,8 +95,8 @@ router.patch("/:orgId/:projectId/mapper_keys", asyncRouter(contentMapperControll * Get Single Global Field data * @route GET /:projectId/:globalFieldUid */ -router.get("/:projectId/globalFields/:globalFieldUid", - asyncRouter(contentMapperController.getSingleGlobalField) -); +// router.get("/:projectId/globalFields/:globalFieldUid", +// asyncRouter(contentMapperController.getSingleGlobalField) +// ); export default router; diff --git a/api/src/services/contentMapper.service.ts b/api/src/services/contentMapper.service.ts index 1b67fe0a..8febbd8e 100644 --- a/api/src/services/contentMapper.service.ts +++ b/api/src/services/contentMapper.service.ts @@ -240,6 +240,7 @@ const getFieldMapping = async (req: Request) => { */ const getExistingContentTypes = async (req: Request) => { const projectId = req?.params?.projectId; + const contentTypeUID = req?.params?.contentTypeUid ?? ''; // UID of the selected content type, if any const { token_payload } = req.body; @@ -247,6 +248,7 @@ const getExistingContentTypes = async (req: Request) => { token_payload?.region, token_payload?.user_id ); + await ProjectModelLowdb.read(); const project = ProjectModelLowdb.chain .get("projects") @@ -257,13 +259,21 @@ const getExistingContentTypes = async (req: Request) => { const baseUrl = `${config.CS_API[ token_payload?.region as keyof typeof config.CS_API ]!}/content_types`; + const headers = { api_key: stackId, - authtoken: authtoken, + authtoken, }; try { - const contentTypes = await fetchAllPaginatedData(baseUrl, headers, 100, "getExistingContentTypes", "content_types"); + // Step 1: Fetch the updated list of all content types + const contentTypes = await fetchAllPaginatedData( + baseUrl, + headers, + 100, + 'getExistingContentTypes', + 'content_types' + ); const processedContentTypes = contentTypes.map((singleCT: any) => ({ title: singleCT.title, @@ -271,8 +281,37 @@ const getExistingContentTypes = async (req: Request) => { schema: singleCT.schema, })); - return { contentTypes: processedContentTypes }; - } catch (error:any) { + // Step 2: Fetch data for the selected content type (if `contentTypeUID` is provided) + let selectedContentType = null; + + if (contentTypeUID) { + const [err, res] = await safePromise( + https({ + method: 'GET', + url: `${baseUrl}/${contentTypeUID}`, + headers, + }) + ); + + if (err) { + throw new Error( + `Error fetching selected content type: ${ + err.response?.data || err.message + }` + ); + } + + selectedContentType = { + title: res.data.content_type?.title, + uid: res.data.content_type?.uid, + schema: res.data.content_type?.schema, + }; + } + return { + contentTypes: processedContentTypes, + selectedContentType, + }; + } catch (error: any) { return { data: error.message, status: 500, @@ -287,6 +326,7 @@ const getExistingContentTypes = async (req: Request) => { */ const getExistingGlobalFields = async (req: Request) => { const projectId = req?.params?.projectId; + const globalFieldUID = req?.params?.globalFieldUid ?? ''; // UID of the selected global field, if any if (!projectId) { return { @@ -335,6 +375,8 @@ const getExistingGlobalFields = async (req: Request) => { authtoken, }; + // Step 1: Fetch the updated list of all global fields + const globalFields = await fetchAllPaginatedData(baseUrl, headers, 100, 'getExistingGlobalFields', 'global_fields'); const processedGlobalFields = globalFields.map((global: any) => ({ @@ -343,7 +385,34 @@ const getExistingGlobalFields = async (req: Request) => { schema: global.schema, })); - return { globalFields: processedGlobalFields }; + // Step 2: Fetch data for the selected global field (if `globalFieldUID` is provided) + let selectedGlobalField = null; + + if (globalFieldUID) { + const [err, res] = await safePromise( + https({ + method: 'GET', + url: `${baseUrl}/${globalFieldUID}`, + headers, + }) + ); + + if (err) { + throw new Error( + `Error fetching selected global field: ${ + err.response?.data || err.message + }` + ); + } + + selectedGlobalField = { + title: res.data.global_field?.title, + uid: res.data.global_field?.uid, + schema: res.data.global_field?.schema, + }; + } + + return { globalFields: processedGlobalFields, selectedGlobalField }; } catch (error:any) { return { data: error.message || 'An unknown error occurred', @@ -382,7 +451,7 @@ const updateContentType = async (req: Request) => { // Check project status if ( - [NEW_PROJECT_STATUS[5], NEW_PROJECT_STATUS[4]].includes(project.status) || + [NEW_PROJECT_STATUS[5]].includes(project.status) || project.current_step < STEPPER_STEPS.CONTENT_MAPPING ) { logger.error( diff --git a/ui/src/components/ContentMapper/index.tsx b/ui/src/components/ContentMapper/index.tsx index b315e9d7..efe14ce7 100644 --- a/ui/src/components/ContentMapper/index.tsx +++ b/ui/src/components/ContentMapper/index.tsx @@ -23,9 +23,9 @@ import { getFieldMapping, updateContentType, resetToInitialMapping, - fetchExistingContentType, - updateContentMapper, - fetchGlobalField + getExistingContentTypes, + getExistingGlobalFields, + updateContentMapper } from '../../services/api/migration.service'; // Redux @@ -81,6 +81,7 @@ const Fields: MappingFields = { 'multi_line_text':{ label : 'Multi Line Textbox', options : { + 'Multi Line Textbox': 'multi_line_text', 'HTML Rich text Editor': 'html', 'JSON Rich Text Editor':'json'} }, @@ -207,10 +208,10 @@ const ContentMapper = forwardRef(({handleStepChange}: contentMapperProps, ref: R const [searchText, setSearchText] = useState(''); const [contentTypes, setContentTypes] = useState([]); - const [otherCmsTitle, setOtherCmsTitle] = useState(contentTypes[0]?.otherCmsTitle); + const [otherCmsTitle, setOtherCmsTitle] = useState(''); const [contentTypeUid, setContentTypeUid] = useState(''); - const [isContentType, setIsContentType] = useState(contentTypes?.[0]?.type === "content_type"); + const [isContentType, setIsContentType] = useState(true); const [contentModels, setContentModels] = useState([]); const [selectedContentType, setSelectedContentType] = useState(); @@ -220,9 +221,10 @@ const ContentMapper = forwardRef(({handleStepChange}: contentMapperProps, ref: R const [contentTypeMapped, setContentTypeMapped] = useState( newMigrationData?.content_mapping?.content_type_mapping || {} ); + const [otherContentType, setOtherContentType] = useState({ - label: contentTypeMapped?.[otherCmsTitle] || `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack`, - value: contentTypeMapped?.[otherCmsTitle] || `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack`, + label: contentTypeMapped?.[otherCmsTitle] ?? `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack`, + value: contentTypeMapped?.[otherCmsTitle] ?? `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack`, }); const [otherCmsUid, setOtherCmsUid] = useState(contentTypes[0]?.otherCmsUid); @@ -298,7 +300,7 @@ const ContentMapper = forwardRef(({handleStepChange}: contentMapperProps, ref: R useEffect(() => { const mappedContentType = contentModels && contentModels?.find((item)=> item?.title === newMigrationData?.content_mapping?.content_type_mapping?.[otherCmsTitle]); - if (contentTypeMapped && otherCmsTitle ) { + // if (contentTypeMapped && otherCmsTitle ) { if (mappedContentType?.uid) { setOtherContentType({ @@ -307,13 +309,14 @@ const ContentMapper = forwardRef(({handleStepChange}: contentMapperProps, ref: R value: mappedContentType?.title }); setIsContentDeleted(false); - } else { - setOtherContentType({ - label: `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack`, - value: `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack` - }); - } - } + } + // else { + // setOtherContentType({ + // label: `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack`, + // value: `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack` + // }); + // } + // } }, [contentTypeMapped, otherCmsTitle, contentModels]); useEffect(()=>{ @@ -421,11 +424,11 @@ const ContentMapper = forwardRef(({handleStepChange}: contentMapperProps, ref: R // To fetch existing content types or global fields as per the type useEffect(() => { if(isContentType) { - setContentModels(JSON?.parse(JSON?.stringify(reduxContentTypes ?? [])) ); + setContentModels(JSON?.parse(JSON?.stringify(reduxContentTypes ?? []))); } else { - if (reduxGlobalFields?.length > 0) { - setContentModels(JSON?.parse(JSON?.stringify(reduxGlobalFields)) ?? []); - } + // if (reduxGlobalFields?.length > 0) { + setContentModels(JSON?.parse(JSON?.stringify(reduxGlobalFields ?? []))); + // } } }, [isContentType, reduxContentTypes, reduxGlobalFields]); @@ -612,17 +615,15 @@ const ContentMapper = forwardRef(({handleStepChange}: contentMapperProps, ref: R setActive(i); const otherTitle = contentTypes?.[i]?.otherCmsTitle; setOtherCmsTitle(otherTitle); - - // setOtherContentType({ - // label: contentTypeMapped?.[otherTitle] || 'Select content type from existing stack', - // value: contentTypeMapped?.[otherTitle] || 'Select content type from existing stack' - // }); - setContentTypeUid(contentTypes?.[i]?.id ?? ''); fetchFields(contentTypes?.[i]?.id ?? '', searchText || ''); setOtherCmsUid(contentTypes?.[i]?.otherCmsUid); setSelectedContentType(contentTypes?.[i]); setIsContentType(contentTypes?.[i]?.type === "content_type"); + setOtherContentType({ + label: contentTypeMapped?.[otherTitle] ?? `Select ${contentTypes?.[i]?.type === "content_type" ? 'Content Type' : 'Global Field'} from existing stack`, + value: contentTypeMapped?.[otherTitle] ?? `Select ${contentTypes?.[i]?.type === "content_type" ? 'Content Type' : 'Global Field'} from existing stack` + }); } const updateFieldSettings = (rowId: string, updatedSettings: Advanced, checkBoxChanged: boolean) => { @@ -1599,54 +1600,38 @@ const ContentMapper = forwardRef(({handleStepChange}: contentMapperProps, ref: R } } }; - - // Function to fetch single content type + /** + * Retrieves existing content types for a given project. + * @returns An array containing the retrieved content types or global fields based on condition if itContentType true and if existing content type or global field id is passed then returns an object containing title, uid and schema of that particular content type or global field. + */ const handleFetchContentType = async () => { if (isContentType) { try { - const { data , status} = await fetchExistingContentType(projectId, otherContentType?.id ?? ''); - - if (status == 201) { - if (data?.contentTypes?.length > 0) { - setContentModels(data?.contentTypes); - Notification({ - notificationContent: { text: 'Content Types fetched successfully' }, - notificationProps: { - position: 'bottom-center', - hideProgressBar: false - }, - type: 'success' - }); - } else if (data?.schema?.length > 0) { - - const contentTypesArr: ContentTypeList[] = contentModels; - const index = contentModels?.findIndex(ct => ct?.uid === data?.uid); - - if(index != -1) { - contentTypesArr[index] = data; - } - - setContentModels(contentTypesArr); - setContentTypeSchema(data?.schema); - Notification({ - notificationContent: { text: 'Content type data fetched successfully' }, - notificationProps: { - position: 'bottom-center', - hideProgressBar: false - }, - type: 'success' - }); - } else { - Notification({ - notificationContent: { text: "No content found in the stack" }, - notificationProps: { - position: 'bottom-center', - hideProgressBar: false - }, - type: 'error' - }); - } + const { data , status} = await getExistingContentTypes(projectId, otherContentType?.id ?? ''); + + if (status == 201 && data?.contentTypes?.length > 0) { + setContentModels(data?.contentTypes); + Notification({ + notificationContent: { text: 'Content Types fetched successfully' }, + notificationProps: { + position: 'bottom-center', + hideProgressBar: false + }, + type: 'success' + }); + if (data?.selectedContentType?.schema?.length > 0) { + setContentTypeSchema(data?.selectedContentType?.schema); + } + } else { + Notification({ + notificationContent: { text: "No content found in the stack" }, + notificationProps: { + position: 'bottom-center', + hideProgressBar: false + }, + type: 'error' + }); } } catch (error) { console.log(error); @@ -1654,42 +1639,19 @@ const ContentMapper = forwardRef(({handleStepChange}: contentMapperProps, ref: R } } else { try { - const { data, status } = await fetchGlobalField(projectId, otherContentType?.id ?? ''); - - if (status == 201) { - - if (data?.globalFields?.length > 0) { - setContentModels(data?.globalFields); - Notification({ - notificationContent: { text: 'Global Fields fetched successfully' }, - notificationProps: { - position: 'bottom-center', - hideProgressBar: false - }, - type: 'success' - }); + const { data, status } = await getExistingGlobalFields(projectId, otherContentType?.id ?? ''); - } else if (data?.schema?.length > 0) { - const index = contentModels?.findIndex(ct => ct?.uid === data?.uid); - - const globalFieldsArr: ContentTypeList[] = contentModels; - - if(index != -1) { - globalFieldsArr[index] = data; - } - setContentModels(globalFieldsArr); - setContentTypeSchema(data?.schema); - - Notification({ - notificationContent: { text: 'Global Field data fetched successfully' }, - notificationProps: { - position: 'bottom-center', - hideProgressBar: false - }, - type: 'success' - }); - } - } else { + if (status == 201 && data?.globalFields?.length > 0) { + setContentModels(data?.globalFields); + Notification({ + notificationContent: { text: 'Global Fields fetched successfully' }, + notificationProps: { + position: 'bottom-center', + hideProgressBar: false + }, + type: 'success' + }); + } else { Notification({ notificationContent: { text: "No Global Fields found in the stack" }, notificationProps: { @@ -1996,7 +1958,7 @@ const ContentMapper = forwardRef(({handleStepChange}: contentMapperProps, ref: R options={adjustedOption} width="440px" maxWidth="440px" - placeholder={otherContentType && `Select ${isContentType ? 'Content Type' : 'Global Field'} from Existing Stack`} + placeholder={otherContentType?.label} isSearchable version="v2" /> diff --git a/ui/src/components/DestinationStack/Actions/LoadOrganisation.tsx b/ui/src/components/DestinationStack/Actions/LoadOrganisation.tsx index 4fa149e1..3eb35305 100644 --- a/ui/src/components/DestinationStack/Actions/LoadOrganisation.tsx +++ b/ui/src/components/DestinationStack/Actions/LoadOrganisation.tsx @@ -28,7 +28,7 @@ const LoadOrganisation = () => { /**** ALL USEEffects HERE ****/ useEffect(() => { - const org :IDropDown = !isEmptyString(newMigrationData.destination_stack.selectedOrg.label) + const org :IDropDown = !isEmptyString(newMigrationData?.destination_stack?.selectedOrg?.label) ? newMigrationData?.destination_stack?.selectedOrg : selectedOrganisation; @@ -37,7 +37,7 @@ const LoadOrganisation = () => { setNewMigrationData({ ...newMigrationData, destination_stack: { - ...newMigrationData.destination_stack, + ...newMigrationData?.destination_stack, selectedOrg: { ...org } } }); @@ -47,7 +47,7 @@ const LoadOrganisation = () => {
{}; currentStep: number; handleStepChange: (stepIndex: number, closeStep?: boolean) => void; } @@ -40,12 +38,22 @@ const LoadPreFix = (props: LoadSelectCmsProps) => { const [isCheckedBoxChecked] = useState( newMigrationData?.legacy_cms?.isRestictedKeywordCheckboxChecked || false ); - const [isRestrictedkey, setIsRestrictedKey] = useState(false); + const [isRestrictedKey, setIsRestrictedKey] = useState(false); + const [restrictedKeywords, setRestrictedKeywords] = useState([]) - const idArray = restrictedKeywords.idArray; + useEffect(() => { + fetchRestrictedKeywords(); + }, []); + const fetchRestrictedKeywords = async () => { + const restrictedIds = await getRestrictedKeywords(); + if (restrictedIds?.status === 200) { + setRestrictedKeywords(restrictedIds?.data?.restricted) + } + } + /**** ALL METHODS HERE ****/ const handleOnChange = useDebouncer(async(e: ChangeEvent) => { @@ -53,7 +61,7 @@ const LoadPreFix = (props: LoadSelectCmsProps) => { const value = e.target.value; if (!isEmptyString(value)) { - if (idArray?.includes(value)) { + if (restrictedKeywords?.includes(value)) { setIsError(true); setErrorMessage('Affix should be valid and not a restricted keyword'); setIsRestrictedKey(true); @@ -119,17 +127,17 @@ const LoadPreFix = (props: LoadSelectCmsProps) => { version="v2" error={isError} aria-label='affix' - disabled={newMigrationData?.project_current_step > 1} - isReadOnly={newMigrationData?.project_current_step > 1} + disabled={newMigrationData?.legacy_cms?.uploadedFile?.isValidated} + isReadOnly={newMigrationData?.legacy_cms?.uploadedFile?.isValidated} /> {isError &&

{errorMessage}

}
- { isRestrictedkey && + { isRestrictedKey &&
-

Please refer the list of Contentstack - restricted keywords +

+ Please refer the list of Contentstack restricted keywords

} diff --git a/ui/src/components/TestMigration/index.tsx b/ui/src/components/TestMigration/index.tsx index da6f7f64..f0bb3367 100644 --- a/ui/src/components/TestMigration/index.tsx +++ b/ui/src/components/TestMigration/index.tsx @@ -207,6 +207,7 @@ const TestMigration = () => { value={`${newMigrationData?.test_migration?.stack_api_key}`} version="v2" width="medium" + disabled /> )} diff --git a/ui/src/services/api/migration.service.ts b/ui/src/services/api/migration.service.ts index ea164008..b68b10ea 100644 --- a/ui/src/services/api/migration.service.ts +++ b/ui/src/services/api/migration.service.ts @@ -152,29 +152,30 @@ export const getFieldMapping = async ( } }; -export const getExistingContentTypes = async (projectId: string) => { - try { - return await getCall(`${API_VERSION}/mapper/${projectId}`, options); - } catch (error) { - if (error instanceof Error) { - throw new Error(`${error.message}`); - } else { - throw new Error('Unknown error'); - } - } -}; +// export const getExistingContentTypes = async (projectId: string) => { +// try { +// return await getCall(`${API_VERSION}/mapper/${projectId}`, options); +// return await getCall(`${API_VERSION}/mapper/${projectId}/${contentTypeUid}`, options); +// } catch (error) { +// if (error instanceof Error) { +// throw new Error(`${error.message}`); +// } else { +// throw new Error('Unknown error'); +// } +// } +// }; -export const getExistingGlobalFields = async (projectId: string) => { - try { - return await getCall(`${API_VERSION}/mapper/${projectId}/globalFields`, options); - } catch (error) { - if (error instanceof Error) { - throw new Error(`${error.message}`); - } else { - throw new Error('Unknown error'); - } - } -}; +// export const getSingleGlobalField = async (projectId: string, globalFieldUid: string) => { +// try { +// return await getCall(`${API_VERSION}/mapper/${projectId}/globalFields/${globalFieldUid}`, options); +// } catch (error) { +// if (error instanceof Error) { +// throw new Error(`${error.message}`); +// } else { +// throw new Error('Unknown error'); +// } +// } +// }; export const updateContentType = async ( orgId: string, @@ -218,9 +219,9 @@ export const resetToInitialMapping = async ( } }; -export const fetchExistingContentType = async (projectId: string, contentTypeUid: string) => { +export const getExistingContentTypes = async (projectId: string, contentTypeUid?: string) => { try { - return await getCall(`${API_VERSION}/mapper/${projectId}/${contentTypeUid}`, options); + return await getCall(`${API_VERSION}/mapper/${projectId}/contentTypes/${contentTypeUid ?? ''}`, options); } catch (error) { if (error instanceof Error) { throw new Error(`${error.message}`); @@ -230,9 +231,9 @@ export const fetchExistingContentType = async (projectId: string, contentTypeUid } } -export const fetchGlobalField = async (projectId: string, globalFieldUid: string) => { +export const getExistingGlobalFields = async (projectId: string, globalFieldUid?: string) => { try { - return await getCall(`${API_VERSION}/mapper/${projectId}/globalFields/${globalFieldUid}`, options); + return await getCall(`${API_VERSION}/mapper/${projectId}/globalFields/${globalFieldUid ?? ''}`, options); } catch (error) { if (error instanceof Error) { throw new Error(`${error.message}`); diff --git a/ui/src/services/api/upload.service.ts b/ui/src/services/api/upload.service.ts index 3f447022..b115789d 100644 --- a/ui/src/services/api/upload.service.ts +++ b/ui/src/services/api/upload.service.ts @@ -67,3 +67,15 @@ export const getConfig = async() => { } } }; + +export const getRestrictedKeywords = async () => { + try { + return await getCall(`https://dev18-api.csnonprod.com/v3/restricted_uids`); + } catch (error) { + if (error instanceof Error) { + throw new Error(`${error.message}`); + } else { + throw new Error('Unknown error'); + } + } +}