Skip to content

Commit 0331739

Browse files
Merge pull request #148 from contentstack/v4-beta/next
merge v4-beta/next to release/v4-beta
2 parents e09c48a + 3eee205 commit 0331739

9 files changed

+335
-5
lines changed

.github/workflows/npm-publish.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
node-version: '18.x'
1717
registry-url: 'https://registry.npmjs.org'
1818
- run: npm ci
19-
- run: npm publish --tag beta --access public
19+
- run: npm publish --tag latest --access public
2020
env:
2121
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
2222
publish-git:

CHANGELOG.md

+13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
## Change log
22

3+
### Version: 4.0.0-beta.4
4+
#### Date: March-14-2024
5+
##### New Features:
6+
- Query operators implementation
7+
8+
### Version: 4.0.0-beta.3
9+
#### Date: February-13-2024
10+
- Live preview support 1.0 and 2.0
11+
12+
### Version: v4.0.0-beta.2
13+
#### Date: February-02-2024
14+
- Includes adding of prepare script to build package
15+
316
### Version: 4.0.0-beta
417
#### Date: January-15-2024
518
- Beta release of Typescript SDK

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentstack/delivery-sdk",
3-
"version": "4.0.0-beta.3",
3+
"version": "4.0.0-beta.4",
44
"type": "commonjs",
55
"main": "./dist/cjs/src/index.js",
66
"types": "./dist/types/src/index.d.ts",

src/lib/query.ts

+135
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,139 @@ export class Query extends BaseQuery {
164164
getQuery(): { [key: string]: any } {
165165
return this._parameters;
166166
}
167+
168+
/**
169+
* @method containedIn
170+
* @memberof Query
171+
* @description Returns the raw (JSON) query based on the filters applied on Query object.
172+
* @example
173+
* import contentstack from '@contentstack/delivery-sdk'
174+
*
175+
* const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
176+
* const query = stack.contentType("contentTypeUid").entry().query();
177+
* const result = await query.containedIn('fieldUid', ['value1', 'value2']).find()
178+
*
179+
* @returns {Query}
180+
*/
181+
containedIn(key: string, value: (string | number | boolean)[]): Query {
182+
this._parameters[key] = { '$in': value };
183+
return this;
184+
}
185+
186+
/**
187+
* @method NoContainedIn
188+
* @memberof Query
189+
* @description Returns the raw (JSON) query based on the filters applied on Query object.
190+
* @example
191+
* import contentstack from '@contentstack/delivery-sdk'
192+
*
193+
* const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
194+
* const query = stack.contentType("contentTypeUid").entry().query();
195+
* const result = await query.notContainedIn('fieldUid', ['value1', 'value2']).find()
196+
*
197+
* @returns {Query}
198+
*/
199+
notContainedIn(key: string, value: (string | number | boolean)[]): Query {
200+
this._parameters[key] = { '$nin': value };
201+
return this;
202+
}
203+
204+
/**
205+
* @method notExists
206+
* @memberof Query
207+
* @description Returns the raw (JSON) query based on the filters applied on Query object.
208+
* @example
209+
* import contentstack from '@contentstack/delivery-sdk'
210+
*
211+
* const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
212+
* const query = stack.contentType("contentTypeUid").entry().query();
213+
* const result = await query.notExists('fieldUid').find()
214+
*
215+
* @returns {Query}
216+
*/
217+
notExists(key: string): Query {
218+
this._parameters[key] = { '$exists': false };
219+
return this;
220+
}
221+
222+
/**
223+
* @method or
224+
* @memberof Query
225+
* @description Returns the raw (JSON) query based on the filters applied on Query object.
226+
* @example
227+
* import contentstack from '@contentstack/delivery-sdk'
228+
*
229+
* const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
230+
* const query1 = stack.contentType('contenttype_uid').Entry().query().containedIn('fieldUID', ['value']);
231+
* const query2 = stack.contentType('contenttype_uid').Entry().query().where('fieldUID', QueryOperation.EQUALS, 'value2');
232+
* const query = await stack.contentType('contenttype_uid').Entry().query().or(query1, query2).find();
233+
*
234+
* @returns {Query}
235+
*/
236+
or(...queries: Query[]): Query {
237+
const paramsList: BaseQueryParameters[] = [];
238+
for (const queryItem of queries) {
239+
paramsList.push(queryItem._parameters);
240+
}
241+
this._parameters.$or = paramsList;
242+
return this;
243+
}
244+
245+
/**
246+
* @method and
247+
* @memberof Query
248+
* @description Returns the raw (JSON) query based on the filters applied on Query object.
249+
* @example
250+
* import contentstack from '@contentstack/delivery-sdk'
251+
*
252+
* const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
253+
* const query1 = stack.contentType('contenttype_uid').Entry().query().containedIn('fieldUID', ['value']);
254+
* const query2 = stack.contentType('contenttype_uid').Entry().query().where('fieldUID', QueryOperation.EQUALS, 'value2');
255+
* const query = await stack.contentType('contenttype_uid').Entry().query().and(query1, query2).find();
256+
*
257+
* @returns {Query}
258+
*/
259+
and(...queries: Query[]): Query {
260+
const paramsList: BaseQueryParameters[] = [];
261+
for (const queryItem of queries) {
262+
paramsList.push(queryItem._parameters);
263+
}
264+
this._parameters.$and = paramsList;
265+
return this;
266+
}
267+
268+
/**
269+
* @method equalTo
270+
* @memberof Query
271+
* @description Returns the raw (JSON) query based on the filters applied on Query object.
272+
* @example
273+
* import contentstack from '@contentstack/delivery-sdk'
274+
*
275+
* const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
276+
* const query = await stack.contentType('contenttype_uid').Entry().query().equalTo('fieldUid', 'value').find();
277+
*
278+
* @returns {Query}
279+
*/
280+
equalTo(key: string, value: string | number | boolean): Query {
281+
this._parameters[key] = value;
282+
return this;
283+
}
284+
285+
/**
286+
* @method equalTo
287+
* @memberof Query
288+
* @description Returns the raw (JSON) query based on the filters applied on Query object.
289+
* @example
290+
* import contentstack from '@contentstack/delivery-sdk'
291+
*
292+
* const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
293+
* const query = stack.contentType('contenttype_uid').query().where('title', QueryOperation.EQUALS, 'value');
294+
* const entryQuery = await stack.contentType('contenttype_uid').query().referenceIn('reference_uid', query).find<TEntry>();
295+
*
296+
* @returns {Query}
297+
*/
298+
referenceIn(key: string, query: Query) {
299+
this._parameters[key] = { '$in_query': query._parameters }
300+
return this;
301+
}
167302
}

