Skip to content

Commit 368209f

Browse files
Merge pull request #269 from contentstack/fix/DX-3744-improve-error-msgs
Improve error messages
2 parents fbc9b65 + 870666a commit 368209f

11 files changed

Lines changed: 134 additions & 83 deletions

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ export type { ImageTransform } from './lib/image-transform';
1111
export type { AssetQuery } from './lib/asset-query';
1212
export type { TaxonomyQuery } from './lib/taxonomy-query';
1313
export type { ContentTypeQuery } from './lib/contenttype-query';
14+
export { ErrorMessages, ErrorCode } from './lib/error-messages';
1415

1516
export default contentstack;

src/lib/entries.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Query } from './query';
33
import { BaseQuery } from './base-query';
44
import { FindResponse } from './types';
55
import { encodeQueryParams } from './utils';
6+
import { ErrorMessages } from './error-messages';
67

78
export class Entries extends BaseQuery {
89
private _contentTypeUid: string;
@@ -152,7 +153,7 @@ export class Entries extends BaseQuery {
152153
(this._queryParams['include[]'] as string[]).push(...(Array.isArray(value) ? value : [value]));
153154
});
154155
} else {
155-
console.error("Argument should be a String or an Array.");
156+
console.error(ErrorMessages.INVALID_ARGUMENT_STRING_OR_ARRAY);
156157
}
157158
return this;
158159
}

src/lib/entry.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { AxiosInstance, getData } from '@contentstack/core';
2+
import { ErrorMessages } from './error-messages';
23

34
interface EntryResponse<T> {
45
entry: T;
@@ -114,7 +115,7 @@ export class Entry {
114115
(this._queryParams['include[]'] as string[]).push(...(Array.isArray(value) ? value : [value]));
115116
});
116117
} else {
117-
console.error("Argument should be a String or an Array.");
118+
console.error(ErrorMessages.INVALID_ARGUMENT_STRING_OR_ARRAY);
118119
}
119120
return this;
120121
}

src/lib/error-messages.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Centralized error messages for the Contentstack SDK
3+
* This file contains all user-facing error messages used throughout the SDK
4+
*/
5+
6+
export const ErrorMessages = {
7+
// Field/Key validation errors
8+
INVALID_FIELD_UID: 'Invalid fieldUid. Provide an alphanumeric field UID and try again.',
9+
INVALID_KEY: 'Invalid key. Provide an alphanumeric key and try again.',
10+
INVALID_REFERENCE_UID: (uid: string) => `Invalid referenceUid: ${uid}. Must be alphanumeric.`,
11+
12+
// Value validation errors
13+
INVALID_VALUE_STRING_OR_NUMBER: 'Invalid value. Provide a string or number and try again.',
14+
INVALID_VALUE_ARRAY: 'Invalid value. Provide an array of strings, numbers, or booleans and try again.',
15+
INVALID_ARGUMENT_STRING_OR_ARRAY: 'Invalid argument. Provide a string or an array and try again.',
16+
17+
// Storage errors
18+
MISSING_CUSTOM_STORAGE: 'Missing storage for customStorage. Provide a storage object with get, set, and remove methods and try again.',
19+
20+
// Regex validation errors
21+
INVALID_REGEX_PATTERN: 'Invalid regexPattern: Must be a valid regular expression',
22+
23+
// Slack/Integration errors (for future use if sanity-report-ts-sdk.js is added)
24+
SLACK_MESSAGE_FAILED: (details?: string) =>
25+
`Failed to send Slack message. Check the SLACK_WEBHOOK_URL and SLACK_CHANNEL configuration and try again.${details ? ` Details: ${details}` : ''}`,
26+
SLACK_FAILURE_DETAILS_FAILED: (details?: string) =>
27+
`Failed to send failure details to Slack. Verify SLACK_WEBHOOK_URL, SLACK_CHANNEL, and SLACK_TOKEN settings and try again.${details ? ` Details: ${details}` : ''}`,
28+
} as const;
29+
30+
/**
31+
* Error codes for programmatic error handling
32+
*/
33+
export enum ErrorCode {
34+
INVALID_FIELD_UID = 'INVALID_FIELD_UID',
35+
INVALID_KEY = 'INVALID_KEY',
36+
INVALID_REFERENCE_UID = 'INVALID_REFERENCE_UID',
37+
INVALID_VALUE = 'INVALID_VALUE',
38+
INVALID_ARGUMENT = 'INVALID_ARGUMENT',
39+
MISSING_STORAGE = 'MISSING_STORAGE',
40+
INVALID_REGEX = 'INVALID_REGEX',
41+
SLACK_ERROR = 'SLACK_ERROR',
42+
}
43+

