Skip to content

Commit ad47633

Browse files
Merge pull request #133 from contentstack/feat/DX-3457-improve-error-msgs
improved error messages
2 parents 05c1d7f + 8ddcd1f commit ad47633

File tree

9 files changed

+140
-86
lines changed

9 files changed

+140
-86
lines changed

.talismanrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,8 @@ fileignoreconfig:
2222
checksum: da69dab1717422e12f3b3865604667151d46c96bde5faba13ae862c41d856fba
2323
- filename: tests/unit/generateTS/generateTS.test.ts
2424
checksum: 10e5139168a951a488760626d8305f379db1a7e5df626feb63ef55409ab76a03
25+
- filename: src/constants/messages.ts
26+
checksum: d8e9492f9294725f54711be06cef880993ab91a4ece37cf361c34ac18fc18a7c
27+
- filename: tests/integration/graphqlTS/graphqlTS.test.ts
28+
checksum: b111cb55740d871a3031bc0214eb445239cd6f62ebbdd922eb34af47f0714a54
2529
version: "1.0"

src/constants/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ export const REGIONS = {
1515
GCP_EU: "GCP_EU",
1616
CUSTOM: "CUSTOM",
1717
};
18+
19+
export { ERROR_MESSAGES, INFO_MESSAGES, WARNING_MESSAGES } from "./messages";

src/constants/messages.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Centralized error and info messages for the types-generator
3+
* This file contains all user-facing messages to ensure consistency and easier maintenance
4+
*/
5+
6+
export const ERROR_MESSAGES = {
7+
// Validation Errors
8+
MISSING_REQUIRED_PARAMS: "Missing required parameters",
9+
REQUIRED_PARAMS_LIST: "Required: token, apiKey, environment, region",
10+
UNSUPPORTED_REGION: (region: string) => `Unsupported region: ${region}`,
11+
SUPPORTED_REGIONS: "Supported regions: US, EU, AU, AZURE_NA, AZURE_EU, GCP_NA, GCP_EU",
12+
CUSTOM_HOST_OPTION: "Or provide a custom host",
13+
14+
// Content Type Errors
15+
NO_CONTENT_TYPES: "No Content Types found in the Stack",
16+
CREATE_CONTENT_MODELS: "Please create Content Models to generate type definitions",
17+
NO_CONTENT_TYPES_DETAILED: "There are no Content Types in the Stack, please create Content Models to generate type definitions",
18+
19+
// Authentication Errors
20+
UNAUTHORIZED: "Unauthorized: The apiKey, token or region is not valid.",
21+
INVALID_CREDENTIALS: "Invalid credentials. Please verify your apiKey, token, and region.",
22+
INVALID_CREDENTIALS_GRAPHQL: "Unauthorized: The apiKey, token or environment is not valid.",
23+
24+
// API Errors
25+
API_ERROR_DEFAULT: "Something went wrong",
26+
API_ERROR_WITH_STATUS: (status: number, message?: string) =>
27+
`API error occurred. Status: ${status}${message ? `. ${message}` : ""}`,
28+
GRAPHQL_SCHEMA_ERROR: "An error occurred while processing GraphQL schema",
29+
30+
// Field/Block Skip Messages
31+
SKIPPED_FIELD_UNKNOWN_TYPE: (uid: string, dataType: string, reason: string) =>
32+
`Skipped field "${uid}" with unknown type "${dataType}": ${reason}`,
33+
SKIPPED_GLOBAL_FIELD_REFERENCE: (uid: string, referenceTo: string, reason: string) =>
34+
`Skipped global field reference "${uid}" to "${referenceTo}": ${reason}`,
35+
SKIPPED_FIELD_AT_PATH: (uid: string, path: string, reason: string) =>
36+
`Skipped field "${uid}" at path "${path}": ${reason}`,
37+
SKIPPED_BLOCK_AT_PATH: (uid: string, path: string, reason: string) =>
38+
`Skipped block "${uid}" at path "${path}": ${reason}`,
39+
SKIPPED_GLOBAL_FIELD: (uid: string, reason: string) =>
40+
`Skipped global field "${uid}": ${reason}`,
41+
SKIPPED_GLOBAL_FIELD_NO_SCHEMA: (uid: string, reason: string) =>
42+
`Skipped global field "${uid}": ${reason}. Did you forget to include it?`,
43+
SKIPPED_REFERENCE: (reference: string, reason: string) =>
44+
`Skipped reference to content type "${reference}": ${reason}`,
45+
46+
// Summary Messages
47+
SUMMARY_HEADER: "Summary of Skipped Items:",
48+
TOTAL_SKIPPED_ITEMS: (count: number) => `Total skipped items: ${count}`,
49+
GENERATION_COMPLETED_PARTIAL: "Generation completed successfully with partial schema.",
50+
51+
// GraphQL Errors
52+
GRAPHQL_API_UNAVAILABLE: (region: string) =>
53+
`GraphQL content delivery api unavailable for '${region}' region and no custom host provided`,
54+
} as const;
55+
56+
export const INFO_MESSAGES = {
57+
// Informational messages can be added here
58+
} as const;
59+
60+
export const WARNING_MESSAGES = {
61+
// Warning-specific messages can be added here
62+
} as const;
63+