test/api/contenttype.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/* eslint-disable no-console */
22
/* eslint-disable promise/always-return */
3-
import { BaseContentType, BaseEntry } from 'src';
43
import { ContentType } from '../../src/lib/content-type';
54
import { stackInstance } from '../utils/stack-instance';
65
import { TContentType, TEntry } from './types';
@@ -26,6 +25,7 @@ describe('ContentType API test cases', () => {
2625
expect(result.schema).toBeDefined();
2726
});
2827
});
28+
2929
function makeContentType(uid = ''): ContentType {
3030
const contentType = stack.ContentType(uid);
3131

test/api/entry-queryables.spec.ts

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { stackInstance } from '../utils/stack-instance';
2+
import { Entries } from '../../src/lib/entries';
3+
import { TEntry } from './types';
4+
import { QueryOperation } from '../../src/lib/types';
5+
import { Query } from '../../src/lib/query';
6+
7+
const stack = stackInstance();
8+
9+
describe('Query Operators API test cases', () => {
10+
it('should get entries which matches the fieldUid and values', async () => {
11+
const query = await makeEntries('contenttype_uid').query().containedIn('title', ['value']).find<TEntry>()
12+
if (query.entries) {
13+
expect(query.entries[0]._version).toBeDefined();
14+
expect(query.entries[0].title).toBeDefined();
15+
expect(query.entries[0].uid).toBeDefined();
16+
expect(query.entries[0].created_at).toBeDefined();
17+
}
18+
});
19+
20+
it('should get entries which does not match the fieldUid and values', async () => {
21+
const query = await makeEntries('contenttype_uid').query().notContainedIn('title', ['test', 'test2']).find<TEntry>()
22+
if (query.entries) {
23+
expect(query.entries[0]._version).toBeDefined();
24+
expect(query.entries[0].title).toBeDefined();
25+
expect(query.entries[0].uid).toBeDefined();
26+
expect(query.entries[0].created_at).toBeDefined();
27+
}
28+
});
29+
30+
it('should get entries which does not match the fieldUid - notExists', async () => {
31+
const query = await makeEntries('contenttype_uid').query().notExists('multi_line').find<TEntry>()
32+
if (query.entries) {
33+
expect(query.entries[0]._version).toBeDefined();
34+
expect(query.entries[0].title).toBeDefined();
35+
expect(query.entries[0].uid).toBeDefined();
36+
expect(query.entries[0].created_at).toBeDefined();
37+
expect((query.entries[0] as any).multi_line).not.toBeDefined()
38+
}
39+
});
40+
41+
it('should return entries matching any of the conditions - or', async () => {
42+
const query1: Query = await makeEntries('contenttype_uid').query().containedIn('title', ['value']);
43+
const query2: Query = await makeEntries('contenttype_uid').query().where('title', QueryOperation.EQUALS, 'value2');
44+
const query = await makeEntries('contenttype_uid').query().or(query1, query2).find<TEntry>();
45+
46+
if (query.entries) {
47+
expect(query.entries.length).toBeGreaterThan(0);
48+
expect(query.entries[0]._version).toBeDefined();
49+
expect(query.entries[0].locale).toBeDefined();
50+
expect(query.entries[0].uid).toBeDefined();
51+
expect(query.entries[0].title).toBe('value2');
52+
expect(query.entries[1]._version).toBeDefined();
53+
expect(query.entries[1].locale).toBeDefined();
54+
expect(query.entries[1].uid).toBeDefined();
55+
expect(query.entries[1].title).toBe('value');
56+
}
57+
});
58+
59+
it('should return entries when at least 1 entry condition is matching - or', async () => {
60+
const query1: Query = await makeEntries('contenttype_uid').query().containedIn('title', ['value0']);
61+
const query2: Query = await makeEntries('contenttype_uid').query().where('title', QueryOperation.EQUALS, 'value2');
62+
const query = await makeEntries('contenttype_uid').query().or(query1, query2).find<TEntry>();
63+
64+
if (query.entries) {
65+
expect(query.entries.length).toBeGreaterThan(0);
66+
expect(query.entries[0]._version).toBeDefined();
67+
expect(query.entries[0].locale).toBeDefined();
68+
expect(query.entries[0].uid).toBeDefined();
69+
expect(query.entries[0].title).toBe('value2');
70+
}
71+
});
72+
73+
it('should return entry both conditions are matching - and', async () => {
74+
const query1: Query = await makeEntries('contenttype_uid').query().containedIn('title', ['value']);
75+
const query2: Query = await makeEntries('contenttype_uid').query().where('locale', QueryOperation.EQUALS, 'en-us');
76+
const query = await makeEntries('contenttype_uid').query().and(query1, query2).find<TEntry>();
77+
78+
if (query.entries) {
79+
expect(query.entries.length).toBeGreaterThan(0);
80+
expect(query.entries[0]._version).toBeDefined();
81+
expect(query.entries[0].locale).toBeDefined();
82+
expect(query.entries[0].uid).toBeDefined();
83+
expect(query.entries[0].title).toBe('value');
84+
}
85+
});
86+
87+
it('should return null when any one condition is not matching - and', async () => {
88+
const query1: Query = await makeEntries('contenttype_uid').query().containedIn('title', ['value0']);
89+
const query2: Query = await makeEntries('contenttype_uid').query().where('locale', QueryOperation.EQUALS, 'fr-fr');
90+
const query = await makeEntries('contenttype_uid').query().and(query1, query2).find<TEntry>();
91+
92+
if (query.entries) {
93+
expect(query.entries).toHaveLength(0);
94+
95+
}
96+
});
97+
98+
it('should return entry equal to the condition - equalTo', async () => {
99+
const query = await makeEntries('contenttype_uid').query().equalTo('title', 'value').find<TEntry>();
100+
101+
if (query.entries) {
102+
expect(query.entries[0]._version).toBeDefined();
103+
expect(query.entries[0].locale).toBeDefined();
104+
expect(query.entries[0].uid).toBeDefined();
105+
expect(query.entries[0].title).toBe('value');
106+
}
107+
});
108+
109+
it('should return entry for referencedIn query', async () => {
110+
const query = makeEntries('contenttype_uid').query().where('title', QueryOperation.EQUALS, 'value');
111+
const entryQuery = await makeEntries('contenttype_uid').query().referenceIn('reference_uid', query).find<TEntry>();
112+
if (entryQuery.entries) {
113+
expect(entryQuery.entries[0]._version).toBeDefined();
114+
expect(entryQuery.entries[0].locale).toBeDefined();
115+
expect(entryQuery.entries[0].uid).toBeDefined();
116+
expect(entryQuery.entries[0].title).toBe('test');
117+
}
118+
});
119+
});
120+
121+
function makeEntries(contentTypeUid = ''): Entries {
122+
const entries = stack.ContentType(contentTypeUid).Entry();
123+
return entries;
124+
}

test/unit/contenttype.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Entry } from '../../src/lib/entry';
55
import { contentTypeResponseMock } from '../utils/mocks';
66
import { Entries } from '../../src/lib/entries';
77
import { MOCK_CLIENT_OPTIONS } from '../utils/constant';
8+
import { Query } from 'src/lib/query';
89