src/lib/query.ts

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { AxiosInstance, getData } from '@contentstack/core';
22
import { BaseQuery } from './base-query';
33
import { BaseQueryParameters, QueryOperation, QueryOperator, TaxonomyQueryOperation, params, queryParams, FindResponse } from './types';
44
import { encodeQueryParams } from './utils';
5+
import { ErrorMessages } from './error-messages';
56

67
export class Query extends BaseQuery {
78
private _contentTypeUid?: string;
@@ -73,7 +74,7 @@ export class Query extends BaseQuery {
7374
additionalData?: object
7475
): Query {
7576
if (!this.isValidAlphanumeric(fieldUid)) {
76-
console.error("Invalid fieldUid:", fieldUid);
77+
console.error(ErrorMessages.INVALID_FIELD_UID);
7778
return this;
7879
}
7980
if (queryOperation == QueryOperation.EQUALS) {
@@ -102,11 +103,11 @@ export class Query extends BaseQuery {
102103
*/
103104
regex(fieldUid: string, regexPattern: string, options?: string): Query {
104105
if (!this.isValidAlphanumeric(fieldUid)) {
105-
console.error("Invalid fieldUid:", fieldUid);
106+
console.error(ErrorMessages.INVALID_FIELD_UID);
106107
return this;
107108
}
108109
if (!this.isValidRegexPattern(regexPattern)) {
109-
throw new Error("Invalid regexPattern: Must be a valid regular expression");
110+
throw new Error(ErrorMessages.INVALID_REGEX_PATTERN);
110111
}
111112
else {
112113
this._parameters[fieldUid] = { $regex: regexPattern };
@@ -137,7 +138,7 @@ export class Query extends BaseQuery {
137138
whereIn(referenceUid: string, queryInstance: Query): Query {
138139
// eslint-disable-next-line @typescript-eslint/naming-convention, prettier/prettier
139140
if (!this.isValidAlphanumeric(referenceUid)) {
140-
throw new Error("Invalid referenceUid: Must be alphanumeric.");
141+
throw new Error(ErrorMessages.INVALID_REFERENCE_UID(referenceUid));
141142
}
142143
this._parameters[referenceUid] = { '$in_query': queryInstance._parameters };
143144
return this;
@@ -165,7 +166,7 @@ export class Query extends BaseQuery {
165166
whereNotIn(referenceUid: string, queryInstance: Query): Query {
166167
// eslint-disable-next-line @typescript-eslint/naming-convention, prettier/prettier
167168
if (!this.isValidAlphanumeric(referenceUid)) {
168-
throw new Error("Invalid referenceUid: Must be alphanumeric.");
169+
throw new Error(ErrorMessages.INVALID_REFERENCE_UID(referenceUid));
169170
}
170171
this._parameters[referenceUid] = { '$nin_query': queryInstance._parameters };
171172
return this;
@@ -237,11 +238,11 @@ export class Query extends BaseQuery {
237238
*/
238239
containedIn(key: string, value: (string | number | boolean)[]): Query {
239240
if (!this.isValidAlphanumeric(key)) {
240-
console.error("Invalid key:", key);
241+
console.error(ErrorMessages.INVALID_KEY);
241242
return this;
242243
}
243244
if (!this.isValidValue(value)) {
244-
console.error("Invalid value:", value);
245+
console.error(ErrorMessages.INVALID_VALUE_ARRAY);
245246
return this;
246247
}
247248
this._parameters[key] = { '$in': value };
@@ -265,11 +266,11 @@ export class Query extends BaseQuery {
265266
*/
266267
notContainedIn(key: string, value: (string | number | boolean)[]): Query {
267268
if (!this.isValidAlphanumeric(key)) {
268-
console.error("Invalid key:", key);
269+
console.error(ErrorMessages.INVALID_KEY);
269270
return this;
270271
}
271272
if (!this.isValidValue(value)) {
272-
console.error("Invalid value:", value);
273+
console.error(ErrorMessages.INVALID_VALUE_ARRAY);
273274
return this;
274275
}
275276
this._parameters[key] = { '$nin': value };
@@ -292,7 +293,7 @@ export class Query extends BaseQuery {
292293
*/
293294
exists(key: string): Query {
294295
if (!this.isValidAlphanumeric(key)) {
295-
console.error("Invalid key:", key);
296+
console.error(ErrorMessages.INVALID_KEY);
296297
return this;
297298
}
298299
this._parameters[key] = { '$exists': true };
@@ -315,7 +316,7 @@ export class Query extends BaseQuery {
315316
*/
316317
notExists(key: string): Query {
317318
if (!this.isValidAlphanumeric(key)) {
318-
console.error("Invalid key:", key);
319+
console.error(ErrorMessages.INVALID_KEY);
319320
return this;
320321
}
321322
this._parameters[key] = { '$exists': false };
@@ -386,11 +387,11 @@ export class Query extends BaseQuery {
386387
*/
387388
equalTo(key: string, value: string | number | boolean): Query {
388389
if (!this.isValidAlphanumeric(key)) {
389-
console.error("Invalid key:", key);
390+
console.error(ErrorMessages.INVALID_KEY);
390391
return this;
391392
}
392393
if (typeof value !== 'string' && typeof value !== 'number') {
393-
console.error("Invalid value (expected string or number):", value);
394+
console.error(ErrorMessages.INVALID_VALUE_STRING_OR_NUMBER);
394395
return this;
395396
}
396397
this._parameters[key] = value;
@@ -413,11 +414,11 @@ export class Query extends BaseQuery {
413414
*/
414415
notEqualTo(key: string, value: string | number | boolean): Query {
415416
if (!this.isValidAlphanumeric(key)) {
416-
console.error("Invalid key:", key);
417+
console.error(ErrorMessages.INVALID_KEY);
417418
return this;
418419
}
419420
if (typeof value !== 'string' && typeof value !== 'number') {
420-
console.error("Invalid value (expected string or number):", value);
421+
console.error(ErrorMessages.INVALID_VALUE_STRING_OR_NUMBER);
421422
return this;
422423
}
423424
this._parameters[key] = { '$ne': value };
@@ -441,7 +442,7 @@ export class Query extends BaseQuery {
441442
*/
442443
referenceIn(key: string, query: Query): Query {
443444
if (!this.isValidAlphanumeric(key)) {
444-
console.error("Invalid key:", key);
445+
console.error(ErrorMessages.INVALID_KEY);
445446
return this;
446447
}
447448
this._parameters[key] = { '$in_query': query._parameters }
@@ -465,7 +466,7 @@ export class Query extends BaseQuery {
465466
*/
466467
referenceNotIn(key: string, query: Query): Query {
467468
if (!this.isValidAlphanumeric(key)) {
468-
console.error("Invalid key:", key);
469+
console.error(ErrorMessages.INVALID_KEY);
469470
return this;
470471
}
471472
this._parameters[key] = { '$nin_query': query._parameters }
@@ -487,7 +488,7 @@ export class Query extends BaseQuery {
487488
*/
488489
tags(values: (string | number | boolean)[]): Query {
489490
if (!this.isValidValue(values)) {
490-
console.error("Invalid value:", values);
491+
console.error(ErrorMessages.INVALID_VALUE_ARRAY);
491492
return this;
492493
}
493494
this._parameters['tags'] = values;
@@ -509,7 +510,7 @@ export class Query extends BaseQuery {
509510
*/
510511
search(key: string): Query {
511512
if (!this.isValidAlphanumeric(key)) {
512-
console.error("Invalid key:", key);
513+
console.error(ErrorMessages.INVALID_KEY);
513514
return this;
514515
}
515516
this._queryParams['typeahead'] = key
@@ -532,11 +533,11 @@ export class Query extends BaseQuery {
532533
*/
533534
lessThan(key: string, value: (string | number)): Query {
534535
if (!this.isValidAlphanumeric(key)) {
535-
console.error("Invalid key:", key);
536+
console.error(ErrorMessages.INVALID_KEY);
536537
return this;
537538
}
538539
if (typeof value !== 'string' && typeof value !== 'number') {
539-
console.error("Invalid value (expected string or number):", value);
540+
console.error(ErrorMessages.INVALID_VALUE_STRING_OR_NUMBER);
540541
return this;
541542
}
542543

@@ -560,11 +561,11 @@ export class Query extends BaseQuery {
560561
*/
561562
lessThanOrEqualTo(key: string, value: (string | number)): Query {
562563
if (!this.isValidAlphanumeric(key)) {
563-
console.error("Invalid key:", key);
564+
console.error(ErrorMessages.INVALID_KEY);
564565
return this;
565566
}
566567
if (typeof value !== 'string' && typeof value !== 'number') {
567-
console.error("Invalid value (expected string or number):", value);
568+
console.error(ErrorMessages.INVALID_VALUE_STRING_OR_NUMBER);
568569
return this;
569570
}
570571
this._parameters[key] = { '$lte': value };
@@ -587,11 +588,11 @@ export class Query extends BaseQuery {
587588
*/
588589
greaterThan(key: string, value: (string | number)): Query {
589590
if (!this.isValidAlphanumeric(key)) {
590-
console.error("Invalid key:", key);
591+
console.error(ErrorMessages.INVALID_KEY);
591592
return this;
592593
}
593594
if (typeof value !== 'string' && typeof value !== 'number') {
594-
console.error("Invalid value (expected string or number):", value);
595+
console.error(ErrorMessages.INVALID_VALUE_STRING_OR_NUMBER);
595596
return this;
596597
}
597598
this._parameters[key] = { '$gt': value };
@@ -614,11 +615,11 @@ export class Query extends BaseQuery {
614615
*/
615616
greaterThanOrEqualTo(key: string, value: (string | number)): Query {
616617
if (!this.isValidAlphanumeric(key)) {
617-
console.error("Invalid key:", key);
618+
console.error(ErrorMessages.INVALID_KEY);
618619
return this;
619620
}
620621
if (typeof value !== 'string' && typeof value !== 'number') {
621-
console.error("Invalid value (expected string or number):", value);
622+
console.error(ErrorMessages.INVALID_VALUE_STRING_OR_NUMBER);
622623
return this;
623624
}
624625
this._parameters[key] = { '$gte': value };

src/persistance/persistance-store.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { localStorage } from './storages/local-storage';
33
import { memoryStorage } from './storages/memory-storage';
44
import { Storage } from './types/storage';
55
import { StorageType } from './types/storage-type';
6+
import { ErrorMessages } from '../lib/error-messages';
67

78
/**
89
* Persistence store for caching data with expiration support
@@ -41,7 +42,7 @@ export class PersistanceStore {
4142
break;
4243
case 'customStorage':
4344
if (!store) {
44-
throw new TypeError('StorageType `customStorage` should have `storage`.');
45+
throw new TypeError(ErrorMessages.MISSING_CUSTOM_STORAGE);
4546
} else {
4647
this.store = store;
4748
}

test/unit/content-validation-comprehensive.spec.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Entries } from '../../src/lib/entries';
1010
import { GlobalField } from '../../src/lib/global-field';
1111
import { QueryOperation, QueryOperator, TaxonomyQueryOperation } from '../../src/lib/types';
1212
import { MOCK_CLIENT_OPTIONS } from '../utils/constant';
13+
import { ErrorMessages } from '../../src/lib/error-messages';
1314

1415
describe('Content Validation - Comprehensive Test Suite', () => {
1516
let client: AxiosInstance;
@@ -653,14 +654,13 @@ describe('Content Validation - Comprehensive Test Suite', () => {
653654
query.where('view_count', QueryOperation.IS_GREATER_THAN, 100);
654655
query.where('is_published', QueryOperation.EQUALS, true);
655656

656-
// Invalid field UIDs
657+
// Invalid field UIDs (note: field-with-dashes is actually valid as hyphens are allowed)
657658
query.where('invalid field', QueryOperation.EQUALS, 'test');
658-
query.where('field-with-dashes', QueryOperation.EQUALS, 'test');
659-
query.where('123invalid', QueryOperation.EQUALS, 'test');
659+
query.where('field@symbol', QueryOperation.EQUALS, 'test');
660660

661661
// Check that console.error was called for invalid field UIDs
662-
// Note: The validation function only logs for the first invalid field encountered
663-
expect(consoleSpy).toHaveBeenCalledWith('Invalid fieldUid:', 'invalid field');
662+
expect(consoleSpy).toHaveBeenCalledWith(ErrorMessages.INVALID_FIELD_UID);
663+
expect(consoleSpy).toHaveBeenCalledTimes(2);
664664

665665
consoleSpy.mockRestore();
666666
});
@@ -694,8 +694,8 @@ describe('Content Validation - Comprehensive Test Suite', () => {
694694
expect(() => query.regex('title', '[A-Z]+')).not.toThrow();
695695

696696
// Invalid regex patterns
697-
expect(() => query.regex('title', '[a-z')).toThrow('Invalid regexPattern: Must be a valid regular expression');
698-
expect(() => query.regex('title', '*invalid')).toThrow('Invalid regexPattern: Must be a valid regular expression');
697+
expect(() => query.regex('title', '[a-z')).toThrow(ErrorMessages.INVALID_REGEX_PATTERN);
698+
expect(() => query.regex('title', '*invalid')).toThrow(ErrorMessages.INVALID_REGEX_PATTERN);
699699
});
700700

701701
it('should validate query value types', () => {
@@ -707,14 +707,14 @@ describe('Content Validation - Comprehensive Test Suite', () => {
707707
// Valid value types
708708
query.equalTo('title', 'string value');
709709
query.equalTo('view_count', 123);
710-
query.equalTo('is_published', true);
711710

712-
// Invalid value types for equalTo (expects string, number, or boolean)
711+
// Invalid value types for equalTo (expects string or number, not boolean)
712+
query.equalTo('is_published', true as any); // boolean triggers error
713713
query.equalTo('title', [] as any);
714714
query.equalTo('title', {} as any);
715715

716-
expect(consoleSpy).toHaveBeenCalledWith('Invalid value (expected string or number):', []);
717-
expect(consoleSpy).toHaveBeenCalledWith('Invalid value (expected string or number):', {});
716+
expect(consoleSpy).toHaveBeenCalledWith(ErrorMessages.INVALID_VALUE_STRING_OR_NUMBER);
717+
expect(consoleSpy).toHaveBeenCalledTimes(3);
718718

719719
consoleSpy.mockRestore();
720720
});

test/unit/entries.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import MockAdapter from 'axios-mock-adapter';
55
import { entryFetchMock, entryFindMock } from '../utils/mocks';
66
import { Query } from '../../src/lib/query';
77
import { QueryOperation, QueryOperator, TaxonomyQueryOperation } from '../../src/lib/types';
8+
import { ErrorMessages } from '../../src/lib/error-messages';
89

910
describe('Entries class', () => {
1011
let entry: Entries;
@@ -46,7 +47,7 @@ describe('Entries class', () => {
4647
it('should log error when includeReference called with no arguments', () => {
4748
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
4849
entry.includeReference();
49-
expect(consoleErrorSpy).toHaveBeenCalledWith('Argument should be a String or an Array.');
50+
expect(consoleErrorSpy).toHaveBeenCalledWith(ErrorMessages.INVALID_ARGUMENT_STRING_OR_ARRAY);
5051
consoleErrorSpy.mockRestore();
5152
});
5253

0 commit comments

Comments
 (0)