diff --git a/CHANGELOG.md b/CHANGELOG.md index 76c3030b..bd0e10cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## Change log +### Version: 4.0.0-beta.5 +#### Date: March-26-2024 +##### New Features: +- Query operators implementation-2 + ### Version: 4.0.0-beta.4 #### Date: March-14-2024 ##### New Features: diff --git a/package-lock.json b/package-lock.json index 0f575d3c..4396f0a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "@contentstack/delivery-sdk", - "version": "4.0.0-beta.4", + "version": "4.0.0-beta.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@contentstack/delivery-sdk", - "version": "4.0.0-beta.4", + "version": "4.0.0-beta.5", "dependencies": { "@contentstack/core": "^1.0.1", - "@contentstack/utils": "^1.3.1", + "@contentstack/utils": "^1.3.3", "@types/humps": "^2.0.6", + "axios": "^1.6.8", "dotenv": "^16.3.1", "humps": "^2.0.1" }, @@ -1846,9 +1847,9 @@ } }, "node_modules/@contentstack/utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@contentstack/utils/-/utils-1.3.1.tgz", - "integrity": "sha512-qvhEYAxPzUAC++pA2y6W9uMHAsyeFhRLd/bw/Mw2TblBkOxf62W1ASuRdJZz2bfSYp8aAX4HBC22DvzYvkLgHg==" + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@contentstack/utils/-/utils-1.3.3.tgz", + "integrity": "sha512-Zj2ejyfbxZlXrF1Wl9lhwK2mCCWo5ooiDnSlNA8nupZ1nDsTfouYERgps8r/uyzm18Vda2wBitxloThxKAyzsA==" }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", @@ -3942,11 +3943,11 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -5389,9 +5390,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -12156,9 +12157,9 @@ } }, "@contentstack/utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@contentstack/utils/-/utils-1.3.1.tgz", - "integrity": "sha512-qvhEYAxPzUAC++pA2y6W9uMHAsyeFhRLd/bw/Mw2TblBkOxf62W1ASuRdJZz2bfSYp8aAX4HBC22DvzYvkLgHg==" + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@contentstack/utils/-/utils-1.3.3.tgz", + "integrity": "sha512-Zj2ejyfbxZlXrF1Wl9lhwK2mCCWo5ooiDnSlNA8nupZ1nDsTfouYERgps8r/uyzm18Vda2wBitxloThxKAyzsA==" }, "@cspotcode/source-map-support": { "version": "0.8.1", @@ -13797,11 +13798,11 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "requires": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -14865,9 +14866,9 @@ "dev": true }, "follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==" + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" }, "form-data": { "version": "4.0.0", diff --git a/package.json b/package.json index 0ad398b9..a4f297a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/delivery-sdk", - "version": "4.0.0-beta.4", + "version": "4.0.0-beta.5", "type": "commonjs", "main": "./dist/cjs/src/index.js", "types": "./dist/types/src/index.d.ts", @@ -24,8 +24,9 @@ }, "dependencies": { "@contentstack/core": "^1.0.1", - "@contentstack/utils": "^1.3.1", + "@contentstack/utils": "^1.3.3", "@types/humps": "^2.0.6", + "axios": "^1.6.8", "dotenv": "^16.3.1", "humps": "^2.0.1" }, diff --git a/src/lib/query.ts b/src/lib/query.ts index 066d53e4..1358ce2b 100644 --- a/src/lib/query.ts +++ b/src/lib/query.ts @@ -201,6 +201,24 @@ export class Query extends BaseQuery { return this; } + /** + * @method exists + * @memberof Query + * @description Returns the raw (JSON) query based on the filters applied on Query object. + * @example + * import contentstack from '@contentstack/delivery-sdk' + * + * const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" }); + * const query = stack.contentType("contentTypeUid").entry().query(); + * const result = await query.exists('fieldUid').find() + * + * @returns {Query} + */ + exists(key: string): Query { + this._parameters[key] = { '$exists': true }; + return this; + } + /** * @method notExists * @memberof Query @@ -291,12 +309,138 @@ export class Query extends BaseQuery { * * const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" }); * const query = stack.contentType('contenttype_uid').query().where('title', QueryOperation.EQUALS, 'value'); - * const entryQuery = await stack.contentType('contenttype_uid').query().referenceIn('reference_uid', query).find(); + * const entryQuery = await stack.contentType('contenttype_uid').query().referenceIn('reference_uid', query).find(); * * @returns {Query} */ - referenceIn(key: string, query: Query) { + referenceIn(key: string, query: Query): Query { this._parameters[key] = { '$in_query': query._parameters } return this; -} + } + + /** + * @method referenceNotIn + * @memberof Query + * @description Returns the raw (JSON) query based on the filters applied on Query object. + * @example + * import contentstack from '@contentstack/delivery-sdk' + * + * const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" }); + * const query = stack.contentType('contenttype_uid').query().where('title', QueryOperation.EQUALS, 'value'); + * const entryQuery = await stack.contentType('contenttype_uid').query().referenceNotIn('reference_uid', query).find(); + * + * @returns {Query} + */ + referenceNotIn(key: string, query: Query): Query { + this._parameters[key] = { '$nin_query': query._parameters } + return this; + } + + /** + * @method tags + * @memberof Query + * @description Returns the raw (JSON) query based on the filters applied on Query object. + * @example + * import contentstack from '@contentstack/delivery-sdk' + * + * const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" }); + * const query = stack.contentType('contenttype_uid').query().where('title', QueryOperation.EQUALS, 'value'); + * const entryQuery = await stack.contentType('contenttype_uid').query().tags(['tag1']).find(); + * + * @returns {Query} + */ + tags(values: (string | number | boolean)[]): Query { + this._parameters['tags'] = values; + return this; + } + + /** + * @method search + * @memberof Query + * @description Returns the raw (JSON) query based on the filters applied on Query object. + * @example + * import contentstack from '@contentstack/delivery-sdk' + * + * const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" }); + * const query = stack.contentType('contenttype_uid').query().where('title', QueryOperation.EQUALS, 'value'); + * const entryQuery = await stack.contentType('contenttype_uid').query().search('key').find(); + * + * @returns {Query} + */ + search(key: string): Query { + this._queryParams['typeahead'] = key + return this + } + + /** + * @method lessThan + * @memberof Query + * @description Returns the raw (JSON) query based on the filters applied on Query object. + * @example + * import contentstack from '@contentstack/delivery-sdk' + * + * const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" }); + * const query = stack.contentType('contenttype_uid').query().where('title', QueryOperation.EQUALS, 'value'); + * const entryQuery = await stack.contentType('contenttype_uid').query().lessThan('fieldUid', 'value').find(); + * + * @returns {Query} + */ + lessThan(key: string, value: (string | number)): Query { + this._parameters[key] = { '$lt': value }; + return this; + } + + /** + * @method lessThanOrEqualTo + * @memberof Query + * @description Returns the raw (JSON) query based on the filters applied on Query object. + * @example + * import contentstack from '@contentstack/delivery-sdk' + * + * const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" }); + * const query = stack.contentType('contenttype_uid').query().where('title', QueryOperation.EQUALS, 'value'); + * const entryQuery = await stack.contentType('contenttype_uid').query().lessThanOrEqualTo('fieldUid', 'value').find(); + * + * @returns {Query} + */ + lessThanOrEqualTo(key: string, value: (string | number)): Query { + this._parameters[key] = { '$lte': value }; + return this; + } + + /** + * @method greaterThan + * @memberof Query + * @description Returns the raw (JSON) query based on the filters applied on Query object. + * @example + * import contentstack from '@contentstack/delivery-sdk' + * + * const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" }); + * const query = stack.contentType('contenttype_uid').query().where('title', QueryOperation.EQUALS, 'value'); + * const entryQuery = await stack.contentType('contenttype_uid').query().greaterThan('fieldUid', 'value').find(); + * + * @returns {Query} + */ + greaterThan(key: string, value: (string | number)): Query { + this._parameters[key] = { '$gt': value }; + return this; + } + + /** + * @method greaterThanOrEqualTo + * @memberof Query + * @description Returns the raw (JSON) query based on the filters applied on Query object. + * @example + * import contentstack from '@contentstack/delivery-sdk' + * + * const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" }); + * const query = stack.contentType('contenttype_uid').query().where('title', QueryOperation.EQUALS, 'value'); + * const entryQuery = await stack.contentType('contenttype_uid').query().greaterThanOrEqualTo('fieldUid', 'value').find(); + * + * @returns {Query} + */ + greaterThanOrEqualTo(key: string, value: (string | number)): Query { + this._parameters[key] = { '$gte': value }; + return this; + } } diff --git a/test/api/entry-queryables.spec.ts b/test/api/entry-queryables.spec.ts index feb15b1f..8bef8853 100644 --- a/test/api/entry-queryables.spec.ts +++ b/test/api/entry-queryables.spec.ts @@ -28,7 +28,7 @@ describe('Query Operators API test cases', () => { }); it('should get entries which does not match the fieldUid - notExists', async () => { - const query = await makeEntries('contenttype_uid').query().notExists('multi_line').find() + const query = await makeEntries('contenttype_uid2').query().notExists('multi_line').find() if (query.entries) { expect(query.entries[0]._version).toBeDefined(); expect(query.entries[0].title).toBeDefined(); @@ -38,6 +38,17 @@ describe('Query Operators API test cases', () => { } }); + it('should get entries which matches the fieldUid - exists', async () => { + const query = await makeEntries('contenttype_uid').query().exists('multi_line').find() + if (query.entries) { + expect(query.entries[0]._version).toBeDefined(); + expect(query.entries[0].title).toBeDefined(); + expect(query.entries[0].uid).toBeDefined(); + expect(query.entries[0].created_at).toBeDefined(); + expect((query.entries[0] as any).multi_line).toBeDefined() + } + }); + it('should return entries matching any of the conditions - or', async () => { const query1: Query = await makeEntries('contenttype_uid').query().containedIn('title', ['value']); const query2: Query = await makeEntries('contenttype_uid').query().where('title', QueryOperation.EQUALS, 'value2'); @@ -116,6 +127,110 @@ describe('Query Operators API test cases', () => { expect(entryQuery.entries[0].title).toBe('test'); } }); + it('should return entry for referenceNotIn query', async () => { + const query = makeEntries('contenttype_uid').query().where('title', QueryOperation.EQUALS, 'value'); + const entryQuery = await makeEntries('contenttype_uid').query().referenceNotIn('reference_uid', query).find(); + + if (entryQuery.entries) { + expect(entryQuery.entries[0]._version).toBeDefined(); + expect(entryQuery.entries[0].locale).toBeDefined(); + expect(entryQuery.entries[0].uid).toBeDefined(); + expect(entryQuery.entries[0].title).not.toBe('test'); + expect(entryQuery.entries[0].title).toBe('value2'); + expect(entryQuery.entries[1]._version).toBeDefined(); + expect(entryQuery.entries[1].locale).toBeDefined(); + expect(entryQuery.entries[1].uid).toBeDefined(); + expect(entryQuery.entries[1].title).toBe('value'); + } + }); + + it('should return entry if tags are matching', async () => { + const query = await makeEntries('contenttype_uid').query().tags(['tag1']).find(); + if (query.entries) { + expect(query.entries[0]._version).toBeDefined(); + expect(query.entries[0].locale).toBeDefined(); + expect(query.entries[0].uid).toBeDefined(); + expect(query.entries[0].title).toBe('value'); + } + }); + + it('should search for the matching key and return the entry', async () => { + const query = await makeEntries('contenttype_uid').query().search('value2').find(); + if (query.entries) { + expect(query.entries[0]._version).toBeDefined(); + expect(query.entries[0].locale).toBeDefined(); + expect(query.entries[0].uid).toBeDefined(); + expect(query.entries[0].title).toBe('value2'); + } + }); + + it('should sort entries in ascending order of the given fieldUID', async () => { + const query = await makeEntries('contenttype_uid').query().orderByAscending('title').find(); + if (query.entries) { + expect(query.entries[0]._version).toBeDefined(); + expect(query.entries[0].locale).toBeDefined(); + expect(query.entries[0].uid).toBeDefined(); + expect(query.entries[0].title).toBe('test'); + expect(query.entries[1].title).toBe('test2'); + expect(query.entries[2].title).toBe('value'); + } + }); + + it('should sort entries in descending order of the given fieldUID', async () => { + const query = await makeEntries('contenttype_uid').query().orderByDescending('title').find(); + if (query.entries) { + expect(query.entries[0]._version).toBeDefined(); + expect(query.entries[0].locale).toBeDefined(); + expect(query.entries[0].uid).toBeDefined(); + expect(query.entries[0].title).toBe('value2'); + expect(query.entries[1].title).toBe('value'); + expect(query.entries[2].title).toBe('test2'); + } + }); + + it('should get entries which is lessThan the fieldUid and values', async () => { + const query = await makeEntries('contenttype_uid').query().lessThan('created_at', '2024-03-01T05:25:30.940Z').find() + if (query.entries) { + expect(query.entries.length).toBeGreaterThan(0); + expect(query.entries[0]._version).toBeDefined(); + expect(query.entries[0].title).toBeDefined(); + expect(query.entries[0].uid).toBeDefined(); + expect(query.entries[0].created_at).toBeDefined(); + } + }); + + it('should get entries which is lessThanOrEqualTo the fieldUid and values', async () => { + const query = await makeEntries('contenttype_uid').query().lessThanOrEqualTo('created_at', '2024-03-01T05:25:30.940Z').find() + if (query.entries) { + expect(query.entries.length).toBeGreaterThan(0); + expect(query.entries[0]._version).toBeDefined(); + expect(query.entries[0].title).toBeDefined(); + expect(query.entries[0].uid).toBeDefined(); + expect(query.entries[0].created_at).toBeDefined(); + } + }); + + it('should get entries which is greaterThan the fieldUid and values', async () => { + const query = await makeEntries('contenttype_uid').query().greaterThan('created_at', '2024-03-01T05:25:30.940Z').find() + if (query.entries) { + expect(query.entries.length).toBeGreaterThan(0); + expect(query.entries[0]._version).toBeDefined(); + expect(query.entries[0].title).toBeDefined(); + expect(query.entries[0].uid).toBeDefined(); + expect(query.entries[0].created_at).toBeDefined(); + } + }); + + it('should get entries which is greaterThanOrEqualTo the fieldUid and values', async () => { + const query = await makeEntries('contenttype_uid').query().greaterThanOrEqualTo('created_at', '2024-03-01T05:25:30.940Z').find() + if (query.entries) { + expect(query.entries.length).toBeGreaterThan(0); + expect(query.entries[0]._version).toBeDefined(); + expect(query.entries[0].title).toBeDefined(); + expect(query.entries[0].uid).toBeDefined(); + expect(query.entries[0].created_at).toBeDefined(); + } + }); }); function makeEntries(contentTypeUid = ''): Entries { diff --git a/test/unit/entry-queryable.spec.ts b/test/unit/entry-queryable.spec.ts index 3a97d5da..03885deb 100644 --- a/test/unit/entry-queryable.spec.ts +++ b/test/unit/entry-queryable.spec.ts @@ -31,6 +31,12 @@ describe('Query Operators API test cases', () => { const query = contentType.Entry().query().notExists('fieldUID'); expect(query._parameters).toStrictEqual({'fieldUID': {'$exists': false}}); }); + it('should get entries which matches the fieldUid - exists', async () => { + const query = contentType.Entry().query().exists('fieldUID'); + if (query) { + expect(query._parameters).toEqual({'fieldUID': {'$exists': true}}); + } + }); it('should return entries matching any of the conditions - or', async () => { const query1: Query = await contentType.Entry().query().containedIn('fieldUID', ['value']); const query2: Query = await contentType.Entry().query().where('fieldUID', QueryOperation.EQUALS, 'value2'); @@ -47,11 +53,58 @@ describe('Query Operators API test cases', () => { const query = contentType.Entry().query().equalTo('fieldUID', 'value'); expect(query._parameters).toStrictEqual({ 'fieldUID': 'value' }); }); - it('should return entry for referencedIn query', async () => { - const query1 = contentType.Entry().query().containedIn('fieldUID', ['value']); + it('should return entry for referenceIn query', async () => { + const query1 = contentType.Entry().query().where('fieldUID', QueryOperation.EQUALS, 'value'); const entryQuery = await contentType.Entry().query().referenceIn('reference_uid', query1); if (entryQuery) { - expect(entryQuery._parameters).toEqual({ reference_uid: { '$in_query': { fieldUID: { '$in': [ 'value' ] } } } }); + expect(entryQuery._parameters).toEqual({ reference_uid: { '$in_query': { fieldUID: 'value' } } }); + } + }); + it('should return entry for referenceNotIn query', async () => { + const query1 = contentType.Entry().query().where('fieldUID', QueryOperation.EQUALS, 'value'); + const entryQuery = await contentType.Entry().query().referenceNotIn('reference_uid', query1); + if (entryQuery) { + expect(entryQuery._parameters).toEqual({ reference_uid: { '$nin_query': { fieldUID: 'value' } } }); + } + }); + it('should return entry if tags are matching', async () => { + const query = contentType.Entry().query().tags(['tag1']); + if (query) { + expect(query._parameters).toEqual({ tags: ['tag1'] }); + } + }); + it('should search for the matching key and return the entry', async () => { + const query = contentType.Entry().query().search('entry'); + if (query) { + expect(query._queryParams).toEqual({ typeahead: 'entry' }); } }); + it('should sort entries in ascending order of the given fieldUID', async () => { + const query = contentType.Entry().query().orderByAscending('fieldUid'); + if (query) { + expect(query._queryParams).toEqual({ asc: 'fieldUid' }); + } + }); + it('should sort entries in descending order of the given fieldUID', async () => { + const query = contentType.Entry().query().orderByDescending('fieldUid'); + if (query) { + expect(query._queryParams).toEqual({ desc: 'fieldUid' }); + } + }); + it('should get entries which is lessThan the fieldUid and values', async () => { + const query = contentType.Entry().query().lessThan('fieldUID', 'value'); + expect(query._parameters).toStrictEqual({'fieldUID': {'$lt': 'value'}}); + }); + it('should get entries which is lessThanOrEqualTo the fieldUid and values', async () => { + const query = contentType.Entry().query().lessThanOrEqualTo('fieldUID', 'value'); + expect(query._parameters).toStrictEqual({'fieldUID': {'$lte': 'value'}}); + }); + it('should get entries which is greaterThan the fieldUid and values', async () => { + const query = contentType.Entry().query().greaterThan('fieldUID', 'value'); + expect(query._parameters).toStrictEqual({'fieldUID': {'$gt': 'value'}}); + }); + it('should get entries which is greaterThanOrEqualTo the fieldUid and values', async () => { + const query = contentType.Entry().query().greaterThanOrEqualTo('fieldUID', 'value'); + expect(query._parameters).toStrictEqual({'fieldUID': {'$gte': 'value'}}); + }); }); \ No newline at end of file