Skip to content

Commit 6f91252

Browse files
authored
Merge pull request #2019 from contentstack/fix/logger-undefined-issue
fix: cannot read properties of undefined issue
2 parents bca057b + e8cc786 commit 6f91252

File tree

6 files changed

+151
-75
lines changed

6 files changed

+151
-75
lines changed

packages/contentstack-export/src/export/module-exporter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,6 @@ class ModuleExporter {
103103
exportModules.push('stack');
104104
}
105105

106-
exportModules.push(moduleName);
107-
108106
if (!this.exportConfig.skipDependencies) {
109107
const {
110108
modules: { [moduleName]: { dependencies = [] } = {} },
@@ -115,6 +113,8 @@ class ModuleExporter {
115113
}
116114
}
117115

116+
exportModules.push(moduleName);
117+
118118
for (const moduleName of exportModules) {
119119
await this.exportByModuleByName(moduleName);
120120
}

packages/contentstack-export/src/export/modules/content-types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export default class ContentTypesExport extends BaseClass {
105105
}
106106

107107
sanitizeAttribs(contentTypes: Record<string, unknown>[]): Record<string, unknown>[] {
108-
log.debug(`Sanitizing ${contentTypes.length} content types`, this.exportConfig.context);
108+
log.debug(`Sanitizing ${contentTypes?.length} content types`, this.exportConfig.context);
109109

110110
const updatedContentTypes: Record<string, unknown>[] = [];
111111

@@ -121,7 +121,7 @@ export default class ContentTypesExport extends BaseClass {
121121
}
122122

123123
async writeContentTypes(contentTypes: Record<string, unknown>[]) {
124-
log.debug(`Writing ${contentTypes.length} content types to disk`, this.exportConfig.context);
124+
log.debug(`Writing ${contentTypes?.length} content types to disk`, this.exportConfig.context);
125125

126126
function write(contentType: Record<string, unknown>) {
127127
return fsUtil.writeFile(

packages/contentstack-export/src/export/modules/custom-roles.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export default class ExportCustomRoles extends BaseClass {
4848
await this.getLocales();
4949
await this.getCustomRolesLocales();
5050

51-
log.debug(`Custom roles export completed. Total custom roles: ${Object.keys(this.customRoles).length}`, this.exportConfig.context);
51+
log.debug(`Custom roles export completed. Total custom roles: ${Object.keys(this.customRoles)?.length}`, this.exportConfig.context);
5252
}
5353

5454
async getCustomRoles(): Promise<void> {
@@ -75,8 +75,8 @@ export default class ExportCustomRoles extends BaseClass {
7575
}
7676

7777
customRoles.forEach((role: any) => {
78-
log.debug(`Processing custom role: ${role.name} (${role.uid})`, this.exportConfig.context);
79-
log.info(messageHandler.parse('ROLES_EXPORTING_ROLE', role.name), this.exportConfig.context);
78+
log.debug(`Processing custom role: ${role?.name} (${role?.uid})`, this.exportConfig.context);
79+
log.info(messageHandler.parse('ROLES_EXPORTING_ROLE', role?.name), this.exportConfig.context);
8080
this.customRoles[role.uid] = role;
8181
});
8282

@@ -93,7 +93,7 @@ export default class ExportCustomRoles extends BaseClass {
9393
.query({})
9494
.find()
9595
.then((data: any) => {
96-
log.debug(`Fetched ${data.items?.length || 0} locales`, this.exportConfig.context);
96+
log.debug(`Fetched ${data?.items?.length || 0} locales`, this.exportConfig.context);
9797
return data;
9898
})
9999
.catch((err: any) => {
@@ -102,23 +102,23 @@ export default class ExportCustomRoles extends BaseClass {
102102
});
103103

104104
for (const locale of locales.items) {
105-
log.debug(`Mapping locale: ${locale.name} (${locale.uid})`, this.exportConfig.context);
105+
log.debug(`Mapping locale: ${locale?.name} (${locale?.uid})`, this.exportConfig.context);
106106
this.sourceLocalesMap[locale.uid] = locale;
107107
}
108108

109-
log.debug(`Mapped ${Object.keys(this.sourceLocalesMap).length} locales`, this.exportConfig.context);
109+
log.debug(`Mapped ${Object.keys(this.sourceLocalesMap)?.length} locales`, this.exportConfig.context);
110110
}
111111

112112
async getCustomRolesLocales() {
113113
log.debug('Processing custom roles locales mapping...', this.exportConfig.context);
114114

115115
for (const role of values(this.customRoles)) {
116116
const customRole = role as Record<string, any>;
117-
log.debug(`Processing locales for custom role: ${customRole.name}`, this.exportConfig.context);
117+
log.debug(`Processing locales for custom role: ${customRole?.name}`, this.exportConfig.context);
118118

119119
const rulesLocales = find(customRole.rules, (rule: any) => rule.module === 'locale');
120120
if (rulesLocales?.locales?.length) {
121-
log.debug(`Found ${rulesLocales.locales.length} locales for role: ${customRole.name}`, this.exportConfig.context);
121+
log.debug(`Found ${rulesLocales.locales.length} locales for role: ${customRole?.name}`, this.exportConfig.context);
122122
forEach(rulesLocales.locales, (locale: any) => {
123123
log.debug(`Adding locale ${locale} to custom roles mapping`, this.exportConfig.context);
124124
this.localesMap[locale] = 1;
@@ -127,7 +127,7 @@ export default class ExportCustomRoles extends BaseClass {
127127
}
128128

129129
if (keys(this.localesMap)?.length) {
130-
log.debug(`Processing ${keys(this.localesMap).length} custom role locales`, this.exportConfig.context);
130+
log.debug(`Processing ${keys(this.localesMap)?.length} custom role locales`, this.exportConfig.context);
131131

132132
for (const locale in this.localesMap) {
133133
if (this.sourceLocalesMap[locale] !== undefined) {

packages/contentstack-export/src/export/modules/entries.ts

Lines changed: 62 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
import * as path from 'path';
2-
import {
3-
ContentstackClient,
4-
FsUtility,
5-
handleAndLogError,
6-
messageHandler,
7-
log,
8-
} from '@contentstack/cli-utilities';
2+
import { ContentstackClient, FsUtility, handleAndLogError, messageHandler, log } from '@contentstack/cli-utilities';
93
import { Export, ExportProjects } from '@contentstack/cli-variants';
104
import { sanitizePath } from '@contentstack/cli-utilities';
115

@@ -65,15 +59,18 @@ export default class EntriesExport extends BaseClass {
6559
try {
6660
log.debug('Starting entries export process...', this.exportConfig.context);
6761
const locales = fsUtil.readFile(this.localesFilePath) as Array<Record<string, unknown>>;
68-
log.debug(`Loaded ${locales.length} locales from ${this.localesFilePath}`, this.exportConfig.context);
69-
62+
if (!Array.isArray(locales) || locales?.length === 0) {
63+
log.debug(`No locales found in ${this.localesFilePath}`, this.exportConfig.context);
64+
} else {
65+
log.debug(`Loaded ${locales?.length} locales from ${this.localesFilePath}`, this.exportConfig.context);
66+
}
67+
7068
const contentTypes = fsUtil.readFile(this.schemaFilePath) as Array<Record<string, unknown>>;
71-
log.debug(`Loaded ${contentTypes.length} content types from ${this.schemaFilePath}`, this.exportConfig.context);
72-
73-
if (contentTypes.length === 0) {
69+
if (contentTypes?.length === 0) {
7470
log.info(messageHandler.parse('CONTENT_TYPE_NO_TYPES'), this.exportConfig.context);
7571
return;
7672
}
73+
log.debug(`Loaded ${contentTypes?.length} content types from ${this.schemaFilePath}`, this.exportConfig.context);
7774

7875
// NOTE Check if variant is enabled in specific stack
7976
if (this.exportConfig.personalizationEnabled) {
@@ -95,18 +92,20 @@ export default class EntriesExport extends BaseClass {
9592
}
9693

9794
const entryRequestOptions = this.createRequestObjects(locales, contentTypes);
98-
log.debug(`Created ${entryRequestOptions.length} entry request objects for processing`, this.exportConfig.context);
99-
95+
log.debug(
96+
`Created ${entryRequestOptions.length} entry request objects for processing`,
97+
this.exportConfig.context,
98+
);
99+
100100
for (let entryRequestOption of entryRequestOptions) {
101-
log.debug(`Processing entries for content type: ${entryRequestOption.contentType}, locale: ${entryRequestOption.locale}`, this.exportConfig.context);
101+
log.debug(
102+
`Processing entries for content type: ${entryRequestOption.contentType}, locale: ${entryRequestOption.locale}`,
103+
this.exportConfig.context,
104+
);
102105
await this.getEntries(entryRequestOption);
103106
this.entriesFileHelper?.completeFile(true);
104107
log.success(
105-
messageHandler.parse(
106-
'ENTRIES_EXPORT_COMPLETE',
107-
entryRequestOption.contentType,
108-
entryRequestOption.locale,
109-
),
108+
messageHandler.parse('ENTRIES_EXPORT_COMPLETE', entryRequestOption.contentType, entryRequestOption.locale),
110109
this.exportConfig.context,
111110
);
112111
}
@@ -120,8 +119,18 @@ export default class EntriesExport extends BaseClass {
120119
locales: Array<Record<string, unknown>>,
121120
contentTypes: Array<Record<string, unknown>>,
122121
): Array<Record<string, any>> {
123-
log.debug(`Creating request objects for ${contentTypes.length} content types and ${locales.length} locales`, this.exportConfig.context);
124-
122+
if (!Array.isArray(locales) || locales?.length === 0) {
123+
log.debug('No locales found, using master locale only', this.exportConfig.context);
124+
} else {
125+
log.debug(`Found ${locales.length} locales for export`, this.exportConfig.context);
126+
}
127+
if (!Array.isArray(contentTypes) || contentTypes.length === 0) {
128+
log.debug('No content types found, skipping entries export', this.exportConfig.context);
129+
return [];
130+
} else {
131+
log.debug(`Found ${contentTypes.length} content types for export`, this.exportConfig.context);
132+
}
133+
125134
let requestObjects: Array<Record<string, any>> = [];
126135
contentTypes.forEach((contentType) => {
127136
if (Object.keys(locales).length !== 0) {
@@ -165,17 +174,21 @@ export default class EntriesExport extends BaseClass {
165174
.entry()
166175
.query(requestObject)
167176
.find();
168-
169-
log.debug(`Fetched ${entriesSearchResponse.items?.length || 0} entries out of total ${entriesSearchResponse.count}`, this.exportConfig.context);
177+
178+
log.debug(
179+
`Fetched ${entriesSearchResponse.items?.length || 0} entries out of total ${entriesSearchResponse.count}`,
180+
this.exportConfig.context,
181+
);
170182
} catch (error) {
171183
handleAndLogError(error, {
172184
...this.exportConfig.context,
173185
contentType: options.contentType,
174186
locale: options.locale,
175187
});
188+
throw error;
176189
}
177190

178-
if (Array.isArray(entriesSearchResponse.items) && entriesSearchResponse.items.length > 0) {
191+
if (Array.isArray(entriesSearchResponse?.items) && entriesSearchResponse?.items?.length > 0) {
179192
if (options.skip === 0) {
180193
const entryBasePath = path.join(
181194
sanitizePath(this.entriesDirPath),
@@ -194,10 +207,10 @@ export default class EntriesExport extends BaseClass {
194207
});
195208
log.debug('Initialized FsUtility for writing entries', this.exportConfig.context);
196209
}
197-
210+
198211
log.debug(`Writing ${entriesSearchResponse.items.length} entries to file`, this.exportConfig.context);
199212
this.entriesFileHelper.writeIntoFile(entriesSearchResponse.items, { mapKeyVal: true });
200-
213+
201214
if (this.entriesConfig.exportVersions) {
202215
log.debug('Exporting entry versions is enabled', this.exportConfig.context);
203216
let versionedEntryPath = path.join(
@@ -227,7 +240,10 @@ export default class EntriesExport extends BaseClass {
227240

228241
options.skip += this.entriesConfig.limit || 100;
229242
if (options.skip >= entriesSearchResponse.count) {
230-
log.debug(`Completed fetching all entries for content type: ${options.contentType}, locale: ${options.locale}`, this.exportConfig.context);
243+
log.debug(
244+
`Completed fetching all entries for content type: ${options.contentType}, locale: ${options.locale}`,
245+
this.exportConfig.context,
246+
);
231247
return Promise.resolve(true);
232248
}
233249
log.debug(`Continuing to fetch entries with skip: ${options.skip}`, this.exportConfig.context);
@@ -240,7 +256,7 @@ export default class EntriesExport extends BaseClass {
240256
options: { locale: string; contentType: string; versionedEntryPath: string },
241257
): Promise<void> {
242258
log.debug(`Fetching versions for ${entries.length} entries`, this.exportConfig.context);
243-
259+
244260
const onSuccess = ({ response, apiData: entry }: any) => {
245261
const versionFilePath = path.join(sanitizePath(options.versionedEntryPath), sanitizePath(`${entry.uid}.json`));
246262
log.debug(`Writing versioned entry to: ${versionFilePath}`, this.exportConfig.context);
@@ -262,7 +278,10 @@ export default class EntriesExport extends BaseClass {
262278
);
263279
};
264280

265-
log.debug(`Starting concurrent calls for versioned entries with batch limit: ${this.entriesConfig.batchLimit}`, this.exportConfig.context);
281+
log.debug(
282+
`Starting concurrent calls for versioned entries with batch limit: ${this.entriesConfig.batchLimit}`,
283+
this.exportConfig.context,
284+
);
266285
return await this.makeConcurrentCall(
267286
{
268287
apiBatches: [entries],
@@ -289,7 +308,7 @@ export default class EntriesExport extends BaseClass {
289308
isLastRequest: boolean;
290309
}) {
291310
log.debug(`Processing versioned entry: ${entry.uid}`, this.exportConfig.context);
292-
311+
293312
return new Promise(async (resolve, reject) => {
294313
return await this.getEntryByVersion(apiParams.queryParam, entry)
295314
.then((response) => {
@@ -323,21 +342,27 @@ export default class EntriesExport extends BaseClass {
323342
},
324343
version: entry._version,
325344
};
326-
345+
327346
log.debug(`Fetching entry version ${entry._version} for uid: ${entry.uid}`, this.exportConfig.context);
328-
347+
329348
const entryResponse = await this.stackAPIClient
330349
.contentType(options.contentType)
331350
.entry(entry.uid)
332351
.fetch(queryRequestObject);
333352
entries.push(entryResponse);
334-
353+
335354
if (--entry._version > 0) {
336-
log.debug(`Continuing to fetch previous version ${entry._version} for entry: ${entry.uid}`, this.exportConfig.context);
355+
log.debug(
356+
`Continuing to fetch previous version ${entry._version} for entry: ${entry.uid}`,
357+
this.exportConfig.context,
358+
);
337359
return await this.getEntryByVersion(options, entry, entries);
338360
}
339-
340-
log.debug(`Completed fetching all versions for entry: ${entry.uid}, total versions: ${entries.length}`, this.exportConfig.context);
361+
362+
log.debug(
363+
`Completed fetching all versions for entry: ${entry.uid}, total versions: ${entries.length}`,
364+
this.exportConfig.context,
365+
);
341366
return entries;
342367
}
343368
}

packages/contentstack-utilities/src/helpers.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { checkSync } from 'recheck';
22
import traverse from 'traverse';
33
import authHandler from './auth-handler';
44
import { HttpClient, cliux, configHandler } from '.';
5+
56
export const isAuthenticated = () => authHandler.isAuthenticated();
67
export const doesBranchExist = async (stack, branchName) => {
78
return stack
@@ -92,9 +93,38 @@ export const formatError = function (error: any) {
9293
parsedError = error;
9394
}
9495

95-
// Check if parsedError is an empty object
9696
if (parsedError && typeof parsedError === 'object' && Object.keys(parsedError).length === 0) {
97-
return `An unknown error occurred. ${error}`;
97+
if (
98+
!parsedError.message &&
99+
!parsedError.code &&
100+
!parsedError.status &&
101+
!parsedError.errorMessage &&
102+
!parsedError.error_message
103+
) {
104+
return `An unknown error occurred. ${error}`;
105+
}
106+
}
107+
108+
if (parsedError?.response?.data?.errorMessage) {
109+
return parsedError.response.data.errorMessage;
110+
}
111+
112+
if (parsedError?.errorMessage) {
113+
return parsedError.errorMessage;
114+
}
115+
116+
const status = parsedError?.status || parsedError?.response?.status;
117+
const errorCode = parsedError?.errorCode || parsedError?.response?.data?.errorCode;
118+
if (status === 422 && errorCode === 104) {
119+
return 'Invalid email or password. Please check your credentials and try again.';
120+
}
121+
122+
if (status === 401) {
123+
return 'Authentication failed. Please check your credentials.';
124+
}
125+
126+
if (status === 403) {
127+
return 'Access denied. Please check your permissions.';
98128
}
99129

100130
// Check for specific SSL error
@@ -107,6 +137,11 @@ export const formatError = function (error: any) {
107137
return 'Self-signed certificate in the certificate chain! Please ensure your certificate configuration is correct and the necessary CA certificates are trusted.';
108138
}
109139

140+
// ENHANCED: Handle network errors with user-friendly messages
141+
if (['ECONNREFUSED', 'ENOTFOUND', 'ETIMEDOUT', 'ENETUNREACH'].includes(parsedError?.code)) {
142+
return `Connection failed: Unable to reach the server. Please check your internet connection.`;
143+
}
144+
110145
// Determine the error message
111146
let message =
112147
parsedError.errorMessage || parsedError.error_message || parsedError?.code || parsedError.message || parsedError;

0 commit comments

Comments
 (0)