Skip to content

Commit

Permalink
Merge pull request #156 from contentstack/feat/cs-43915-or-operator-q…
Browse files Browse the repository at this point in the history
…uery-implementation

changes in query operators implementation
  • Loading branch information
harshithad0703 authored Mar 11, 2024
2 parents 6346656 + 667346a commit cfe3241
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 82 deletions.
16 changes: 0 additions & 16 deletions src/lib/content-type.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { AxiosInstance, getData } from '@contentstack/core';
import { Entry } from './entry';
import { Entries } from './entries';
import { Query } from './query';

interface ContentTypeResponse<T> {
content_type: T;
Expand All @@ -19,21 +18,6 @@ export class ContentType {
this._urlPath = `/content_types/${this._contentTypeUid}`;
}

/**
* @method Query
* @memberof ContentType
* @description queries get all entries that satisfy the condition of the following function
* @returns {Query}
* @example
* import contentstack from '@contentstack/delivery-sdk'
*
* const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
* const entries = stack.contentType("contentTypeUid").Query().containedIn('fieldUid', ['value1','value2'])
*/
Query(): Query {
return new Query(this._client, this._contentTypeUid);
};

/**
* @method entry
* @memberof ContentType
Expand Down
24 changes: 18 additions & 6 deletions src/lib/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export class Query extends BaseQuery {
* import contentstack from '@contentstack/delivery-sdk'
*
* const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
* const query = stack.contentType("contentTypeUid").Query();
* const query = stack.contentType("contentTypeUid").entry().query();
* const result = containedIn('fieldUid', ['value1', 'value2']).find()
*
* @returns {Query}
Expand All @@ -191,7 +191,7 @@ export class Query extends BaseQuery {
* import contentstack from '@contentstack/delivery-sdk'
*
* const stack = contentstack.Stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
* const query = stack.contentType("contentTypeUid").Query();
* const query = stack.contentType("contentTypeUid").entry().query();
* const result = notContainedIn('fieldUid', ['value1', 'value2']).find()
*
* @returns {Query}
Expand All @@ -202,20 +202,32 @@ export class Query extends BaseQuery {
}

/**
* @method notExists
* @method or
* @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").Query();
* const result = notExists('fieldUid').find()
*
* const query1 = await contentType.Entry().query().containedIn('fieldUID', ['value']);
* const query2 = await contentType.Entry().query().where('fieldUID', QueryOperation.EQUALS, 'value2');
* const query = await contentType.Entry().query().or(query1, query2).find();
*
* @returns {Query}
*/
notExists(key: string): Query {
this._parameters[key] = { '$exists': false };
return this;
}

or(...queries: Query[]): Query {
const combinedQuery: any = { $or: [] };
for (const query of queries) {
combinedQuery.$or.push(query._parameters);
}
const newQuery: Query = Object.create(this);
newQuery._parameters = combinedQuery;

return newQuery;
}
}
34 changes: 1 addition & 33 deletions test/api/contenttype.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/* eslint-disable no-console */
/* eslint-disable promise/always-return */
import { BaseContentType, BaseEntry, FindResponse } from 'src';
import { ContentType } from '../../src/lib/content-type';
import { stackInstance } from '../utils/stack-instance';
import { TContentType, TEntries, TEntry } from './types';
import { TContentType, TEntry } from './types';
import dotenv from 'dotenv';

dotenv.config()
Expand All @@ -26,38 +25,7 @@ describe('ContentType API test cases', () => {
expect(result.schema).toBeDefined();
});
});
describe('ContentType Query API test cases', () => {
it('should get entries which matches the fieldUid and values', async () => {
const query = await makeContentType('contenttype_uid').Query().containedIn('title', ['value']).find<TEntry>()
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();
}
});

it('should get entries which does not match the fieldUid and values', async () => {
const query = await makeContentType('contenttype_uid').Query().notContainedIn('title', ['test', 'test2']).find<TEntry>()
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();
}
});

