diff --git a/lib/index.ts b/lib/index.ts index 6d013dfb..cf8d90a0 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -13,6 +13,12 @@ export * from './models/common/cloud-error.class'; export * from './services/type-resolver.service'; export * from './interfaces/common/iquery.config'; +// filters +export * from './models/common/filters'; + +// parameters +export * from './models/common/parameters'; + // items export * from './models/item/type-resolver.class'; export * from './models/item/content-item.class'; diff --git a/lib/models/common/filters.ts b/lib/models/common/filters.ts index e446d683..479abbf1 100644 --- a/lib/models/common/filters.ts +++ b/lib/models/common/filters.ts @@ -1,7 +1,9 @@ import { IQueryParameter } from '../../interfaces/common/iquery-parameter.interface'; export namespace Filters { - + + var defaultValue: string = ''; + export class EqualsFilter implements IQueryParameter { constructor( public field: string, @@ -17,9 +19,9 @@ export namespace Filters { return this.field.trim(); } - public GetParamValue(): string | null{ + public GetParamValue(): string | null { if (!this.value) { - return null; + return defaultValue; } return this.value @@ -40,9 +42,9 @@ export namespace Filters { return `${this.field.trim()}[all]`; } - public GetParamValue(): string | null{ - if (!this.values) { - return null; + public GetParamValue(): string | null { + if (!this.values || !Array.isArray(this.values)) { + return defaultValue; } return this.values.map(m => m.trim()).join(','); @@ -63,9 +65,9 @@ export namespace Filters { return `${this.field.trim()}[any]`; } - public GetParamValue(): string | null{ - if (!this.values) { - return null; + public GetParamValue(): string | null { + if (!this.values || !Array.isArray(this.values)) { + return defaultValue; } return this.values.map(m => m.trim()).join(','); @@ -86,9 +88,9 @@ export namespace Filters { return `${this.field.trim()}[contains]`; } - public GetParamValue(): string | null{ - if (!this.values) { - return null; + public GetParamValue(): string | null { + if (!this.values || !Array.isArray(this.values)) { + return defaultValue; } return this.values.map(m => m.trim()).join(','); @@ -110,9 +112,9 @@ export namespace Filters { return `${this.field.trim()}[gt]`; } - public GetParamValue(): string | null{ + public GetParamValue(): string | null { if (!this.value) { - return null; + return defaultValue; } return this.value; @@ -124,7 +126,7 @@ export namespace Filters { public field: string, public value: string ) { - if (!this.field.trim) { + if (!this.field) { throw Error(`Field specified in 'GreaterThanOrEqualFilter' is null or empty`); } } @@ -134,9 +136,9 @@ export namespace Filters { return `${this.field.trim()}[gte]`; } - public GetParamValue(): string | null{ + public GetParamValue(): string | null { if (!this.value) { - return null; + return defaultValue; } return this.value; @@ -157,15 +159,12 @@ export namespace Filters { return `${this.field.trim()}[in]`; } - public GetParamValue(): string | null{ - if (!this.values) { - return null; + public GetParamValue(): string | null { + if (!this.values || !Array.isArray(this.values)) { + return defaultValue; } return this.values.map(m => { - if (!m) { - throw Error(`Elements in 'InFilter' cannot be null or empty`); - } return m.trim() } ).join(','); @@ -186,9 +185,9 @@ export namespace Filters { return `${this.field.trim()}[lt]`; } - public GetParamValue(): string | null{ + public GetParamValue(): string | null { if (!this.value) { - return null; + return defaultValue; } return this.value; } @@ -209,9 +208,9 @@ export namespace Filters { return `${this.field.trim()}[lte]`; } - public GetParamValue(): string | null{ + public GetParamValue(): string | null { if (!this.value) { - return null; + return defaultValue; } return this.value; @@ -237,16 +236,19 @@ export namespace Filters { return `${this.field.trim()}[range]`; } - public GetParamValue(): string | null{ - if (!this.lowerValue) { - return null; + public GetParamValue(): string { + var lowerVal = defaultValue; + var higherVal = defaultValue; + + if (this.lowerValue) { + lowerVal = this.lowerValue.toString(); } - if (!this.higherValue) { - return null; + if (this.higherValue) { + higherVal = this.higherValue.toString(); } - return `${this.lowerValue},${this.higherValue}`; + return `${lowerVal},${higherVal}`; } } } diff --git a/lib/models/common/parameters.ts b/lib/models/common/parameters.ts index 0c406935..3d71b45f 100644 --- a/lib/models/common/parameters.ts +++ b/lib/models/common/parameters.ts @@ -2,6 +2,8 @@ import { IQueryParameter } from '../../interfaces/common/iquery-parameter.interf import { SortOrder } from './sort-order.enum'; export namespace Parameters { + + var defaultValue: string = ''; export class ElementsParameter implements IQueryParameter { @@ -13,9 +15,7 @@ export namespace Parameters { constructor( public elementCodenames: string[] ) { - if (!this.elementCodenames) { - throw Error(`'elementCodenames' are not set in 'ElementsParameter'`); - } + } public GetParam(): string { @@ -23,6 +23,10 @@ export namespace Parameters { } public GetParamValue(): string { + if (!this.elementCodenames) { + return defaultValue; + } + return this.elementCodenames.map(m => { if (!m) { throw Error(`Codename of 'ElementsParameter' cannot be null or empty`); diff --git a/lib/query/element/base-element-query.class.ts b/lib/query/element/base-element-query.class.ts index a5da5906..04d54486 100644 --- a/lib/query/element/base-element-query.class.ts +++ b/lib/query/element/base-element-query.class.ts @@ -47,14 +47,6 @@ export abstract class BaseElementQuery extends BaseQuery { } protected getElementQueryUrl(typeCodename: string, elementCodename: string): string { - if (!typeCodename) { - throw Error(`Type codename has to be set in order to fetch content type element`); - } - - if (!elementCodename) { - throw Error(`Type element's codename has to be set in order to fetch content type element`); - } - var action = '/types/' + typeCodename + '/elements/' + elementCodename; return this.getUrl(action, this._queryConfig, this.parameters); diff --git a/lib/query/item/multiple-item-query.class.ts b/lib/query/item/multiple-item-query.class.ts index 835ee7c9..24d9dbdb 100644 --- a/lib/query/item/multiple-item-query.class.ts +++ b/lib/query/item/multiple-item-query.class.ts @@ -33,9 +33,6 @@ export class MultipleItemQuery extends BaseItemQuery * @param type Codename of type to get */ type(type: string): this { - if (!type) { - throw Error(`'type' cannot be null or empty`); - } this._contentType = type; return this; } diff --git a/lib/services/field-map.service.ts b/lib/services/field-map.service.ts index a1b9eba7..3ffcd3b6 100644 --- a/lib/services/field-map.service.ts +++ b/lib/services/field-map.service.ts @@ -36,10 +36,18 @@ export class FieldMapService { * @param queryConfig Query configuration */ mapFields(item: IContentItem, modularContent: any, queryConfig: IItemQueryConfig): TItem{ - if (item == null) { + if (!item) { throw Error(`Cannot map fields because item is not defined`); } + if (!item.elements) { + throw Error(`Cannot map elements of the item`); + } + + if (!item.system) { + throw Error(`Cannot map system attributes of the item`); + } + var properties = Object.getOwnPropertyNames(item.elements); // create typed item @@ -64,7 +72,6 @@ export class FieldMapService { if (!propertyName) { propertyName = fieldName; } - itemTyped[propertyName] = this.mapField(field, modularContent, itemTyped, queryConfig); }); diff --git a/lib/services/taxonomy-map.service.ts b/lib/services/taxonomy-map.service.ts index ae258f64..3d5374f1 100644 --- a/lib/services/taxonomy-map.service.ts +++ b/lib/services/taxonomy-map.service.ts @@ -21,6 +21,10 @@ export class TaxonomyMapService { throw Error(`Cannot map taxonomy due to missing 'terms' property`); } + if (!Array.isArray(taxonomyTerms)) { + throw Error(`Cannot map terms because no terms array was provided`); + } + var mappedSystemAttributes: TaxonomySystemAttributes = new TaxonomySystemAttributes( taxonomySystem.id, taxonomySystem.name, @@ -56,14 +60,6 @@ export class TaxonomyMapService { * @param termsArray Terms array to map */ private mapTaxonomyTerms(termsArray: ITaxonomyTerms[]): TaxonomyTerms[] { - if (!termsArray) { - throw Error(`Cannot map taxonomy terms due to invalid property data`); - } - - if (!Array.isArray(termsArray)) { - throw Error(`Cannot map terms because no terms array was provided`); - } - if (termsArray.length == 0) { return []; } diff --git a/lib/services/type-map.service.ts b/lib/services/type-map.service.ts index 096ff2ce..0ff006a4 100644 --- a/lib/services/type-map.service.ts +++ b/lib/services/type-map.service.ts @@ -15,7 +15,11 @@ export class TypeMapService { private mapType(type: IContentType): ContentType { if (!type) { - throw Error(`Cannot map type: ` + type); + throw Error(`Cannot map type`); + } + + if (!type.elements) { + throw Error(`Cannot map type elements`); } var typeSystem = new ContentTypeSystemAttributes( @@ -27,39 +31,35 @@ export class TypeMapService { var elements: Element[] = []; - if (type.elements) { - var elementNames = Object.getOwnPropertyNames(type.elements); - if (elementNames) { - elementNames.forEach(elementName => { - var typeElement = type.elements[elementName] as CloudTypeResponseInterfaces.IContentTypeElementCloudResponse; + var elementNames = Object.getOwnPropertyNames(type.elements); + elementNames.forEach(elementName => { + var typeElement = type.elements[elementName] as CloudTypeResponseInterfaces.IContentTypeElementCloudResponse; - if (!typeElement) { - throw Error(`Cannot find element '${elementName}' on type '${type}'`); - } - - // use json property as a codename of the type element - var elementCodename = elementName; + if (!typeElement) { + throw Error(`Cannot find element '${elementName}' on type '${type}'`); + } - // extra properties for certain field types - var taxonomyGroup: string | undefined = typeElement.taxonomy_group; - var options: IElementOption[] = []; + // use json property as a codename of the type element + var elementCodename = elementName; - // some elements can contain options - var rawOptions = typeElement.options; - if (rawOptions){ - if (!Array.isArray(rawOptions)){ - throw Error(`Content type 'options' property has to be an array`); - } + // extra properties for certain field types + var taxonomyGroup: string | undefined = typeElement.taxonomy_group; + var options: IElementOption[] = []; - rawOptions.forEach(rawOption => { - options.push(new ElementOption(rawOption.name, rawOption.codename)); - }); - } + // some elements can contain options + var rawOptions = typeElement.options; + if (rawOptions) { + if (!Array.isArray(rawOptions)) { + throw Error(`Content type 'options' property has to be an array`); + } - elements.push(new Element(elementCodename, typeElement.type, typeElement.name, taxonomyGroup, options)); + rawOptions.forEach(rawOption => { + options.push(new ElementOption(rawOption.name, rawOption.codename)); }); } - } + + elements.push(new Element(elementCodename, typeElement.type, typeElement.name, taxonomyGroup, options)); + }); return new ContentType(typeSystem, elements); } diff --git a/test/isolated-tests/query/query-config.spec.ts b/test/isolated-tests/query/query-config.spec.ts new file mode 100644 index 00000000..a5167e65 --- /dev/null +++ b/test/isolated-tests/query/query-config.spec.ts @@ -0,0 +1,37 @@ +// url parser +import urlParser from 'url-parse'; + +// setup +import { setup, Context } from '../../setup'; + +import { QueryConfig } from '../../../lib/models/common/query.config'; +import { ItemQueryConfig } from '../../../lib/models/item/item-query.config'; + +// tests +describe('Query configurations', () => { + + var context = new Context(); + setup(context); + + it(`QueryConfig should set values properly`, () => { + var queryConfig = new QueryConfig({usePreviewMode: true, waitForLoadingNewContent: true}); + expect(queryConfig.usePreviewMode).toEqual(true); + expect(queryConfig.waitForLoadingNewContent).toEqual(true); + + var queryConfig = new QueryConfig({usePreviewMode: false, waitForLoadingNewContent: false}); + expect(queryConfig.usePreviewMode).toEqual(false); + expect(queryConfig.waitForLoadingNewContent).toEqual(false); + }); + + it(`ItemQuerConfig should set values properly`, () => { + var queryConfig = new ItemQueryConfig({usePreviewMode: true, waitForLoadingNewContent: true}); + expect(queryConfig.usePreviewMode).toEqual(true); + expect(queryConfig.waitForLoadingNewContent).toEqual(true); + + var queryConfig = new ItemQueryConfig({usePreviewMode: false, waitForLoadingNewContent: false}); + expect(queryConfig.usePreviewMode).toEqual(false); + expect(queryConfig.waitForLoadingNewContent).toEqual(false); + }); + +}); + diff --git a/test/isolated-tests/query/query-initialization.spec.ts b/test/isolated-tests/query/query-initialization.spec.ts index c6f09f7b..85ce05ca 100644 --- a/test/isolated-tests/query/query-initialization.spec.ts +++ b/test/isolated-tests/query/query-initialization.spec.ts @@ -10,23 +10,23 @@ describe('Query initialization', () => { var context = new Context(); setup(context); - it(`Element query initialization with invalid type should throw exception`, () => { + it(`Element query initialization with invalid type should throw Error`, () => { expect(() => context.deliveryClient.element(null, 'title')).toThrowError(); }); - it(`Element query initialization with invalid type should throw exception`, () => { + it(`Element query initialization with invalid type should throw Error`, () => { expect(() => context.deliveryClient.element('movie', null)).toThrowError(); }); - it(`Item query initialization with invalid codename should throw exception`, () => { + it(`Item query initialization with invalid codename should throw Error`, () => { expect(() => context.deliveryClient.item(null)).toThrowError(); }); - it(`Type query initialization with invalid codename should throw exception`, () => { + it(`Type query initialization with invalid codename should throw Error`, () => { expect(() => context.deliveryClient.type(null)).toThrowError(); }); - it(`Taxonomy query initialization with invalid codename should throw exception`, () => { + it(`Taxonomy query initialization with invalid codename should throw Error`, () => { expect(() => context.deliveryClient.taxonomy(null)).toThrowError(); }); diff --git a/test/isolated-tests/response/responses.spec.ts b/test/isolated-tests/response/responses.spec.ts new file mode 100644 index 00000000..8c77ddb2 --- /dev/null +++ b/test/isolated-tests/response/responses.spec.ts @@ -0,0 +1,43 @@ +// setup +import { setup, Context, Movie, Actor, MockQueryService, warriorMovieJson } from '../../setup'; + +// models +import { ItemResponses } from '../../../lib'; + +// tests +describe('Responses', () => { + + var context = new Context(); + setup(context); + + // mock query service + var mockQueryService = new MockQueryService(context.getConfig()) + + var response: ItemResponses.DeliveryItemResponse; + + beforeAll((done) => { + response = mockQueryService.mockGetSingleItem(warriorMovieJson, {}); + done(); + }) + + it(`DeliveryItemListingResponse should be initialize properties for invalid item`, () => { + var response = new ItemResponses.DeliveryItemListingResponse(null as any, {} as any, {} as any); + var responseWithEmptyArray = new ItemResponses.DeliveryItemListingResponse([] as any, {} as any, {} as any); + + expect(response.isEmpty).toEqual(true); + expect(response.firstItem).toEqual(undefined); + expect(response.lastItem).toEqual(undefined); + expect(responseWithEmptyArray.firstItem).toEqual(undefined); + expect(responseWithEmptyArray.lastItem).toEqual(undefined); + }); + + it(`DeliveryItemListingResponse should be initialize properties for invalid item`, () => { + var response = new ItemResponses.DeliveryItemResponse(null as any, {} as any); + var responseWithItem = new ItemResponses.DeliveryItemResponse({'test':1} as any, {} as any); + + expect(response.isEmpty).toEqual(true); + expect(responseWithItem.isEmpty).toEqual(false); + }); + +}); + diff --git a/test/isolated-tests/services/field-map-service.spec.ts b/test/isolated-tests/services/field-map-service.spec.ts index feaf93ef..9e0395d3 100644 --- a/test/isolated-tests/services/field-map-service.spec.ts +++ b/test/isolated-tests/services/field-map-service.spec.ts @@ -3,10 +3,29 @@ import { setup, Context } from '../../setup'; // models import { FieldMapService } from '../../../lib/services/field-map.service'; +import { FieldInterfaces } from '../../../lib/fields/field-interfaces'; +import { FieldType } from '../../../lib/fields/field-type'; +import { ContentItem } from '../../../lib'; // tests describe('FieldMapService', () => { + var fieldType = 'invalid'; + + class FakeField implements FieldInterfaces.IField { + public type: FieldType = fieldType as any; + constructor( + public name: string, + public value: any + ) { + }; + } + + class FakeContentItem extends ContentItem { + public testField: FakeField; + public elements: any; + } + var context = new Context(); setup(context); @@ -15,6 +34,31 @@ describe('FieldMapService', () => { it(`should throw an Error when invalid response is given`, () => { expect(() => fieldMapService.mapFields(null, null, null)).toThrowError(); expect(() => fieldMapService.mapFields(undefined, undefined, undefined)).toThrowError(); + + expect(() => { + let item = new FakeContentItem(); + item.elements = {}; + fieldMapService.mapFields(item, undefined, undefined) + } ).toThrowError(); + + expect(() => { + let item = new FakeContentItem(); + item.system = {} as any; + fieldMapService.mapFields(item, undefined, undefined) + } ).toThrowError(); + + }); + + it(`should throw an Error when unsupported field type is used`, () => { + + var fakeField = new FakeField('testField', 'testValue') + + var fakeItem = new FakeContentItem(); + fakeItem.elements = { 'testField': fakeField }; + fakeItem.system = {} as any; + fakeItem.system.type = 'movie'; + fakeItem.system.codename = 'cd'; + expect(() => fieldMapService.mapFields(fakeItem, {}, {})).toThrowError(`Unsupported field type '${fieldType}'`); }); }); diff --git a/test/isolated-tests/services/taxonomy-map-service.spec.ts b/test/isolated-tests/services/taxonomy-map-service.spec.ts index 0b724e3c..571181ef 100644 --- a/test/isolated-tests/services/taxonomy-map-service.spec.ts +++ b/test/isolated-tests/services/taxonomy-map-service.spec.ts @@ -14,10 +14,13 @@ describe('TaxonomyMapService', () => { expect(() => taxonomyMapService.mapTaxonomy({} as any, null)).toThrowError(); expect(() => taxonomyMapService.mapTaxonomy({} as any, undefined)).toThrowError(); + expect(() => taxonomyMapService.mapTaxonomy({} as any, 'test' as any)).toThrowError(); + expect(() => taxonomyMapService.mapTaxonomies(null)).toThrowError(); expect(() => taxonomyMapService.mapTaxonomies(undefined)).toThrowError(); expect(() => taxonomyMapService.mapTaxonomies('test' as any)).toThrowError(); + }); }); diff --git a/test/isolated-tests/services/type-map-service.spec.ts b/test/isolated-tests/services/type-map-service.spec.ts index 5ee971ac..3d6d2e0f 100644 --- a/test/isolated-tests/services/type-map-service.spec.ts +++ b/test/isolated-tests/services/type-map-service.spec.ts @@ -11,6 +11,8 @@ describe('TypeMapService', () => { expect(() => typeMapService.mapSingleType(null)).toThrowError(); expect(() => typeMapService.mapSingleType(undefined)).toThrowError(); + expect(() => typeMapService.mapSingleType({} as any)).toThrowError(); + expect(() => typeMapService.mapMultipleTypes(null)).toThrowError(); expect(() => typeMapService.mapMultipleTypes(undefined)).toThrowError(); }); diff --git a/test/isolated-tests/url/item-filters.ts b/test/isolated-tests/url/item-filters.ts index 4a5987f4..1a96a376 100644 --- a/test/isolated-tests/url/item-filters.ts +++ b/test/isolated-tests/url/item-filters.ts @@ -4,6 +4,8 @@ import urlParser from 'url-parse'; // setup import { Context, setup } from '../../setup'; +import { Filters } from '../../../lib'; + // tests describe('Item url filters', () => { @@ -224,6 +226,51 @@ describe('Item url filters', () => { expect(() => context.deliveryClient.items().equalsFilter(null, 'val1')).toThrowError(); }); + // null value checks + + it(`EqualsFilter without value should return empty string as param value`, () => { + expect(new Filters.EqualsFilter('f', undefined).GetParamValue()).toEqual(''); + }); + + it(`AllFilter without value should return empty string as param value`, () => { + expect(new Filters.AllFilter('f', undefined).GetParamValue()).toEqual(''); + }); + + it(`AnyFilter without value should return empty string as param value`, () => { + expect(new Filters.AnyFilter('f', undefined).GetParamValue()).toEqual(''); + }); + + it(`ContainsFilter without value should return empty string as param value`, () => { + expect(new Filters.ContainsFilter('f', undefined).GetParamValue()).toEqual(''); + }); + + it(`GreaterThanFilter without value should return empty string as param value`, () => { + expect(new Filters.GreaterThanFilter('f', undefined).GetParamValue()).toEqual(''); + }); + + it(`GreaterThanOrEqualFilter without value should return empty string as param value`, () => { + expect(new Filters.GreaterThanOrEqualFilter('f', undefined).GetParamValue()).toEqual(''); + }); + + it(`Infilter without value should return empty string as param value`, () => { + expect(new Filters.Infilter('f', undefined).GetParamValue()).toEqual(''); + }); + + it(`LessThanFilter without value should return empty string as param value`, () => { + expect(new Filters.LessThanFilter('f', undefined).GetParamValue()).toEqual(''); + }); + + it(`LessThanOrEqualFilter without value should return empty string as param value`, () => { + expect(new Filters.LessThanOrEqualFilter('f', undefined).GetParamValue()).toEqual(''); + }); + + it(`RangeFilter without value should return empty string as param value`, () => { + expect(new Filters.RangeFilter('f', undefined, 3).GetParamValue()).toEqual(',3'); + }); + + it(`RangeFilter without value should return empty string as param value`, () => { + expect(new Filters.RangeFilter('f', 3, undefined).GetParamValue()).toEqual('3,'); + }); // trim checks it(`inFilter should trim its field`, () => { diff --git a/test/isolated-tests/url/item-url-parameters.spec.ts b/test/isolated-tests/url/item-url-parameters.spec.ts index ce12087f..5b39418c 100644 --- a/test/isolated-tests/url/item-url-parameters.spec.ts +++ b/test/isolated-tests/url/item-url-parameters.spec.ts @@ -5,7 +5,7 @@ import urlParser from 'url-parse'; import { Context, setup } from '../../setup'; // models -import { SortOrder } from '../../../lib'; +import { SortOrder, Parameters } from '../../../lib'; // tests describe('Item url parameters', () => { @@ -184,5 +184,10 @@ describe('Item url parameters', () => { expect(param).toEqual('en'); }); + + // empty value checks + it(`ElementsParameter without value should return empty string as param value`, () => { + expect(new Parameters.ElementsParameter(undefined).GetParamValue()).toEqual(''); + }); });