src/generateTS/factory.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
checkNumericIdentifierExclusion,
1111
throwNumericIdentifierValidationError,
1212
} from "./shared/utils";
13+
import { ERROR_MESSAGES } from "../constants";
1314

1415
export type TSGenOptions = {
1516
docgen: DocumentationGenerator;
@@ -276,7 +277,7 @@ export default function (userOptions: TSGenOptions) {
276277
const reason = `Unknown field type: ${field.data_type}`;
277278
skippedFields.push({ uid: field.uid, path: field.uid, reason });
278279
logger?.warn(
279-
`Skipped field "${field.uid}" with unknown type "${field.data_type}": ${reason}`
280+
ERROR_MESSAGES.SKIPPED_FIELD_UNKNOWN_TYPE(field.uid, field.data_type, reason)
280281
);
281282
type = "Record<string, unknown>"; // Use Record<string, unknown> for balanced type safety
282283
}
@@ -294,7 +295,7 @@ export default function (userOptions: TSGenOptions) {
294295
if (exclusionCheck.shouldExclude) {
295296
skippedFields.push(exclusionCheck.record!);
296297
logger?.warn(
297-
`Skipped global field reference "${field.uid}" to "${field.reference_to}": ${NUMERIC_IDENTIFIER_EXCLUSION_REASON}`
298+
ERROR_MESSAGES.SKIPPED_GLOBAL_FIELD_REFERENCE(field.uid, field.reference_to, NUMERIC_IDENTIFIER_EXCLUSION_REASON)
298299
);
299300
return "string"; // Use string as fallback for global field references
300301
}
@@ -348,7 +349,7 @@ export default function (userOptions: TSGenOptions) {
348349
if (exclusionCheck.shouldExclude) {
349350
skippedFields.push(exclusionCheck.record!);
350351
logger?.warn(
351-
`Skipped field "${field.uid}" at path "${fieldPath}": ${NUMERIC_IDENTIFIER_EXCLUSION_REASON}`
352+
ERROR_MESSAGES.SKIPPED_FIELD_AT_PATH(field.uid, fieldPath, NUMERIC_IDENTIFIER_EXCLUSION_REASON)
352353
);
353354
continue;
354355
}
@@ -411,7 +412,7 @@ export default function (userOptions: TSGenOptions) {
411412
if (exclusionCheck.shouldExclude) {
412413
skippedBlocks.push(exclusionCheck.record!);
413414
logger?.warn(
414-
`Skipped block "${block.uid}" at path "${blockPath}": ${NUMERIC_IDENTIFIER_EXCLUSION_REASON}`
415+
ERROR_MESSAGES.SKIPPED_BLOCK_AT_PATH(block.uid, blockPath, NUMERIC_IDENTIFIER_EXCLUSION_REASON)
415416
);
416417
return null; // Return null to filter out later
417418
}
@@ -513,7 +514,7 @@ export default function (userOptions: TSGenOptions) {
513514
if (exclusionCheck.shouldExclude) {
514515
skippedFields.push(exclusionCheck.record!);
515516
logger?.warn(
516-
`Skipped global field "${field.uid}": ${NUMERIC_IDENTIFIER_EXCLUSION_REASON}`
517+
ERROR_MESSAGES.SKIPPED_GLOBAL_FIELD(field.uid, NUMERIC_IDENTIFIER_EXCLUSION_REASON)
517518
);
518519
return "string"; // Use string as fallback for global fields
519520
}
@@ -522,7 +523,7 @@ export default function (userOptions: TSGenOptions) {
522523
const reason = "Schema not found for global field";
523524
skippedFields.push({ uid: field.uid, path: field.uid, reason });
524525
logger?.warn(
525-
`Skipped global field "${field.uid}": ${reason}. Did you forget to include it?`
526+
ERROR_MESSAGES.SKIPPED_GLOBAL_FIELD_NO_SCHEMA(field.uid, reason)
526527
);
527528
return "string"; // Use string as fallback
528529
}
@@ -559,7 +560,7 @@ export default function (userOptions: TSGenOptions) {
559560
references.push(name_type(v));
560561
} else {
561562
logger?.warn(
562-
`Skipped reference to content type "${v}": ${NUMERIC_IDENTIFIER_EXCLUSION_REASON}`
563+
ERROR_MESSAGES.SKIPPED_REFERENCE(v, NUMERIC_IDENTIFIER_EXCLUSION_REASON)
563564
);
564565
}
565566
});
@@ -569,7 +570,7 @@ export default function (userOptions: TSGenOptions) {
569570
references.push(name_type(field.reference_to));
570571
} else {
571572
logger?.warn(
572-
`Skipped reference to content type "${field.reference_to}": ${NUMERIC_IDENTIFIER_EXCLUSION_REASON}`
573+
ERROR_MESSAGES.SKIPPED_REFERENCE(field.reference_to, NUMERIC_IDENTIFIER_EXCLUSION_REASON)
573574
);
574575
}
575576
}
@@ -603,7 +604,7 @@ export default function (userOptions: TSGenOptions) {
603604
// Log summary table of skipped fields and blocks
604605
if (logger && (skippedFields.length > 0 || skippedBlocks.length > 0)) {
605606
logger.info("");
606-
logger.info("Summary of Skipped Items:");
607+
logger.info(ERROR_MESSAGES.SUMMARY_HEADER);
607608

608609
// Create combined table data for all skipped items
609610
const allSkippedItems = [
@@ -636,8 +637,8 @@ export default function (userOptions: TSGenOptions) {
636637

637638
const totalSkipped = skippedFields.length + skippedBlocks.length;
638639
logger.info("");
639-
logger.warn(`Total skipped items: ${totalSkipped}`);
640-
logger.success(" Generation completed successfully with partial schema.");
640+
logger.warn(ERROR_MESSAGES.TOTAL_SKIPPED_ITEMS(totalSkipped));
641+
logger.success(ERROR_MESSAGES.GENERATION_COMPLETED_PARTIAL);
641642
}
642643

643644
return {

src/generateTS/index.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import async from "async";
22
import { flatMap, flatten } from "lodash";
3-
import { TOKEN_TYPE } from "../constants";
3+
import { TOKEN_TYPE, ERROR_MESSAGES } from "../constants";
44
import { initializeContentstackSdk } from "../sdk/utils";
55
import { GenerateTS, GenerateTSFromContentTypes } from "../types";
66
import { DocumentationGenerator } from "./docgen/doc";
@@ -55,13 +55,9 @@ export const generateTS = async ({
5555
const { content_types }: any = contentTypes;
5656

5757
if (!content_types.length) {
58-
logger.error("No Content Types found in the Stack");
59-
logger.warn(
60-
"Please create Content Models to generate type definitions"
61-
);
62-
throw createValidationError(
63-
"There are no Content Types in the Stack, please create Content Models to generate type definitions"
64-
);
58+
logger.error(ERROR_MESSAGES.NO_CONTENT_TYPES);
59+
logger.warn(ERROR_MESSAGES.CREATE_CONTENT_MODELS);
60+
throw createValidationError(ERROR_MESSAGES.NO_CONTENT_TYPES_DETAILED);
6561
}
6662

6763
let schemas: ContentType[] = [];
@@ -97,23 +93,21 @@ export const generateTS = async ({
9793
};
9894
} else {
9995
const errorObj = JSON.parse(error?.message?.replace("Error: ", ""));
100-
let errorMessage = "Something went wrong";
96+
let errorMessage: string = ERROR_MESSAGES.API_ERROR_DEFAULT;
10197
let errorCode = "API_ERROR";
10298

10399
if (errorObj.status) {
104100
switch (errorObj.status) {
105101
case 401:
106-
errorMessage =
107-
"Unauthorized: The apiKey, token or region is not valid.";
102+
errorMessage = ERROR_MESSAGES.UNAUTHORIZED;
108103
errorCode = "AUTHENTICATION_FAILED";
109104
break;
110105
case 412:
111-
errorMessage =
112-
"Invalid Credentials: Please check the provided apiKey, token and region.";
106+
errorMessage = ERROR_MESSAGES.INVALID_CREDENTIALS;
113107
errorCode = "INVALID_CREDENTIALS";
114108
break;
115109
default:
116-
errorMessage = `${errorMessage}, ${errorObj.error_message}`;
110+
errorMessage = ERROR_MESSAGES.API_ERROR_WITH_STATUS(errorObj.status, errorObj.error_message);
117111
errorCode = `API_ERROR_${errorObj.status}`;
118112
}
119113
}

src/graphqlTS/index.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { GraphQLBase } from "../types";
33
import { introspectionQuery } from "./queries";
44
import axios from "axios";
55
import { createLogger } from "../logger";
6+
import { ERROR_MESSAGES } from "../constants";
67

78
type RegionUrlMap = {
89
[prop: string]: string;
@@ -31,8 +32,8 @@ export async function graphqlTS({
3132
const logger = createLogger(loggerInstance);
3233
try {
3334
if (!token || !apiKey || !environment || !region) {
34-
logger.error("Missing required parameters");
35-
logger.warn("Required: token, apiKey, environment, region");
35+
logger.error(ERROR_MESSAGES.MISSING_REQUIRED_PARAMS);
36+
logger.warn(ERROR_MESSAGES.REQUIRED_PARAMS_LIST);
3637
throw {
3738
type: "validation",
3839
error_message:
@@ -61,14 +62,12 @@ export async function graphqlTS({
6162
}
6263

6364
if (!GRAPHQL_REGION_URL_MAPPING[region] && !host) {
64-
logger.error(`Unsupported region: ${region}`);
65-
logger.warn(
66-
"Supported regions: US, EU, AU, AZURE_NA, AZURE_EU, GCP_NA, GCP_EU"
67-
);
68-
logger.warn("Or provide a custom host");
65+
logger.error(ERROR_MESSAGES.UNSUPPORTED_REGION(region));
66+
logger.warn(ERROR_MESSAGES.SUPPORTED_REGIONS);
67+
logger.warn(ERROR_MESSAGES.CUSTOM_HOST_OPTION);
6968
throw {
7069
type: "validation",
71-
error_message: `GraphQL content delivery api unavailable for '${region}' region and no custom host provided`,
70+
error_message: ERROR_MESSAGES.GRAPHQL_API_UNAVAILABLE(region),
7271
};
7372
}
7473

@@ -92,8 +91,7 @@ export async function graphqlTS({
9291

9392
if (error.response?.status === 412) {
9493
throw {
95-
error_message:
96-
"Unauthorized: The apiKey, token or environment is not valid.",
94+
error_message: ERROR_MESSAGES.INVALID_CREDENTIALS_GRAPHQL,
9795
};
9896
} else {
9997
let details = "";
@@ -120,9 +118,7 @@ export async function graphqlTS({
120118

121119
throw {
122120
error_message:
123-
details ||
124-
errorMessage ||
125-
"An error occurred while processing GraphQL schema",
121+
details || errorMessage || ERROR_MESSAGES.GRAPHQL_SCHEMA_ERROR,
126122
};
127123
}
128124
}

0 commit comments

Comments
 (0)