910
describe('ContentType class', () => {
1011
let contentType: ContentType;

test/unit/entry-queryable.spec.ts

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { AxiosInstance, httpClient } from '@contentstack/core';
2+
import { ContentType } from '../../src/lib/content-type';
3+
import MockAdapter from 'axios-mock-adapter';
4+
import { MOCK_CLIENT_OPTIONS } from '../utils/constant';
5+
import { Query } from '../../src/lib/query';
6+
import { QueryOperation } from '../../src/lib/types';
7+
8+
9+
describe('Query Operators API test cases', () => {
10+
let contentType: ContentType;
11+
let client: AxiosInstance;
12+
let mockClient: MockAdapter;
13+
14+
beforeAll(() => {
15+
client = httpClient(MOCK_CLIENT_OPTIONS);
16+
mockClient = new MockAdapter(client as any);
17+
});
18+
19+
beforeEach(() => {
20+
contentType = new ContentType(client, 'contentTypeUid');
21+
});
22+
it('should get entries which matches the fieldUid and values', () => {
23+
const query = contentType.Entry().query().containedIn('fieldUID', ['value']);
24+
expect(query._parameters).toStrictEqual({'fieldUID': {'$in': ['value']}});
25+
});
26+
it('should get entries which does not match the fieldUid and values', () => {
27+
const query = contentType.Entry().query().notContainedIn('fieldUID', ['value', 'value2']);
28+
expect(query._parameters).toStrictEqual({'fieldUID': {'$nin': ['value', 'value2']}});
29+
});
30+
it('should get entries which does not match the fieldUid - notExists', () => {
31+
const query = contentType.Entry().query().notExists('fieldUID');
32+
expect(query._parameters).toStrictEqual({'fieldUID': {'$exists': false}});
33+
});
34+
it('should return entries matching any of the conditions - or', async () => {
35+
const query1: Query = await contentType.Entry().query().containedIn('fieldUID', ['value']);
36+
const query2: Query = await contentType.Entry().query().where('fieldUID', QueryOperation.EQUALS, 'value2');
37+
const query = await contentType.Entry().query().or(query1, query2);
38+
expect(query._parameters).toStrictEqual({ '$or': [ {'fieldUID': {'$in': ['value']}}, { 'fieldUID': 'value2' } ] });
39+
});
40+
it('should return entry when both conditions are matching - and', async () => {
41+
const query1: Query = await contentType.Entry().query().containedIn('fieldUID', ['value']);
42+
const query2: Query = await contentType.Entry().query().where('fieldUID', QueryOperation.EQUALS, 'value2');
43+
const query = await contentType.Entry().query().and(query1, query2);
44+
expect(query._parameters).toStrictEqual({ '$and': [ {'fieldUID': {'$in': ['value']}}, { 'fieldUID': 'value2' } ] });
45+
});
46+
it('should return entry equal to the condition - equalTo', async () => {
47+
const query = contentType.Entry().query().equalTo('fieldUID', 'value');
48+
expect(query._parameters).toStrictEqual({ 'fieldUID': 'value' });
49+
});
50+
it('should return entry for referencedIn query', async () => {
51+
const query1 = contentType.Entry().query().containedIn('fieldUID', ['value']);
52+
const entryQuery = await contentType.Entry().query().referenceIn('reference_uid', query1);
53+
if (entryQuery) {
54+
expect(entryQuery._parameters).toEqual({ reference_uid: { '$in_query': { fieldUID: { '$in': [ 'value' ] } } } });
55+
}
56+
});
57+
});

0 commit comments

Comments
 (0)