Headless CMS and Content Managment Hub
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

228 lines
5.3 KiB

/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
// tslint:disable: readonly-array
import { Types } from '@app/framework';
import { StatusInfo } from './../services/contents.service';
import { LanguageDto } from './../services/languages.service';
import { SchemaDetailsDto } from './../services/schemas.service';
export type QueryValueType =
'boolean' |
'date' |
'datetime' |
'number' |
'reference' |
'status' |
'string' |
'tags';
export interface FilterOperator {
// The optional display value.
name?: string;
// The operator value.
value: string;
// True, when the operator does not require an value.
noValue?: boolean;
}
export interface QueryFieldModel {
// The value type.
type: QueryValueType;
// The allowed operator.
operators: ReadonlyArray<FilterOperator>;
// Extra values.
extra?: any;
}
export interface QueryModel {
// All available fields.
fields: { [name: string]: QueryFieldModel };
}
export type FilterNode = FilterComparison | FilterLogical;
export interface FilterComparison {
// The full path to the property.
path: string;
// The operator.
op: string;
// The value.
value: any;
}
export interface FilterLogical {
// The child filters if the logical filter is a conjunction (AND).
and?: FilterNode[];
// The child filters if the logical filter is a disjunction (OR).
or?: FilterNode[];
}
export interface QuerySorting {
// The full path to the property.
path: string;
// The sort order.
order: SortMode;
}
export type SortMode = 'ascending' | 'descending';
export interface Query {
// The optional filter.
filter?: FilterLogical;
// The full text search.
fullText?: string;
// The sorting.
sort?: QuerySorting[];
// The number of items to take.
take?: number;
// The number of items to skip.
skip?: number;
}
export function encodeQuery(query?: Query) {
if (Types.isEmpty(query)) {
return '';
}
query = { ...query };
if (!query.sort) {
query.sort = [];
}
if (!query.filter) {
query.filter = { and: [] };
}
return encodeURIComponent(JSON.stringify(query));
}
export function hasFilter(query?: Query) {
return !!query && !Types.isEmpty(query.filter);
}
const EqualOperators: ReadonlyArray<FilterOperator> = [
{ name: '==', value: 'eq' },
{ name: '!=', value: 'ne' }
];
const CompareOperator: ReadonlyArray<FilterOperator> = [
{ name: '<', value: 'lt' },
{ name: '<=', value: 'le' },
{ name: '>', value: 'gt' },
{ name: '>=', value: 'ge' }
];
const StringOperators: ReadonlyArray<FilterOperator> = [
{ name: 'T*', value: 'startsWith' },
{ name: '*T', value: 'endsWith' },
{ name: '*T*', value: 'contains' }
];
const ArrayOperators: ReadonlyArray<FilterOperator> = [
{ value: 'empty', noValue: true }
];
const TypeBoolean: QueryFieldModel = {
type: 'boolean',
operators: EqualOperators
};
const TypeDateTime: QueryFieldModel = {
type: 'datetime',
operators: [...EqualOperators, ...CompareOperator]
};
const TypeNumber: QueryFieldModel = {
type: 'number',
operators: [...EqualOperators, ...CompareOperator]
};
const TypeReference: QueryFieldModel = {
type: 'reference',
operators: [...EqualOperators, ...ArrayOperators]
};
const TypeStatus: QueryFieldModel = {
type: 'status',
operators: EqualOperators
};
const TypeString: QueryFieldModel = {
type: 'string',
operators: [...EqualOperators, ...CompareOperator, ...StringOperators, ...ArrayOperators]
};
const TypeTags: QueryFieldModel = {
type: 'string',
operators: EqualOperators
};
export function queryModelFromSchema(schema: SchemaDetailsDto, languages: ReadonlyArray<LanguageDto>, statuses: ReadonlyArray<StatusInfo> | undefined) {
const languagesCodes = languages.map(x => x.iso2Code);
const invariantCodes = ['iv'];
const model: QueryModel = {
fields: {}
};
model.fields['created'] = TypeDateTime;
model.fields['createdBy'] = TypeString;
model.fields['lastModified'] = TypeDateTime;
model.fields['lastModifiedBy'] = TypeString;
model.fields['version'] = TypeNumber;
if (statuses) {
model.fields['status'] = { ...TypeStatus, extra: statuses };
}
for (const field of schema.fields) {
let type: QueryFieldModel | null = null;
if (field.properties.fieldType === 'Tags') {
type = TypeTags;
} else if (field.properties.fieldType === 'Boolean') {
type = TypeBoolean;
} else if (field.properties.fieldType === 'Number') {
type = TypeNumber;
} else if (field.properties.fieldType === 'String') {
type = TypeString;
} else if (field.properties.fieldType === 'DateTime') {
type = TypeDateTime;
} else if (field.properties.fieldType === 'References') {
const extra = field.rawProperties.singleId;
type = { ...TypeReference, extra };
}
if (type) {
const codes = field.isLocalizable ? languagesCodes : invariantCodes;
for (const code of codes) {
model.fields[`data.${field.name}.${code}`] = type;
}
}
}
return model;
}