it('should get entries which does not match the fieldUid - notExists', async () => {
const query = await makeContentType('contenttype_uid').Query().notExists('multi_line').find<TEntry>()
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).not.toBeDefined()
}
});
});
function makeContentType(uid = ''): ContentType {
const contentType = stack.ContentType(uid);

Expand Down
62 changes: 62 additions & 0 deletions test/api/entry-queryables.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { stackInstance } from '../utils/stack-instance';
import { Entries } from '../../src/lib/entries';
import { TEntry } from './types';
import { QueryOperation } from 'src/lib/types';
import { Query } from 'src/lib/query';

const stack = stackInstance();

describe('Query Operators API test cases', () => {
it('should get entries which matches the fieldUid and values', async () => {
const query = await makeEntries('contenttype_uid').query().containedIn('title', ['value']).find<TEntry>()
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();
}
});

it('should get entries which does not match the fieldUid and values', async () => {
const query = await makeEntries('contenttype_uid').query().notContainedIn('title', ['test', 'test2']).find<TEntry>()
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();
}
});

it('should get entries which does not match the fieldUid - notExists', async () => {
const query = await makeEntries('contenttype_uid').query().notExists('multi_line').find<TEntry>()
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).not.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');
const query = await makeEntries('contenttype_uid').query().or(query1, query2).find<TEntry>();

if (query.entries) {
expect(query.entries).toHaveLength(2);
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]._version).toBeDefined();
expect(query.entries[1].locale).toBeDefined();
expect(query.entries[1].uid).toBeDefined();
expect(query.entries[1].title).toBe('value');
}
});
});

function makeEntries(contentTypeUid = ''): Entries {
const entries = stack.ContentType(contentTypeUid).Entry();
return entries;
}
27 changes: 0 additions & 27 deletions test/unit/contenttype.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,30 +42,3 @@ describe('ContentType class', () => {
expect(response).toEqual(contentTypeResponseMock.content_type);
});
});

describe('ContentType Query class', () => {
let contentType: ContentType;
let client: AxiosInstance;
let mockClient: MockAdapter;

beforeAll(() => {
client = httpClient(MOCK_CLIENT_OPTIONS);
mockClient = new MockAdapter(client as any);
});

beforeEach(() => {
contentType = new ContentType(client, 'contentTypeUid');
});
it('should get entries which matches the fieldUid and values', () => {
const query = contentType.Query().containedIn('fieldUID', ['value']);
expect(query._parameters).toStrictEqual({'fieldUID': {'$in': ['value']}});
});
it('should get entries which does not match the fieldUid and values', () => {
const query = contentType.Query().notContainedIn('fieldUID', ['value', 'value2']);
expect(query._parameters).toStrictEqual({'fieldUID': {'$nin': ['value', 'value2']}});
});
it('should get entries which does not match the fieldUid - notExists', () => {
const query = contentType.Query().notExists('fieldUID');
expect(query._parameters).toStrictEqual({'fieldUID': {'$exists': false}});
});
});
40 changes: 40 additions & 0 deletions test/unit/entry-queryable.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { AxiosInstance, httpClient } from '@contentstack/core';
import { ContentType } from '../../src/lib/content-type';
import MockAdapter from 'axios-mock-adapter';
import { MOCK_CLIENT_OPTIONS } from '../utils/constant';
import { Query } from 'src/lib/query';
import { QueryOperation } from 'src/lib/types';


describe('Query Operators API test cases', () => {
let contentType: ContentType;
let client: AxiosInstance;
let mockClient: MockAdapter;

beforeAll(() => {
client = httpClient(MOCK_CLIENT_OPTIONS);
mockClient = new MockAdapter(client as any);
});

beforeEach(() => {
contentType = new ContentType(client, 'contentTypeUid');
});
it('should get entries which matches the fieldUid and values', () => {
const query = contentType.Entry().query().containedIn('fieldUID', ['value']);
expect(query._parameters).toStrictEqual({'fieldUID': {'$in': ['value']}});
});
it('should get entries which does not match the fieldUid and values', () => {
const query = contentType.Entry().query().notContainedIn('fieldUID', ['value', 'value2']);
expect(query._parameters).toStrictEqual({'fieldUID': {'$nin': ['value', 'value2']}});
});
it('should get entries which does not match the fieldUid - notExists', () => {
const query = contentType.Entry().query().notExists('fieldUID');
expect(query._parameters).toStrictEqual({'fieldUID': {'$exists': false}});
});
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');
const query = await contentType.Entry().query().or(query1, query2);
expect(query._parameters).toStrictEqual({ '$or': [ {'fieldUID': {'$in': ['value']}}, { 'fieldUID': 'value2' } ] });
});
});

0 comments on commit cfe3241

Please sign in to comment.