diff --git a/.talismanrc b/.talismanrc index 46d82a6..2c8830b 100644 --- a/.talismanrc +++ b/.talismanrc @@ -1,6 +1,6 @@ fileignoreconfig: - filename: package-lock.json - checksum: d388091773e9515cd3c0a5b881644775aa7b8233a405642e49133f296a4ceeeb + checksum: c27c6a4629a6b1cec5e01b5db15d0e12646a1250e0cf1292ac58c561e9bc4993 - filename: test/unit/image-transform.spec.ts checksum: 7beabdd07bd35d620668fcd97e1a303b9cbc40170bf3008a376d75ce0895de2a - filename: test/utils/mocks.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 80754d2..4e702b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### Version: 4.4.3 +#### Date: November-30-2024 +Fix: regex method fixed for validation + ### Version: 4.4.2 #### Date: November-16-2024 Fix: Variants reset issue fix on query call diff --git a/package-lock.json b/package-lock.json index d95acbd..5355f11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,18 @@ { "name": "@contentstack/delivery-sdk", - "version": "4.4.2", + "version": "4.4.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@contentstack/delivery-sdk", - "version": "4.4.2", + "version": "4.4.3", "license": "MIT", "dependencies": { "@contentstack/core": "^1.1.3", "@contentstack/utils": "^1.3.14", "@types/humps": "^2.0.6", - "axios": "^1.7.7", + "axios": "^1.7.8", "dotenv": "^16.4.5", "humps": "^2.0.1", "path-browserify": "^1.0.1" @@ -4630,10 +4630,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", - "license": "MIT", + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.8.tgz", + "integrity": "sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", diff --git a/package.json b/package.json index 216971b..59503ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/delivery-sdk", - "version": "4.4.2", + "version": "4.4.3", "type": "module", "license": "MIT", "main": "./dist/legacy/index.cjs", @@ -36,7 +36,7 @@ "@contentstack/core": "^1.1.3", "@contentstack/utils": "^1.3.14", "@types/humps": "^2.0.6", - "axios": "^1.7.7", + "axios": "^1.7.8", "dotenv": "^16.4.5", "humps": "^2.0.1", "path-browserify": "^1.0.1" diff --git a/src/lib/base-query.ts b/src/lib/base-query.ts index 91b77ce..cb99594 100644 --- a/src/lib/base-query.ts +++ b/src/lib/base-query.ts @@ -217,7 +217,6 @@ export class BaseQuery extends Pagination { 'x-cs-variant-uid': this._variants }; } - const response = await getData(this._client, this._urlPath, getRequestOptions); return response as FindResponse; diff --git a/src/lib/entry-queryable.ts b/src/lib/entry-queryable.ts index 9a25de0..57c1edd 100644 --- a/src/lib/entry-queryable.ts +++ b/src/lib/entry-queryable.ts @@ -15,9 +15,16 @@ export class EntryQueryable extends BaseQuery { * @param {string} fieldUid - field uid to select * @returns {EntryQueryable} - returns EntryQueryable object for chaining method calls */ - only(fieldUid: string): EntryQueryable { - this._queryParams['only[BASE][]'] = fieldUid; - + only(fieldUid: string|string[]): EntryQueryable { + if (Array.isArray(fieldUid)) { + let i = 0; + for (const uid of fieldUid) { + this._queryParams[`only[BASE][${i}]`] = uid; + i++; + } + } else { + this._queryParams["only[BASE][]"] = fieldUid; + } return this; } @@ -34,8 +41,16 @@ export class EntryQueryable extends BaseQuery { * @param {string} fieldUid - field uid to exclude * @returns {EntryQueryable} - returns EntryQueryable object for chaining method calls */ - except(fieldUid: string): EntryQueryable { - this._queryParams['except[BASE][]'] = fieldUid; + except(fieldUid: string|string[]): EntryQueryable { + if (Array.isArray(fieldUid)) { + let i = 0; + for (const uid of fieldUid) { + this._queryParams[`except[BASE][${i}]`] = uid; + i++; + } + } else { + this._queryParams["except[BASE][]"] = fieldUid; + } return this; } diff --git a/src/lib/query.ts b/src/lib/query.ts index 0139f8a..d2f519e 100644 --- a/src/lib/query.ts +++ b/src/lib/query.ts @@ -3,10 +3,6 @@ import { BaseQuery } from './base-query'; import { BaseQueryParameters, QueryOperation, QueryOperator, TaxonomyQueryOperation } from './types'; import { params, queryParams } from './internal-types'; -const safePatterns: RegExp[] = [ - /^[a-zA-Z0-9_.-]+$/, // Alphanumeric with underscores, periods, and dashes -]; - export class Query extends BaseQuery { private _contentTypeUid?: string; @@ -34,10 +30,16 @@ export class Query extends BaseQuery { // Validate if input matches any of the safe, pre-approved patterns private isValidRegexPattern(input: string): boolean { - if (!this.isValidAlphanumeric(input)) { - return false; + const validRegex = /^[a-zA-Z0-9|^$.*+?()[\]{}\\-]+$/; // Allow only safe regex characters + if (!validRegex.test(input)) { + return false; + } + try { + new RegExp(input); + return true; + } catch (e) { + return false; } - return safePatterns.some(pattern => pattern.test(input)); } private isValidValue(value: any[]): boolean { diff --git a/test/api/entry-queryables.spec.ts b/test/api/entry-queryables.spec.ts index 8763f14..a57c6f1 100644 --- a/test/api/entry-queryables.spec.ts +++ b/test/api/entry-queryables.spec.ts @@ -258,6 +258,28 @@ describe('Query Operators API test cases', () => { } }); + + it('should check for projected fields after only filter is applied', async () => { + const query = makeEntries('contenttype_uid2').only(['title', 'reference']) + const result = await query.find(); + if (result.entries) { + expect(result.entries.length).toBeGreaterThan(0); + expect(result.entries[0].reference).toBeDefined(); + expect(result.entries[0].title).toBeDefined(); + expect(result.entries[0]._version).toBeUndefined(); + } + }); + + it('should ignore fields after except filter is applied', async () => { + const query = makeEntries('contenttype_uid2').except(['title', 'reference']) + const result = await query.find(); + if (result.entries) { + expect(result.entries.length).toBeGreaterThan(0); + expect(result.entries[0].reference).toBeUndefined(); + expect(result.entries[0].title).toBeUndefined(); + expect(result.entries[0]._version).toBeDefined(); + } + }); }); function makeEntries(contentTypeUid = ''): Entries { diff --git a/test/unit/query.spec.ts b/test/unit/query.spec.ts index 4482f7d..301d124 100644 --- a/test/unit/query.spec.ts +++ b/test/unit/query.spec.ts @@ -83,6 +83,82 @@ describe('Query class', () => { expect(mainQuery2._parameters).toHaveProperty('$and', [subQuery1._parameters, subQuery2._parameters]); }); + it('should result in error when regex method is called with invalid regex', async () => { + const regexQuery = getQueryObject(client, 'your-referenced-content-type-uid'); + expect(() => regexQuery.regex("fieldUid", "[a-z")).toThrow("Invalid regexPattern: Must be a valid regular expression"); + }); + + it('should add a regex parameter to _parameters when regex method is called with valid regex', () => { + query.regex('fieldUid', '^ABCXYZ123'); + expect(query._parameters['fieldUid']).toEqual({ $regex: '^ABCXYZ123' }); + }); + + it('should add a containedIn parameter to _parameters', () => { + query.containedIn('fieldUid', ['value1', 'value2']); + expect(query._parameters['fieldUid']).toEqual({ '$in': ['value1', 'value2'] }); + }); + + it('should add a notContainedIn parameter to _parameters', () => { + query.notContainedIn('fieldUid', ['value1', 'value2']); + expect(query._parameters['fieldUid']).toEqual({ '$nin': ['value1', 'value2'] }); + }); + + it('should add an exists parameter to _parameters', () => { + query.exists('fieldUid'); + expect(query._parameters['fieldUid']).toEqual({ '$exists': true }); + }); + + it('should add a notExists parameter to _parameters', () => { + query.notExists('fieldUid'); + expect(query._parameters['fieldUid']).toEqual({ '$exists': false }); + }); + + it('should add an equalTo parameter to _parameters', () => { + query.equalTo('fieldUid', 'value'); + expect(query._parameters['fieldUid']).toEqual('value'); + }); + + it('should add a notEqualTo parameter to _parameters', () => { + query.notEqualTo('fieldUid', 'value'); + expect(query._parameters['fieldUid']).toEqual({ '$ne': 'value' }); + }); + + it('should add a lessThan parameter to _parameters', () => { + query.lessThan('fieldUid', 10); + expect(query._parameters['fieldUid']).toEqual({ '$lt': 10 }); + }); + + it('should add a lessThanOrEqualTo parameter to _parameters', () => { + query.lessThanOrEqualTo('fieldUid', 10); + expect(query._parameters['fieldUid']).toEqual({ '$lte': 10 }); + }); + + it('should add a greaterThan parameter to _parameters', () => { + query.greaterThan('fieldUid', 10); + expect(query._parameters['fieldUid']).toEqual({ '$gt': 10 }); + }); + + it('should add a greaterThanOrEqualTo parameter to _parameters', () => { + query.greaterThanOrEqualTo('fieldUid', 10); + expect(query._parameters['fieldUid']).toEqual({ '$gte': 10 }); + }); + + it('should add a tags parameter to _parameters', () => { + query.tags(['tag1', 'tag2']); + expect(query._parameters['tags']).toEqual(['tag1', 'tag2']); + }); + + it('should add a search parameter to _queryParams', () => { + query.search('searchKey'); + expect(query._queryParams['typeahead']).toEqual('searchKey'); + }); + + it('should provide proper response when find method is called', async () => { + mockClient.onGet(`/content_types/contentTypeUid/entries`).reply(200, entryFindMock); + const returnedValue = await query.find(); + expect(returnedValue).toEqual(entryFindMock); + }); + it('should provide proper response when find method is called', async () => { mockClient.onGet(`/content_types/contentTypeUid/entries`).reply(200, entryFindMock); const returnedValue = await query.find();