Browse Source

Model classes.

pull/297/head
Sebastian 8 years ago
parent
commit
078fdc691f
  1. 2
      src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs
  2. 13
      src/Squidex/app/features/administration/services/event-consumers.service.ts
  3. 20
      src/Squidex/app/features/administration/services/users.service.ts
  4. 30
      src/Squidex/app/features/administration/state/event-consumers.state.ts
  5. 4
      src/Squidex/app/features/administration/state/users.state.ts
  6. 6
      src/Squidex/app/features/content/pages/content/content-field.component.ts
  7. 4
      src/Squidex/app/features/content/shared/content-item.component.ts
  8. 12
      src/Squidex/app/features/schemas/pages/schema/field.component.ts
  9. 21
      src/Squidex/app/framework/state.ts
  10. 15
      src/Squidex/app/shared/services/app-clients.service.ts
  11. 15
      src/Squidex/app/shared/services/app-contributors.service.ts
  12. 15
      src/Squidex/app/shared/services/app-languages.service.ts
  13. 15
      src/Squidex/app/shared/services/app-patterns.service.ts
  14. 10
      src/Squidex/app/shared/services/apps.service.ts
  15. 57
      src/Squidex/app/shared/services/assets.service.ts
  16. 10
      src/Squidex/app/shared/services/backups.service.ts
  17. 24
      src/Squidex/app/shared/services/contents.service.spec.ts
  18. 46
      src/Squidex/app/shared/services/contents.service.ts
  19. 15
      src/Squidex/app/shared/services/plans.service.ts
  20. 32
      src/Squidex/app/shared/services/rules.service.ts
  21. 81
      src/Squidex/app/shared/services/schemas.fields.spec.ts
  22. 312
      src/Squidex/app/shared/services/schemas.service.spec.ts
  23. 664
      src/Squidex/app/shared/services/schemas.service.ts
  24. 2
      src/Squidex/app/shared/state/clients.state.ts
  25. 101
      src/Squidex/app/shared/state/contents.state.ts
  26. 44
      src/Squidex/app/shared/state/rules.state.ts
  27. 46
      src/Squidex/app/shared/state/schemas.state.spec.ts
  28. 382
      src/Squidex/app/shared/state/schemas.state.ts

2
src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs

@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
Validate.It(() => "Cannot add a new field.", error =>
{
if (!command.Partitioning.IsValidPartitioning())
if (command.ParentFieldId == null && !command.Partitioning.IsValidPartitioning())
{
error(new ValidationError("Partitioning is not valid.", nameof(command.Partitioning)));
}

13
src/Squidex/app/features/administration/services/event-consumers.service.ts

@ -11,9 +11,13 @@ import { Observable } from 'rxjs';
import '@app/framework/angular/http/http-extensions';
import { ApiUrlConfig, HTTP } from '@app/shared';
import {
ApiUrlConfig,
HTTP,
Model
} from '@app/shared';
export class EventConsumerDto {
export class EventConsumerDto extends Model {
constructor(
public readonly name: string,
public readonly isStopped: boolean,
@ -21,6 +25,11 @@ export class EventConsumerDto {
public readonly error: string,
public readonly position: string
) {
super();
}
public with(value: Partial<EventConsumerDto>): EventConsumerDto {
return this.clone(value);
}
}

20
src/Squidex/app/features/administration/services/users.service.ts

@ -11,23 +11,37 @@ import { Observable } from 'rxjs';
import '@app/framework/angular/http/http-extensions';
import { ApiUrlConfig, HTTP } from '@app/shared';
import {
ApiUrlConfig,
HTTP,
Model
} from '@app/shared';
export class UsersDto {
export class UsersDto extends Model {
constructor(
public readonly total: number,
public readonly items: UserDto[]
) {
super();
}
public with(value: Partial<UsersDto>): UsersDto {
return this.clone(value);
}
}
export class UserDto {
export class UserDto extends Model {
constructor(
public readonly id: string,
public readonly email: string,
public readonly displayName: string,
public readonly isLocked: boolean
) {
super();
}
public with(value: Partial<UserDto>): UserDto {
return this.clone(value);
}
}

30
src/Squidex/app/features/administration/state/event-consumers.state.ts

@ -67,41 +67,41 @@ export class EventConsumersState extends State<Snapshot> {
});
}
public start(es: EventConsumerDto): Observable<any> {
return this.eventConsumersService.putStart(es.name)
public start(eventConsumer: EventConsumerDto): Observable<any> {
return this.eventConsumersService.putStart(eventConsumer.name)
.do(() => {
this.replaceEventConsumer(setStopped(es, false));
this.replaceEventConsumer(setStopped(eventConsumer, false));
})
.notify(this.dialogs);
}
public stop(es: EventConsumerDto): Observable<any> {
return this.eventConsumersService.putStop(es.name)
public stop(eventConsumer: EventConsumerDto): Observable<any> {
return this.eventConsumersService.putStop(eventConsumer.name)
.do(() => {
this.replaceEventConsumer(setStopped(es, true));
this.replaceEventConsumer(setStopped(eventConsumer, true));
})
.notify(this.dialogs);
}
public reset(es: EventConsumerDto): Observable<any> {
return this.eventConsumersService.putReset(es.name)
public reset(eventConsumer: EventConsumerDto): Observable<any> {
return this.eventConsumersService.putReset(eventConsumer.name)
.do(() => {
this.replaceEventConsumer(reset(es));
this.replaceEventConsumer(reset(eventConsumer));
})
.notify(this.dialogs);
}
private replaceEventConsumer(es: EventConsumerDto) {
private replaceEventConsumer(eventConsumer: EventConsumerDto) {
this.next(s => {
const eventConsumers = s.eventConsumers.replaceBy('name', es);
const eventConsumers = s.eventConsumers.replaceBy('name', eventConsumer);
return { ...s, eventConsumers };
});
}
}
const setStopped = (es: EventConsumerDto, isStoped: boolean) =>
new EventConsumerDto(es.name, isStoped, false, es.error, es.position);
const setStopped = (eventConsumer: EventConsumerDto, isStopped: boolean) =>
eventConsumer.with({ isStopped });
const reset = (es: EventConsumerDto) =>
new EventConsumerDto(es.name, es.isStopped, true, es.error, es.position);
const reset = (eventConsumer: EventConsumerDto) =>
eventConsumer.with({ isResetting: true });

4
src/Squidex/app/features/administration/state/users.state.ts

@ -247,7 +247,7 @@ export class UsersState extends State<Snapshot> {
const update = (user: UserDto, request: UpdateUserDto) =>
new UserDto(user.id, request.email, request.displayName, user.isLocked);
user.with(request);
const setLocked = (user: UserDto, isLocked: boolean) =>
new UserDto(user.id, user.email, user.displayName, isLocked);
user.with({ isLocked });

6
src/Squidex/app/features/content/pages/content/content-field.component.ts

@ -10,9 +10,9 @@ import { AbstractControl, FormGroup } from '@angular/forms';
import {
AppLanguageDto,
FieldDto,
fieldInvariant,
ImmutableArray
ImmutableArray,
RootFieldDto
} from '@app/shared';
@Component({
@ -22,7 +22,7 @@ import {
})
export class ContentFieldComponent implements OnChanges {
@Input()
public field: FieldDto;
public field: RootFieldDto;
@Input()
public fieldForm: FormGroup;

4
src/Squidex/app/features/content/shared/content-item.component.ts

@ -12,10 +12,10 @@ import {
ContentDto,
ContentsState,
fadeAnimation,
FieldDto,
fieldInvariant,
ModalView,
PatchContentForm,
RootFieldDto,
SchemaDetailsDto,
Types
} from '@app/shared';
@ -136,7 +136,7 @@ export class ContentItemComponent implements OnChanges {
}
}
private getRawValue(field: FieldDto): any {
private getRawValue(field: RootFieldDto): any {
const contentField = this.content.dataDraft[field.name];
if (contentField) {

12
src/Squidex/app/features/schemas/pages/schema/field.component.ts

@ -9,15 +9,17 @@ import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import {
AnyFieldDto,
AppPatternDto,
createProperties,
EditFieldForm,
fadeAnimation,
FieldDto,
ImmutableArray,
ModalView,
RootFieldDto,
SchemaDetailsDto,
SchemasState,
Types,
UpdateFieldDto
} from '@app/shared';
@ -31,7 +33,7 @@ import {
})
export class FieldComponent implements OnInit {
@Input()
public field: FieldDto;
public field: AnyFieldDto;
@Input()
public schema: SchemaDetailsDto;
@ -56,7 +58,7 @@ export class FieldComponent implements OnInit {
this.editForm = new EditFieldForm(this.formBuilder);
this.editForm.load(this.field.properties);
if (this.field.isLocked) {
if (Types.is(this.field, RootFieldDto) && this.field.isLocked) {
this.editForm.form.disable();
}
}
@ -95,7 +97,9 @@ export class FieldComponent implements OnInit {
}
public lockField() {
this.schemasState.lockField(this.schema, this.field).onErrorResumeNext().subscribe();
if (Types.is(this.field, RootFieldDto)) {
this.schemasState.lockField(this.schema, this.field).onErrorResumeNext().subscribe();
}
}
public save() {

21
src/Squidex/app/framework/state.ts

@ -78,6 +78,27 @@ export class Form<T extends AbstractControl> {
}
}
export class Model {
protected onCreated() {
return;
}
protected clone(update: ((v: any) => object) | object): any {
let values: object;
if (Types.isFunction(update)) {
values = update(<any>this);
} else {
values = update;
}
const clone = Object.assign(Object.create(Object.getPrototypeOf(this)), this, values);
clone.onCreated();
return clone;
}
}
export class State<T extends {}> {
private readonly state: BehaviorSubject<T>;
private readonly initialState: T;

15
src/Squidex/app/shared/services/app-clients.service.ts

@ -15,25 +15,36 @@ import {
AnalyticsService,
ApiUrlConfig,
HTTP,
Model,
Version,
Versioned
} from '@app/framework';
export class AppClientsDto {
export class AppClientsDto extends Model {
constructor(
public readonly clients: AppClientDto[],
public readonly version: Version
) {
super();
}
public with(value: Partial<AppClientsDto>): AppClientsDto {
return this.clone(value);
}
}
export class AppClientDto {
export class AppClientDto extends Model {
constructor(
public readonly id: string,
public readonly name: string,
public readonly secret: string,
public readonly permission: string
) {
super();
}
public with(value: Partial<AppClientDto>): AppClientDto {
return this.clone(value);
}
}

15
src/Squidex/app/shared/services/app-contributors.service.ts

@ -15,24 +15,35 @@ import {
AnalyticsService,
ApiUrlConfig,
HTTP,
Model,
Version,
Versioned
} from '@app/framework';
export class AppContributorsDto {
export class AppContributorsDto extends Model {
constructor(
public readonly contributors: AppContributorDto[],
public readonly maxContributors: number,
public readonly version: Version
) {
super();
}
public with(value: Partial<AppContributorsDto>): AppContributorsDto {
return this.clone(value);
}
}
export class AppContributorDto {
export class AppContributorDto extends Model {
constructor(
public readonly contributorId: string,
public readonly permission: string
) {
super();
}
public with(value: Partial<AppContributorDto>): AppContributorDto {
return this.clone(value);
}
}

15
src/Squidex/app/shared/services/app-languages.service.ts

@ -15,19 +15,25 @@ import {
AnalyticsService,
ApiUrlConfig,
HTTP,
Model,
Version,
Versioned
} from '@app/framework';
export class AppLanguagesDto {
export class AppLanguagesDto extends Model {
constructor(
public readonly languages: AppLanguageDto[],
public readonly version: Version
) {
super();
}
public with(value: Partial<AppLanguagesDto>): AppLanguagesDto {
return this.clone(value);
}
}
export class AppLanguageDto {
export class AppLanguageDto extends Model {
constructor(
public readonly iso2Code: string,
public readonly englishName: string,
@ -35,6 +41,11 @@ export class AppLanguageDto {
public readonly isOptional: boolean,
public readonly fallback: string[]
) {
super();
}
public with(value: Partial<AppLanguageDto>): AppLanguageDto {
return this.clone(value);
}
}

15
src/Squidex/app/shared/services/app-patterns.service.ts

@ -14,25 +14,36 @@ import '@app/framework/angular/http/http-extensions';
import {
ApiUrlConfig,
HTTP,
Model,
Version,
Versioned
} from '@app/framework';
export class AppPatternsDto {
export class AppPatternsDto extends Model {
constructor(
public readonly patterns: AppPatternDto[],
public readonly version: Version
) {
super();
}
public with(value: Partial<AppPatternsDto>): AppPatternsDto {
return this.clone(value);
}
}
export class AppPatternDto {
export class AppPatternDto extends Model {
constructor(
public readonly id: string,
public readonly name: string,
public readonly pattern: string,
public readonly message: string
) {
super();
}
public with(value: Partial<AppPatternDto>): AppPatternDto {
return this.clone(value);
}
}

10
src/Squidex/app/shared/services/apps.service.ts

@ -15,10 +15,11 @@ import {
AnalyticsService,
ApiUrlConfig,
DateTime,
HTTP
HTTP,
Model
} from '@app/framework';
export class AppDto {
export class AppDto extends Model {
constructor(
public readonly id: string,
public readonly name: string,
@ -28,6 +29,11 @@ export class AppDto {
public readonly planName: string,
public readonly planUpgrade: string
) {
super();
}
public with(value: Partial<AppDto>): AppDto {
return this.clone(value);
}
}

57
src/Squidex/app/shared/services/assets.service.ts

@ -14,20 +14,26 @@ import {
ApiUrlConfig,
DateTime,
HTTP,
Model,
Types,
Version,
Versioned
} from '@app/framework';
export class AssetsDto {
export class AssetsDto extends Model {
constructor(
public readonly total: number,
public readonly items: AssetDto[]
) {
super();
}
public with(value: Partial<AssetsDto>): AssetsDto {
return this.clone(value);
}
}
export class AssetDto {
export class AssetDto extends Model {
constructor(
public readonly id: string,
public readonly createdBy: string,
@ -45,40 +51,29 @@ export class AssetDto {
public readonly url: string,
public readonly version: Version
) {
super();
}
public with(value: Partial<AssetDto>): AssetDto {
return this.clone(value);
}
public update(update: AssetReplacedDto, user: string, version: Version, now?: DateTime): AssetDto {
return new AssetDto(
this.id,
this.createdBy, user,
this.created, now || DateTime.now(),
this.fileName,
this.fileType,
update.fileSize,
update.fileVersion,
update.mimeType,
update.isImage,
update.pixelWidth,
update.pixelHeight,
this.url,
version);
return this.with({
...update,
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
}
public rename(name: string, user: string, version: Version, now?: DateTime): AssetDto {
return new AssetDto(
this.id,
this.createdBy, user,
this.created, now || DateTime.now(),
name,
this.fileType,
this.fileSize,
this.fileVersion,
this.mimeType,
this.isImage,
this.pixelWidth,
this.pixelHeight,
this.url,
version);
public rename(fileName: string, user: string, version: Version, now?: DateTime): AssetDto {
return this.with({
fileName,
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
}
}

10
src/Squidex/app/shared/services/backups.service.ts

@ -14,10 +14,11 @@ import '@app/framework/angular/http/http-extensions';
import {
AnalyticsService,
ApiUrlConfig,
DateTime
DateTime,
Model
} from '@app/framework';
export class BackupDto {
export class BackupDto extends Model {
constructor(
public readonly id: string,
public readonly started: DateTime,
@ -26,6 +27,11 @@ export class BackupDto {
public readonly handledAssets: number,
public readonly isFailed: boolean
) {
super();
}
public with(value: Partial<BackupDto>): BackupDto {
return this.clone(value);
}
}

24
src/Squidex/app/shared/services/contents.service.spec.ts

@ -89,17 +89,17 @@ describe('ContentsService', () => {
expect(contents).toEqual(
new ContentsDto(10, [
new ContentDto('id1', 'Published', 'Created1', 'LastModifiedBy1',
DateTime.parseISO_UTC('2016-12-12T10:10'),
DateTime.parseISO_UTC('2017-12-12T10:10'),
new ContentDto('id1', 'Published',
DateTime.parseISO_UTC('2016-12-12T10:10'), 'Created1',
DateTime.parseISO_UTC('2017-12-12T10:10'), 'LastModifiedBy1',
new ScheduleDto('Draft', 'Scheduler1', DateTime.parseISO_UTC('2018-12-12T10:10')),
true,
{},
{},
new Version('11')),
new ContentDto('id2', 'Published', 'Created2', 'LastModifiedBy2',
DateTime.parseISO_UTC('2016-10-12T10:10'),
DateTime.parseISO_UTC('2017-10-12T10:10'),
new ContentDto('id2', 'Published',
DateTime.parseISO_UTC('2016-10-12T10:10'), 'Created2',
DateTime.parseISO_UTC('2017-10-12T10:10'), 'LastModifiedBy2',
null,
false,
{},
@ -183,9 +183,9 @@ describe('ContentsService', () => {
});
expect(content).toEqual(
new ContentDto('id1', 'Published', 'Created1', 'LastModifiedBy1',
DateTime.parseISO_UTC('2016-12-12T10:10'),
DateTime.parseISO_UTC('2017-12-12T10:10'),
new ContentDto('id1', 'Published',
DateTime.parseISO_UTC('2016-12-12T10:10'), 'Created1',
DateTime.parseISO_UTC('2017-12-12T10:10'), 'LastModifiedBy1',
new ScheduleDto('Draft', 'Scheduler1', DateTime.parseISO_UTC('2018-12-12T10:10')),
true,
{},
@ -224,9 +224,9 @@ describe('ContentsService', () => {
});
expect(content).toEqual(
new ContentDto('id1', 'Published', 'Created1', 'LastModifiedBy1',
DateTime.parseISO_UTC('2016-12-12T10:10'),
DateTime.parseISO_UTC('2017-12-12T10:10'),
new ContentDto('id1', 'Published',
DateTime.parseISO_UTC('2016-12-12T10:10'), 'Created1',
DateTime.parseISO_UTC('2017-12-12T10:10'), 'LastModifiedBy1',
null,
true,
null,

46
src/Squidex/app/shared/services/contents.service.ts

@ -16,42 +16,58 @@ import {
ApiUrlConfig,
DateTime,
HTTP,
Model,
Version,
Versioned
} from '@app/framework';
export class ContentsDto {
export class ContentsDto extends Model {
constructor(
public readonly total: number,
public readonly items: ContentDto[]
) {
super();
}
}
public with(value: Partial<ContentsDto>): ContentsDto {
return this.clone(value);
}}
export class ScheduleDto {
export class ScheduleDto extends Model {
constructor(
public readonly status: string,
public readonly scheduledBy: string,
public readonly dueTime: DateTime
) {
super();
}
public with(value: Partial<ScheduleDto>): ScheduleDto {
return this.clone(value);
}
}
export class ContentDto {
export class ContentDto extends Model {
constructor(
public readonly id: string,
public readonly status: string,
public readonly createdBy: string,
public readonly lastModifiedBy: string,
public readonly created: DateTime,
public readonly createdBy: string,
public readonly lastModified: DateTime,
public readonly lastModifiedBy: string,
public readonly scheduleJob: ScheduleDto | null,
public readonly isPending: boolean,
public readonly data: object | any,
public readonly dataDraft: object,
public readonly version: Version
) {
super();
}
public with(value: Partial<ContentDto>): ContentDto {
return this.clone(value);
}
}
@ -107,10 +123,8 @@ export class ContentsService {
return new ContentDto(
item.id,
item.status,
item.createdBy,
item.lastModifiedBy,
DateTime.parseISO_UTC(item.created),
DateTime.parseISO_UTC(item.lastModified),
DateTime.parseISO_UTC(item.created), item.createdBy,
DateTime.parseISO_UTC(item.lastModified), item.lastModifiedBy,
item.scheduleJob
? new ScheduleDto(
item.scheduleJob.status,
@ -136,10 +150,8 @@ export class ContentsService {
return new ContentDto(
body.id,
body.status,
body.createdBy,
body.lastModifiedBy,
DateTime.parseISO_UTC(body.created),
DateTime.parseISO_UTC(body.lastModified),
DateTime.parseISO_UTC(body.created), body.createdBy,
DateTime.parseISO_UTC(body.lastModified), body.lastModifiedBy,
body.scheduleJob
? new ScheduleDto(
body.scheduleJob.status,
@ -174,10 +186,8 @@ export class ContentsService {
return new ContentDto(
body.id,
body.status,
body.createdBy,
body.lastModifiedBy,
DateTime.parseISO_UTC(body.created),
DateTime.parseISO_UTC(body.lastModified),
DateTime.parseISO_UTC(body.created), body.createdBy,
DateTime.parseISO_UTC(body.lastModified), body.lastModifiedBy,
null,
true,
null,

15
src/Squidex/app/shared/services/plans.service.ts

@ -15,11 +15,12 @@ import {
AnalyticsService,
ApiUrlConfig,
HTTP,
Model,
Version,
Versioned
} from '@app/framework';
export class PlansDto {
export class PlansDto extends Model {
constructor(
public readonly currentPlanId: string,
public readonly planOwner: string,
@ -27,10 +28,15 @@ export class PlansDto {
public readonly plans: PlanDto[],
public readonly version: Version
) {
super();
}
public with(value: Partial<PlansDto>): PlansDto {
return this.clone(value);
}
}
export class PlanDto {
export class PlanDto extends Model {
constructor(
public readonly id: string,
public readonly name: string,
@ -41,6 +47,11 @@ export class PlanDto {
public readonly maxAssetSize: number,
public readonly maxContributors: number
) {
super();
}
public with(value: Partial<PlanDto>): PlanDto {
return this.clone(value);
}
}

32
src/Squidex/app/shared/services/rules.service.ts

@ -16,6 +16,7 @@ import {
ApiUrlConfig,
DateTime,
HTTP,
Model,
Version,
Versioned
} from '@app/framework';
@ -50,7 +51,7 @@ export const ruleActions: any = {
}
};
export class RuleDto {
export class RuleDto extends Model {
constructor(
public readonly id: string,
public readonly createdBy: string,
@ -64,10 +65,28 @@ export class RuleDto {
public readonly action: any,
public readonly actionType: string
) {
super();
}
public with(value: Partial<RuleDto>): RuleDto {
return this.clone(value);
}
}
export class RuleEventsDto extends Model {
constructor(
public readonly total: number,
public readonly items: RuleEventDto[]
) {
super();
}
public with(value: Partial<RuleEventsDto>): RuleEventsDto {
return this.clone(value);
}
}
export class RuleEventDto {
export class RuleEventDto extends Model {
constructor(
public readonly id: string,
public readonly created: DateTime,
@ -79,14 +98,11 @@ export class RuleEventDto {
public readonly jobResult: string,
public readonly numCalls: number
) {
super();
}
}
export class RuleEventsDto {
constructor(
public readonly total: number,
public readonly items: RuleEventDto[]
) {
public with(value: Partial<RuleEventDto>): RuleEventDto {
return this.clone(value);
}
}

81
src/Squidex/app/shared/services/schemas.fields.spec.ts

@ -8,15 +8,16 @@
import { DateTime } from '@app/framework';
import {
ArrayFieldPropertiesDto,
AssetsFieldPropertiesDto,
BooleanFieldPropertiesDto,
DateTimeFieldPropertiesDto,
FieldDto,
FieldPropertiesDto,
GeolocationFieldPropertiesDto,
JsonFieldPropertiesDto,
NumberFieldPropertiesDto,
ReferencesFieldPropertiesDto,
RootFieldDto,
SchemaDetailsDto,
SchemaPropertiesDto,
StringFieldPropertiesDto,
@ -43,9 +44,9 @@ describe('SchemaDetailsDto', () => {
});
it('should return configured fields as list fields if no schema field are declared', () => {
const field1 = createField(new AssetsFieldPropertiesDto(null, null, null, null, false, true), 1);
const field2 = createField(new AssetsFieldPropertiesDto(null, null, null, null, false, false), 2);
const field3 = createField(new AssetsFieldPropertiesDto(null, null, null, null, false, true), 3);
const field1 = createField(new ArrayFieldPropertiesDto({ isListField: true }), 1);
const field2 = createField(new ArrayFieldPropertiesDto({ isListField: false }), 2);
const field3 = createField(new ArrayFieldPropertiesDto({ isListField: true }), 3);
const schema = createSchema(new SchemaPropertiesDto(''), 1, [field1, field2, field3]);
@ -53,9 +54,9 @@ describe('SchemaDetailsDto', () => {
});
it('should return first fields as list fields if no schema field is declared', () => {
const field1 = createField(new AssetsFieldPropertiesDto(null, null, null, null, false, false), 1);
const field2 = createField(new AssetsFieldPropertiesDto(null, null, null, null, false, false), 2);
const field3 = createField(new AssetsFieldPropertiesDto(null, null, null, null, false, false), 3);
const field1 = createField(new ArrayFieldPropertiesDto(), 1);
const field2 = createField(new ArrayFieldPropertiesDto(), 2);
const field3 = createField(new ArrayFieldPropertiesDto(), 3);
const schema = createSchema(new SchemaPropertiesDto(''), 1, [field1, field2, field3]);
@ -63,7 +64,7 @@ describe('SchemaDetailsDto', () => {
});
it('should return empty list fields if fields is empty', () => {
const schema = createSchema(new SchemaPropertiesDto(''), 1, []);
const schema = createSchema(new SchemaPropertiesDto(), 1, []);
expect(schema.listFields).toEqual([{ properties: {} }]);
});
@ -71,50 +72,74 @@ describe('SchemaDetailsDto', () => {
describe('FieldDto', () => {
it('should return label as display name', () => {
const field = createField(new AssetsFieldPropertiesDto('Label', null, null, null, true, false), 1);
const field = createField(new AssetsFieldPropertiesDto({ label: 'Label' }), 1);
expect(field.displayName).toBe('Label');
});
it('should return name as display name if label is null', () => {
const field = createField(new AssetsFieldPropertiesDto(null, null, null, null, true, false), 1);
const field = createField(new AssetsFieldPropertiesDto(), 1);
expect(field.displayName).toBe('field1');
});
it('should return name as display name label is empty', () => {
const field = createField(new AssetsFieldPropertiesDto('', null, null, null, true, false), 1);
const field = createField(new AssetsFieldPropertiesDto({ label: '' }), 1);
expect(field.displayName).toBe('field1');
});
it('should return placeholder as display placeholder', () => {
const field = createField(new AssetsFieldPropertiesDto(null, null, 'Placeholder', null, true, false), 1);
const field = createField(new AssetsFieldPropertiesDto({ placeholder: 'Placeholder' }), 1);
expect(field.displayPlaceholder).toBe('Placeholder');
});
it('should return empty as display placeholder if placeholder is null', () => {
const field = createField(new AssetsFieldPropertiesDto(null, null, null, null, true, false));
const field = createField(new AssetsFieldPropertiesDto());
expect(field.displayPlaceholder).toBe('');
});
it('should return localizable if partitioning is language', () => {
const field = createField(new AssetsFieldPropertiesDto(null, null, null, null, true, false), 1, 'language');
const field = createField(new AssetsFieldPropertiesDto(), 1, 'language');
expect(field.isLocalizable).toBeTruthy();
});
it('should not return localizable if partitioning is invarient', () => {
const field = createField(new AssetsFieldPropertiesDto(null, null, null, null, true, false), 1, 'invariant');
const field = createField(new AssetsFieldPropertiesDto(), 1, 'invariant');
expect(field.isLocalizable).toBeFalsy();
});
});
describe('ArrayField', () => {
const field = createField(new ArrayFieldPropertiesDto({ isRequired: true, minItems: 1, maxItems: 5 }));
it('should create validators', () => {
expect(field.createValidators(false).length).toBe(3);
});
it('should format to empty string if null', () => {
expect(field.formatValue(null)).toBe('');
});
it('should format to asset count', () => {
expect(field.formatValue([1, 2, 3])).toBe('3 Items(s)');
});
it('should return zero formatting if other type', () => {
expect(field.formatValue(1)).toBe('0 Items');
});
it('should return null for default properties', () => {
expect(field.defaultValue()).toBeNull();
});
});
describe('AssetsField', () => {
const field = createField(new AssetsFieldPropertiesDto(null, null, null, null, true, false, 1, 1));
const field = createField(new AssetsFieldPropertiesDto({ isRequired: true, minItems: 1, maxItems: 5 }));
it('should create validators', () => {
expect(field.createValidators(false).length).toBe(3);
@ -138,7 +163,7 @@ describe('AssetsField', () => {
});
describe('TagsField', () => {
const field = createField(new TagsFieldPropertiesDto(null, null, null, null, true, false, 1, 1));
const field = createField(new TagsFieldPropertiesDto({ isRequired: true, minItems: 1, maxItems: 5 }));
it('should create validators', () => {
expect(field.createValidators(false).length).toBe(3);
@ -162,7 +187,7 @@ describe('TagsField', () => {
});
describe('BooleanField', () => {
const field = createField(new BooleanFieldPropertiesDto(null, null, null, null, true, false, false, 'Checkbox'));
const field = createField(new BooleanFieldPropertiesDto('Checkbox', { isRequired: true }));
it('should create validators', () => {
expect(field.createValidators(false).length).toBe(1);
@ -189,7 +214,7 @@ describe('BooleanField', () => {
describe('DateTimeField', () => {
const now = DateTime.parseISO_UTC('2017-10-12T16:30:10Z');
const field = createField(new DateTimeFieldPropertiesDto(null, null, null, null, true, false, 'Date'));
const field = createField(new DateTimeFieldPropertiesDto('DateTime', { isRequired: true }));
it('should create validators', () => {
expect(field.createValidators(false).length).toBe(1);
@ -204,13 +229,13 @@ describe('DateTimeField', () => {
});
it('should format to date', () => {
const dateField = createField(new DateTimeFieldPropertiesDto(null, null, null, null, true, false, 'Date'));
const dateField = createField(new DateTimeFieldPropertiesDto('Date'));
expect(dateField.formatValue('2017-12-12T16:00:00Z')).toBe('2017-12-12');
});
it('should format to date time', () => {
const dateTimeField = createField(new DateTimeFieldPropertiesDto(null, null, null, null, true, false, 'DateTime'));
const dateTimeField = createField(new DateTimeFieldPropertiesDto('DateTime'));
expect(dateTimeField.formatValue('2017-12-12T16:00:00Z')).toBe('2017-12-12 16:00:00');
});
@ -235,7 +260,7 @@ describe('DateTimeField', () => {
});
describe('GeolocationField', () => {
const field = createField(new GeolocationFieldPropertiesDto(null, null, null, null, true, false, 'Default'));
const field = createField(new GeolocationFieldPropertiesDto({ isRequired: true }));
it('should create validators', () => {
expect(field.createValidators(false).length).toBe(1);
@ -255,7 +280,7 @@ describe('GeolocationField', () => {
});
describe('JsonField', () => {
const field = createField(new JsonFieldPropertiesDto(null, null, null, null, true, false));
const field = createField(new JsonFieldPropertiesDto({ isRequired: true }));
it('should create validators', () => {
expect(field.createValidators(false).length).toBe(1);
@ -275,7 +300,7 @@ describe('JsonField', () => {
});
describe('NumberField', () => {
const field = createField(new NumberFieldPropertiesDto(null, null, null, null, true, false, false, 'Input', undefined, 3, 1, [1, 2, 3]));
const field = createField(new NumberFieldPropertiesDto('Input', { isRequired: true, minValue: 1, maxValue: 6, allowedValues: [1, 3] }));
it('should create validators', () => {
expect(field.createValidators(false).length).toBe(4);
@ -297,7 +322,7 @@ describe('NumberField', () => {
});
describe('ReferencesField', () => {
const field = createField(new ReferencesFieldPropertiesDto(null, null, null, null, true, false, 1, 1));
const field = createField(new ReferencesFieldPropertiesDto({ isRequired: true, minItems: 1, maxItems: 5 }));
it('should create validators', () => {
expect(field.createValidators(false).length).toBe(3);
@ -321,7 +346,7 @@ describe('ReferencesField', () => {
});
describe('StringField', () => {
const field = createField(new StringFieldPropertiesDto(null, null, null, null, true, false, false, 'Input', undefined, 'pattern', undefined, 3, 1, ['1', '2']));
const field = createField(new StringFieldPropertiesDto('Input', { isRequired: true, pattern: 'pattern', minLength: 1, maxLength: 5, allowedValues: ['a', 'b'] }));
it('should create validators', () => {
expect(field.createValidators(false).length).toBe(5);
@ -342,10 +367,10 @@ describe('StringField', () => {
});
});
function createSchema(properties: SchemaPropertiesDto, index = 1, fields: FieldDto[]) {
function createSchema(properties: SchemaPropertiesDto, index = 1, fields: RootFieldDto[]) {
return new SchemaDetailsDto('id' + index, 'schema' + index, '', properties, true, null!, null!, null!, null!, null!, fields);
}
function createField(properties: FieldPropertiesDto, index = 1, partitioning = 'languages') {
return new FieldDto(index, 'field' + index, false, false, false, partitioning, properties);
return new RootFieldDto(index, 'field' + index, properties, partitioning);
}

312
src/Squidex/app/shared/services/schemas.service.spec.ts

@ -16,6 +16,8 @@ import {
CreateSchemaDto,
DateTime,
FieldDto,
NestedFieldDto,
RootFieldDto,
SchemaDetailsDto,
SchemaDto,
SchemaPropertiesDto,
@ -103,13 +105,13 @@ describe('SchemasService', () => {
]);
expect(schemas).toEqual([
new SchemaDto('id1', 'name1', 'category1', new SchemaPropertiesDto('label1', 'hints1'), true, 'Created1', 'LastModifiedBy1',
DateTime.parseISO_UTC('2016-12-12T10:10'),
DateTime.parseISO_UTC('2017-12-12T10:10'),
new SchemaDto('id1', 'name1', 'category1', new SchemaPropertiesDto('label1', 'hints1'), true,
DateTime.parseISO_UTC('2016-12-12T10:10'), 'Created1',
DateTime.parseISO_UTC('2017-12-12T10:10'), 'LastModifiedBy1',
new Version('11')),
new SchemaDto('id2', 'name2', 'category2', new SchemaPropertiesDto('label2', 'hints2'), true, 'Created2', 'LastModifiedBy2',
DateTime.parseISO_UTC('2016-10-12T10:10'),
DateTime.parseISO_UTC('2017-10-12T10:10'),
new SchemaDto('id2', 'name2', 'category2', new SchemaPropertiesDto('label2', 'hints2'), true,
DateTime.parseISO_UTC('2016-10-12T10:10'), 'Created2',
DateTime.parseISO_UTC('2017-10-12T10:10'), 'LastModifiedBy2',
new Version('22'))
]);
}));
@ -143,30 +145,50 @@ describe('SchemasService', () => {
},
fields: [
{
fieldId: 1,
name: 'field1',
fieldId: 11,
name: 'field11',
isLocked: true,
isHidden: true,
isDisabled: true,
partitioning: 'language',
properties: {
fieldType: 'Number'
}
fieldType: 'Array'
},
nested: [
{
fieldId: 101,
name: 'field101',
isHidden: true,
isDisabled: true,
properties: {
fieldType: 'String'
}
},
{
fieldId: 102,
name: 'field102',
isHidden: true,
isDisabled: true,
properties: {
fieldType: 'Number'
}
}
]
},
{
fieldId: 2,
name: 'field2',
fieldId: 12,
name: 'field12',
isLocked: true,
isHidden: true,
isDisabled: true,
partitioning: 'language',
properties: {
fieldType: 'String'
fieldType: 'Assets'
}
},
{
fieldId: 3,
name: 'field3',
fieldId: 13,
name: 'field13',
isLocked: true,
isHidden: true,
isDisabled: true,
@ -176,8 +198,8 @@ describe('SchemasService', () => {
}
},
{
fieldId: 4,
name: 'field4',
fieldId: 14,
name: 'field14',
isLocked: true,
isHidden: true,
isDisabled: true,
@ -187,41 +209,41 @@ describe('SchemasService', () => {
}
},
{
fieldId: 5,
name: 'field5',
fieldId: 15,
name: 'field15',
isLocked: true,
isHidden: true,
isDisabled: true,
partitioning: 'language',
properties: {
fieldType: 'Json'
fieldType: 'Geolocation'
}
},
{
fieldId: 6,
name: 'field6',
fieldId: 16,
name: 'field16',
isLocked: true,
isHidden: true,
isDisabled: true,
partitioning: 'language',
properties: {
fieldType: 'Geolocation'
fieldType: 'Json'
}
},
{
fieldId: 7,
name: 'field7',
fieldId: 17,
name: 'field17',
isLocked: true,
isHidden: true,
isDisabled: true,
partitioning: 'language',
properties: {
fieldType: 'Assets'
fieldType: 'Number'
}
},
{
fieldId: 8,
name: 'field8',
fieldId: 18,
name: 'field18',
isLocked: true,
isHidden: true,
isDisabled: true,
@ -231,8 +253,19 @@ describe('SchemasService', () => {
}
},
{
fieldId: 9,
name: 'field9',
fieldId: 19,
name: 'field19',
isLocked: true,
isHidden: true,
isDisabled: true,
partitioning: 'language',
properties: {
fieldType: 'String'
}
},
{
fieldId: 20,
name: 'field20',
isLocked: true,
isHidden: true,
isDisabled: true,
@ -254,20 +287,24 @@ describe('SchemasService', () => {
});
expect(schema).toEqual(
new SchemaDetailsDto('id1', 'name1', 'category1', new SchemaPropertiesDto('label1', 'hints1'), true, 'Created1', 'LastModifiedBy1',
DateTime.parseISO_UTC('2016-12-12T10:10'),
DateTime.parseISO_UTC('2017-12-12T10:10'),
new SchemaDetailsDto('id1', 'name1', 'category1', new SchemaPropertiesDto('label1', 'hints1'), true,
DateTime.parseISO_UTC('2016-12-12T10:10'), 'Created1',
DateTime.parseISO_UTC('2017-12-12T10:10'), 'LastModifiedBy1',
new Version('2'),
[
new FieldDto(1, 'field1', true, true, true, 'language', createProperties('Number')),
new FieldDto(2, 'field2', true, true, true, 'language', createProperties('String')),
new FieldDto(3, 'field3', true, true, true, 'language', createProperties('Boolean')),
new FieldDto(4, 'field4', true, true, true, 'language', createProperties('DateTime')),
new FieldDto(5, 'field5', true, true, true, 'language', createProperties('Json')),
new FieldDto(6, 'field6', true, true, true, 'language', createProperties('Geolocation')),
new FieldDto(7, 'field7', true, true, true, 'language', createProperties('Assets')),
new FieldDto(8, 'field8', true, true, true, 'language', createProperties('References')),
new FieldDto(9, 'field9', true, true, true, 'language', createProperties('Tags'))
new RootFieldDto(11, 'field11', createProperties('Array'), 'language', true, true, true, [
new NestedFieldDto(101, 'field101', createProperties('String'), 11, true, true),
new NestedFieldDto(102, 'field102', createProperties('Number'), 11, true, true)
]),
new RootFieldDto(12, 'field12', createProperties('Assets'), 'language', true, true, true),
new RootFieldDto(13, 'field13', createProperties('Boolean'), 'language', true, true, true),
new RootFieldDto(14, 'field14', createProperties('DateTime'), 'language', true, true, true),
new RootFieldDto(15, 'field15', createProperties('Geolocation'), 'language', true, true, true),
new RootFieldDto(16, 'field16', createProperties('Json'), 'language', true, true, true),
new RootFieldDto(17, 'field17', createProperties('Number'), 'language', true, true, true),
new RootFieldDto(18, 'field18', createProperties('References'), 'language', true, true, true),
new RootFieldDto(19, 'field19', createProperties('String'), 'language', true, true, true),
new RootFieldDto(20, 'field20', createProperties('Tags'), 'language', true, true, true)
],
'<script-query>',
'<script-create>',
@ -301,7 +338,52 @@ describe('SchemasService', () => {
});
expect(schema).toEqual(
new SchemaDetailsDto('1', dto.name, '', new SchemaPropertiesDto(), false, user, user, now, now, new Version('2'), []));
new SchemaDetailsDto('1', dto.name, '', new SchemaPropertiesDto(), false, now, user, now, user, new Version('2'), []));
}));
it('should make put request to update schema',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
const dto = new UpdateSchemaDto('label', 'hints');
schemasService.putSchema('my-app', 'my-schema', dto, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({});
}));
it('should make put request to update schema scripts',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
const dto = new UpdateSchemaScriptsDto();
schemasService.putScripts('my-app', 'my-schema', dto, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/scripts');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({});
}));
it('should make put request to update category',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
const dto = new UpdateSchemaCategoryDto();
schemasService.putCategory('my-app', 'my-schema', dto, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/category');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({});
}));
it('should make post request to add field',
@ -311,7 +393,7 @@ describe('SchemasService', () => {
let field: FieldDto | null = null;
schemasService.postField('my-app', 'my-schema', dto, version).subscribe(result => {
schemasService.postField('my-app', 'my-schema', dto, undefined, version).subscribe(result => {
field = result.payload;
});
@ -323,17 +405,15 @@ describe('SchemasService', () => {
req.flush({ id: 123 });
expect(field).toEqual(
new FieldDto(123, dto.name, false, false, false, dto.partitioning, dto.properties));
new RootFieldDto(123, dto.name, dto.properties, dto.partitioning));
}));
it('should make put request to update schema',
it('should make put request to publish schema',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
const dto = new UpdateSchemaDto('label', 'hints');
schemasService.putSchema('my-app', 'my-schema', dto, version).subscribe();
schemasService.publishSchema('my-app', 'my-schema', version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema');
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/publish');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
@ -341,14 +421,12 @@ describe('SchemasService', () => {
req.flush({});
}));
it('should make put request to update schema scripts',
it('should make put request to unpublish schema',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
const dto = new UpdateSchemaScriptsDto();
schemasService.putScripts('my-app', 'my-schema', dto, version).subscribe();
schemasService.unpublishSchema('my-app', 'my-schema', version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/scripts');
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/unpublish');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
@ -356,19 +434,26 @@ describe('SchemasService', () => {
req.flush({});
}));
it('should make put request to update category',
it('should make post request to add nested field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
const dto = new UpdateSchemaCategoryDto();
const dto = new AddFieldDto('name', 'invariant', createProperties('Number'));
schemasService.putCategory('my-app', 'my-schema', dto, version).subscribe();
let field: FieldDto | null = null;
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/category');
schemasService.postField('my-app', 'my-schema', dto, 13, version).subscribe(result => {
field = result.payload;
});
expect(req.request.method).toEqual('PUT');
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested');
expect(req.request.method).toEqual('POST');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({});
req.flush({ id: 123 });
expect(field).toEqual(
new NestedFieldDto(123, dto.name, dto.properties, 13));
}));
it('should make put request to update field',
@ -376,7 +461,7 @@ describe('SchemasService', () => {
const dto = new UpdateFieldDto(createProperties('Number'));
schemasService.putField('my-app', 'my-schema', 1, dto, version).subscribe();
schemasService.putField('my-app', 'my-schema', 1, dto, undefined, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1');
@ -386,12 +471,27 @@ describe('SchemasService', () => {
req.flush({});
}));
it('should make put request to update nested field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
const dto = new UpdateFieldDto(createProperties('Number'));
schemasService.putField('my-app', 'my-schema', 1, dto, 13, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/1');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({});
}));
it('should make put request to update field ordering',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
const dto = [1, 2, 3];
schemasService.putFieldOrdering('my-app', 'my-schema', dto, version).subscribe();
schemasService.putFieldOrdering('my-app', 'my-schema', dto, undefined, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/ordering');
@ -401,12 +501,14 @@ describe('SchemasService', () => {
req.flush({});
}));
it('should make put request to publish schema',
it('should make put request to update nested field ordering',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.publishSchema('my-app', 'my-schema', version).subscribe();
const dto = [1, 2, 3];
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/publish');
schemasService.putFieldOrdering('my-app', 'my-schema', dto, 13, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/ordering');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
@ -414,12 +516,12 @@ describe('SchemasService', () => {
req.flush({});
}));
it('should make put request to unpublish schema',
it('should make put request to enable field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.unpublishSchema('my-app', 'my-schema', version).subscribe();
schemasService.enableField('my-app', 'my-schema', 1, undefined, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/unpublish');
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/enable');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
@ -427,12 +529,12 @@ describe('SchemasService', () => {
req.flush({});
}));
it('should make put request to enable field',
it('should make put request to enable nested field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.enableField('my-app', 'my-schema', 1, version).subscribe();
schemasService.enableField('my-app', 'my-schema', 1, 13, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/enable');
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/1/enable');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
@ -443,7 +545,7 @@ describe('SchemasService', () => {
it('should make put request to disable field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.disableField('my-app', 'my-schema', 1, version).subscribe();
schemasService.disableField('my-app', 'my-schema', 1, undefined, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/disable');
@ -453,12 +555,12 @@ describe('SchemasService', () => {
req.flush({});
}));
it('should make put request to lock field',
it('should make put request to disable nested field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.lockField('my-app', 'my-schema', 1, version).subscribe();
schemasService.disableField('my-app', 'my-schema', 1, 13, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/lock');
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/1/disable');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
@ -469,7 +571,7 @@ describe('SchemasService', () => {
it('should make put request to show field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.showField('my-app', 'my-schema', 1, version).subscribe();
schemasService.showField('my-app', 'my-schema', 1, undefined, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/show');
@ -479,10 +581,23 @@ describe('SchemasService', () => {
req.flush({});
}));
it('should make put request to show nested field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.showField('my-app', 'my-schema', 1, 13, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/1/show');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({});
}));
it('should make put request to hide field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.hideField('my-app', 'my-schema', 1, version).subscribe();
schemasService.hideField('my-app', 'my-schema', 1, undefined, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/hide');
@ -492,10 +607,23 @@ describe('SchemasService', () => {
req.flush({});
}));
it('should make put request to hide nested field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.hideField('my-app', 'my-schema', 1, 13, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/1/hide');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({});
}));
it('should make delete request to delete field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.deleteField('my-app', 'my-schema', 1, version).subscribe();
schemasService.deleteField('my-app', 'my-schema', 1, undefined, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1');
@ -505,6 +633,19 @@ describe('SchemasService', () => {
req.flush({});
}));
it('should make delete request to delete nested field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.deleteField('my-app', 'my-schema', 1, 13, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/1');
expect(req.request.method).toEqual('DELETE');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({});
}));
it('should make delete request to delete schema',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
@ -517,4 +658,17 @@ describe('SchemasService', () => {
req.flush({});
}));
it('should make put request to lock field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.lockField('my-app', 'my-schema', 1, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/lock');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({});
}));
});

664
src/Squidex/app/shared/services/schemas.service.ts

@ -17,6 +17,7 @@ import {
ApiUrlConfig,
DateTime,
HTTP,
Model,
StringHelper,
ValidatorsEx,
Version,
@ -60,32 +61,35 @@ export function createProperties(fieldType: string, values: Object | null = null
let properties: FieldPropertiesDto;
switch (fieldType) {
case 'Number':
properties = new NumberFieldPropertiesDto(null, null, null, null, false, false, false, 'Input');
case 'Array':
properties = new ArrayFieldPropertiesDto();
break;
case 'String':
properties = new StringFieldPropertiesDto(null, null, null, null, false, false, false, 'Input');
case 'Assets':
properties = new AssetsFieldPropertiesDto();
break;
case 'Boolean':
properties = new BooleanFieldPropertiesDto(null, null, null, null, false, false, false, 'Checkbox');
properties = new BooleanFieldPropertiesDto('Checkbox');
break;
case 'DateTime':
properties = new DateTimeFieldPropertiesDto(null, null, null, null, false, false, 'DateTime');
properties = new DateTimeFieldPropertiesDto('DateTime');
break;
case 'Geolocation':
properties = new GeolocationFieldPropertiesDto(null, null, null, null, false, false, 'Map');
properties = new GeolocationFieldPropertiesDto();
break;
case 'Json':
properties = new JsonFieldPropertiesDto(null, null, null, null, false, false);
properties = new JsonFieldPropertiesDto();
break;
case 'Number':
properties = new NumberFieldPropertiesDto('Input');
break;
case 'References':
properties = new ReferencesFieldPropertiesDto(null, null, null, null, false, false);
properties = new ReferencesFieldPropertiesDto();
break;
case 'Assets':
properties = new AssetsFieldPropertiesDto(null, null, null, null, false, false);
case 'String':
properties = new StringFieldPropertiesDto('Input');
break;
case 'Tags':
properties = new TagsFieldPropertiesDto(null, null, null, null, false, false);
properties = new TagsFieldPropertiesDto();
break;
default:
throw 'Invalid properties type';
@ -98,8 +102,8 @@ export function createProperties(fieldType: string, values: Object | null = null
return properties;
}
export class SchemaDto {
public readonly displayName = StringHelper.firstNonEmpty(this.properties.label, this.name);
export class SchemaDto extends Model {
public displayName: string;
constructor(
public readonly id: string,
@ -107,27 +111,42 @@ export class SchemaDto {
public readonly category: string,
public readonly properties: SchemaPropertiesDto,
public readonly isPublished: boolean,
public readonly createdBy: string,
public readonly lastModifiedBy: string,
public readonly created: DateTime,
public readonly createdBy: string,
public readonly lastModified: DateTime,
public readonly lastModifiedBy: string,
public readonly version: Version
) {
super();
}
public onCreated() {
this.displayName = StringHelper.firstNonEmpty(this.properties.label, this.name);
}
public with(value: Partial<SchemaDto>): SchemaDto {
return this.clone(value);
}
}
export class SchemaDetailsDto extends SchemaDto {
public readonly listFields: FieldDto[];
public listFields: RootFieldDto[];
constructor(id: string, name: string, category: string, properties: SchemaPropertiesDto, isPublished: boolean, createdBy: string, lastModifiedBy: string, created: DateTime, lastModified: DateTime, version: Version,
public readonly fields: FieldDto[],
constructor(id: string, name: string, category: string, properties: SchemaPropertiesDto, isPublished: boolean, created: DateTime, createdBy: string, lastModified: DateTime, lastModifiedBy: string, version: Version,
public readonly fields: RootFieldDto[],
public readonly scriptQuery?: string,
public readonly scriptCreate?: string,
public readonly scriptUpdate?: string,
public readonly scriptDelete?: string,
public readonly scriptChange?: string
) {
super(id, name, category, properties, isPublished, createdBy, lastModifiedBy, created, lastModified, version);
super(id, name, category, properties, isPublished, created, createdBy, lastModified, lastModifiedBy, version);
this.onCreated();
}
public onCreated() {
super.onCreated();
this.listFields = this.fields.filter(x => x.properties.isListField);
@ -139,23 +158,27 @@ export class SchemaDetailsDto extends SchemaDto {
this.listFields = [<any>{ properties: {} }];
}
}
}
export class FieldDto {
public readonly displayName = StringHelper.firstNonEmpty(this.properties.label, this.name);
public readonly displayPlaceholder = this.properties.placeholder || '';
public with(value: Partial<SchemaDetailsDto>): SchemaDetailsDto {
return this.clone(value);
}
}
public readonly isLocalizable = this.partitioning !== 'invariant';
export class FieldDto extends Model {
public displayName: string;
public displayPlaceholder: string;
constructor(
public readonly fieldId: number,
public readonly name: string,
public readonly isLocked: boolean,
public readonly isHidden: boolean,
public readonly isDisabled: boolean,
public readonly partitioning: string,
public readonly properties: FieldPropertiesDto
) {
super();
}
public onCreated() {
this.displayName = StringHelper.firstNonEmpty(this.properties.label, this.name);
this.displayPlaceholder = this.properties.placeholder || '';
}
public formatValue(value: any): string {
@ -169,18 +192,66 @@ export class FieldDto {
public defaultValue(): any {
return this.properties.getDefaultValue();
}
public with(value: Partial<FieldDto>): FieldDto {
return this.clone(value);
}
}
export class RootFieldDto extends FieldDto {
public readonly isLocalizable = this.partitioning === 'language';
constructor(fieldId: number, name: string, properties: FieldPropertiesDto,
public readonly partitioning: string,
public readonly isHidden: boolean = false,
public readonly isDisabled: boolean = false,
public readonly isLocked: boolean = false,
public readonly nested: NestedFieldDto[] = []
) {
super(fieldId, name, properties);
this.onCreated();
}
public with(value: Partial<RootFieldDto>): RootFieldDto {
return this.clone(value);
}
}
export class NestedFieldDto extends FieldDto {
constructor(fieldId: number, name: string, properties: FieldPropertiesDto,
public readonly parentId: number,
public readonly isHidden: boolean = false,
public readonly isDisabled: boolean = false
) {
super(fieldId, name, properties);
this.onCreated();
}
public with(value: Partial<NestedFieldDto>): NestedFieldDto {
return this.clone(value);
}
}
export type AnyFieldDto = RootFieldDto | NestedFieldDto;
export abstract class FieldPropertiesDto {
constructor(
public readonly fieldType: string,
public readonly label: string | null,
public readonly hints: string | null,
public readonly placeholder: string | null,
public readonly editorUrl: string | null,
public readonly isRequired: boolean,
public readonly isListField: boolean
public abstract fieldType: string;
public readonly editorUrl?: string;
public readonly label?: string;
public readonly hints?: string;
public readonly placeholder?: string;
public readonly isRequired: boolean = false;
public readonly isListField: boolean = false;
constructor(public readonly editor: string,
props?: Partial<FieldPropertiesDto>
) {
if (props) {
Object.assign(this, props);
}
}
public abstract formatValue(value: any): string;
@ -192,20 +263,16 @@ export abstract class FieldPropertiesDto {
}
}
export class StringFieldPropertiesDto extends FieldPropertiesDto {
constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null,
isRequired: boolean,
isListField: boolean,
public readonly inlineEditable: boolean,
public readonly editor: string,
public readonly defaultValue?: string,
public readonly pattern?: string,
public readonly patternMessage?: string,
public readonly minLength?: number,
public readonly maxLength?: number,
public readonly allowedValues?: string[]
export class ArrayFieldPropertiesDto extends FieldPropertiesDto {
public readonly fieldType = 'Array';
public readonly minItems?: number;
public readonly maxItems?: number;
constructor(
props?: Partial<ArrayFieldPropertiesDto>
) {
super('String', label, hints, placeholder, editorUrl, isRequired, isListField);
super('Default', props);
}
public formatValue(value: any): string {
@ -213,58 +280,52 @@ export class StringFieldPropertiesDto extends FieldPropertiesDto {
return '';
}
return value;
if (value.length) {
return `${value.length} Items(s)`;
} else {
return '0 Items';
}
}
public createValidators(isOptional: false): ValidatorFn[] {
public createValidators(isOptional: boolean): ValidatorFn[] {
const validators: ValidatorFn[] = [];
if (this.isRequired && !isOptional) {
validators.push(Validators.required);
}
if (this.minLength) {
validators.push(Validators.minLength(this.minLength));
}
if (this.maxLength) {
validators.push(Validators.maxLength(this.maxLength));
}
if (this.pattern && this.pattern.length > 0) {
validators.push(ValidatorsEx.pattern(this.pattern, this.patternMessage));
if (this.minItems) {
validators.push(Validators.minLength(this.minItems));
}
if (this.allowedValues && this.allowedValues.length > 0) {
const values: (string | null)[] = this.allowedValues;
if (this.isRequired && !isOptional) {
validators.push(ValidatorsEx.validValues(values));
} else {
validators.push(ValidatorsEx.validValues(values.concat([null])));
}
if (this.maxItems) {
validators.push(Validators.maxLength(this.maxItems));
}
return validators;
}
public getDefaultValue(): any {
return this.defaultValue;
}
}
export class NumberFieldPropertiesDto extends FieldPropertiesDto {
constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null,
isRequired: boolean,
isListField: boolean,
public readonly inlineEditable: boolean,
public readonly editor: string,
public readonly defaultValue?: number,
public readonly maxValue?: number,
public readonly minValue?: number,
public readonly allowedValues?: number[]
export class AssetsFieldPropertiesDto extends FieldPropertiesDto {
public readonly fieldType = 'Assets';
public readonly minItems?: number;
public readonly maxItems?: number;
public readonly minSize?: number;
public readonly maxSize?: number;
public readonly allowedExtensions?: string[];
public readonly mustBeImage?: boolean;
public readonly minWidth?: number;
public readonly maxWidth?: number;
public readonly minHeight?: number;
public readonly maxHeight?: number;
public readonly aspectWidth?: number;
public readonly aspectHeight?: number;
constructor(
props?: Partial<AssetsFieldPropertiesDto>
) {
super('Number', label, hints, placeholder, editorUrl, isRequired, isListField);
super('Default', props);
}
public formatValue(value: any): string {
@ -272,7 +333,11 @@ export class NumberFieldPropertiesDto extends FieldPropertiesDto {
return '';
}
return value;
if (value.length) {
return `${value.length} Asset(s)`;
} else {
return '0 Assets';
}
}
public createValidators(isOptional: boolean): ValidatorFn[] {
@ -282,22 +347,43 @@ export class NumberFieldPropertiesDto extends FieldPropertiesDto {
validators.push(Validators.required);
}
if (this.minValue) {
validators.push(Validators.min(this.minValue));
if (this.minItems) {
validators.push(Validators.minLength(this.minItems));
}
if (this.maxValue) {
validators.push(Validators.max(this.maxValue));
if (this.maxItems) {
validators.push(Validators.maxLength(this.maxItems));
}
if (this.allowedValues && this.allowedValues.length > 0) {
const values: (number | null)[] = this.allowedValues;
return validators;
}
}
if (this.isRequired && !isOptional) {
validators.push(ValidatorsEx.validValues(values));
} else {
validators.push(ValidatorsEx.validValues(values.concat([null])));
}
export class BooleanFieldPropertiesDto extends FieldPropertiesDto {
public readonly fieldType = 'Boolean';
public readonly inlineEditable: boolean = false;
public readonly defaultValue?: boolean;
constructor(editor: string,
props?: Partial<BooleanFieldPropertiesDto>
) {
super(editor, props);
}
public formatValue(value: any): string {
if (value === null || value === undefined) {
return '';
}
return value ? 'Yes' : 'No';
}
public createValidators(isOptional: boolean): ValidatorFn[] {
const validators: ValidatorFn[] = [];
if (this.isRequired && !isOptional) {
validators.push(Validators.required);
}
return validators;
@ -309,16 +395,17 @@ export class NumberFieldPropertiesDto extends FieldPropertiesDto {
}
export class DateTimeFieldPropertiesDto extends FieldPropertiesDto {
constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null,
isRequired: boolean,
isListField: boolean,
public readonly editor: string,
public readonly defaultValue?: string,
public readonly maxValue?: string,
public readonly minValue?: string,
public readonly calculatedDefaultValue?: string
public readonly fieldType = 'DateTime';
public readonly defaultValue?: string;
public readonly maxValue?: string;
public readonly minValue?: string;
public readonly calculatedDefaultValue?: string;
constructor(editor: string,
props?: Partial<DateTimeFieldPropertiesDto>
) {
super('DateTime', label, hints, placeholder, editorUrl, isRequired, isListField);
super(editor, props);
}
public formatValue(value: any): string {
@ -362,23 +449,21 @@ export class DateTimeFieldPropertiesDto extends FieldPropertiesDto {
}
}
export class BooleanFieldPropertiesDto extends FieldPropertiesDto {
constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null,
isRequired: boolean,
isListField: boolean,
public readonly inlineEditable: boolean,
public readonly editor: string,
public readonly defaultValue?: boolean
export class GeolocationFieldPropertiesDto extends FieldPropertiesDto {
public readonly fieldType = 'Geolocation';
constructor(
props?: Partial<GeolocationFieldPropertiesDto>
) {
super('Boolean', label, hints, placeholder, editorUrl, isRequired, isListField);
super('Default', props);
}
public formatValue(value: any): string {
if (value === null || value === undefined) {
if (!value) {
return '';
}
return value ? 'Yes' : 'No';
return `${value.longitude}, ${value.latitude}`;
}
public createValidators(isOptional: boolean): ValidatorFn[] {
@ -390,19 +475,15 @@ export class BooleanFieldPropertiesDto extends FieldPropertiesDto {
return validators;
}
public getDefaultValue(): any {
return this.defaultValue;
}
}
export class GeolocationFieldPropertiesDto extends FieldPropertiesDto {
constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null,
isRequired: boolean,
isListField: boolean,
public readonly editor: string
export class JsonFieldPropertiesDto extends FieldPropertiesDto {
public readonly fieldType = 'Json';
constructor(
props?: Partial<JsonFieldPropertiesDto>
) {
super('Geolocation', label, hints, placeholder, editorUrl, isRequired, isListField);
super('Default', props);
}
public formatValue(value: any): string {
@ -410,7 +491,7 @@ export class GeolocationFieldPropertiesDto extends FieldPropertiesDto {
return '';
}
return `${value.longitude}, ${value.latitude}`;
return '<Json />';
}
public createValidators(isOptional: boolean): ValidatorFn[] {
@ -424,15 +505,19 @@ export class GeolocationFieldPropertiesDto extends FieldPropertiesDto {
}
}
export class ReferencesFieldPropertiesDto extends FieldPropertiesDto {
constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null,
isRequired: boolean,
isListField: boolean,
public readonly minItems?: number,
public readonly maxItems?: number,
public readonly schemaId?: string
export class NumberFieldPropertiesDto extends FieldPropertiesDto {
public readonly fieldType = 'Number';
public readonly inlineEditable: boolean = false;
public readonly defaultValue?: number;
public readonly maxValue?: number;
public readonly minValue?: number;
public readonly allowedValues?: number[];
constructor(editor: string,
props?: Partial<NumberFieldPropertiesDto>
) {
super('References', label, hints, placeholder, editorUrl, isRequired, isListField);
super(editor, props);
}
public formatValue(value: any): string {
@ -440,11 +525,7 @@ export class ReferencesFieldPropertiesDto extends FieldPropertiesDto {
return '';
}
if (value.length) {
return `${value.length} Reference(s)`;
} else {
return '0 References';
}
return value;
}
public createValidators(isOptional: boolean): ValidatorFn[] {
@ -454,36 +535,43 @@ export class ReferencesFieldPropertiesDto extends FieldPropertiesDto {
validators.push(Validators.required);
}
if (this.minItems) {
validators.push(Validators.minLength(this.minItems));
if (this.minValue) {
validators.push(Validators.min(this.minValue));
}
if (this.maxItems) {
validators.push(Validators.maxLength(this.maxItems));
if (this.maxValue) {
validators.push(Validators.max(this.maxValue));
}
if (this.allowedValues && this.allowedValues.length > 0) {
const values: (number | null)[] = this.allowedValues;
if (this.isRequired && !isOptional) {
validators.push(ValidatorsEx.validValues(values));
} else {
validators.push(ValidatorsEx.validValues(values.concat([null])));
}
}
return validators;
}
public getDefaultValue(): any {
return this.defaultValue;
}
}
export class AssetsFieldPropertiesDto extends FieldPropertiesDto {
constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null,
isRequired: boolean,
isListField: boolean,
public readonly minItems?: number,
public readonly maxItems?: number,
public readonly minSize?: number,
public readonly maxSize?: number,
public readonly allowedExtensions?: string[],
public readonly mustBeImage?: boolean,
public readonly minWidth?: number,
public readonly maxWidth?: number,
public readonly minHeight?: number,
public readonly maxHeight?: number,
public readonly aspectWidth?: number,
public readonly aspectHeight?: number
export class ReferencesFieldPropertiesDto extends FieldPropertiesDto {
public readonly fieldType = 'References';
public readonly minItems?: number;
public readonly maxItems?: number;
public readonly schemaId?: string;
constructor(
props?: Partial<ReferencesFieldPropertiesDto>
) {
super('Assets', label, hints, placeholder, editorUrl, isRequired, isListField);
super('Default', props);
}
public formatValue(value: any): string {
@ -492,9 +580,9 @@ export class AssetsFieldPropertiesDto extends FieldPropertiesDto {
}
if (value.length) {
return `${value.length} Asset(s)`;
return `${value.length} Reference(s)`;
} else {
return '0 Assets';
return '0 References';
}
}
@ -517,14 +605,21 @@ export class AssetsFieldPropertiesDto extends FieldPropertiesDto {
}
}
export class TagsFieldPropertiesDto extends FieldPropertiesDto {
constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null,
isRequired: boolean,
isListField: boolean,
public readonly minItems?: number,
public readonly maxItems?: number
export class StringFieldPropertiesDto extends FieldPropertiesDto {
public readonly fieldType = 'String';
public readonly inlineEditable = false;
public readonly defaultValue?: string;
public readonly pattern?: string;
public readonly patternMessage?: string;
public readonly minLength?: number;
public readonly maxLength?: number;
public readonly allowedValues?: string[];
constructor(editor: string,
props?: Partial<StringFieldPropertiesDto>
) {
super('Tags', label, hints, placeholder, editorUrl, isRequired, isListField);
super(editor, props);
}
public formatValue(value: any): string {
@ -532,38 +627,56 @@ export class TagsFieldPropertiesDto extends FieldPropertiesDto {
return '';
}
if (value.length) {
return value.join(', ');
} else {
return '';
}
return value;
}
public createValidators(isOptional: boolean): ValidatorFn[] {
public createValidators(isOptional: false): ValidatorFn[] {
const validators: ValidatorFn[] = [];
if (this.isRequired && !isOptional) {
validators.push(Validators.required);
}
if (this.minItems) {
validators.push(Validators.minLength(this.minItems));
if (this.minLength) {
validators.push(Validators.minLength(this.minLength));
}
if (this.maxItems) {
validators.push(Validators.maxLength(this.maxItems));
if (this.maxLength) {
validators.push(Validators.maxLength(this.maxLength));
}
if (this.pattern && this.pattern.length > 0) {
validators.push(ValidatorsEx.pattern(this.pattern, this.patternMessage));
}
if (this.allowedValues && this.allowedValues.length > 0) {
const values: (string | null)[] = this.allowedValues;
if (this.isRequired && !isOptional) {
validators.push(ValidatorsEx.validValues(values));
} else {
validators.push(ValidatorsEx.validValues(values.concat([null])));
}
}
return validators;
}
public getDefaultValue(): any {
return this.defaultValue;
}
}
export class JsonFieldPropertiesDto extends FieldPropertiesDto {
constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null,
isRequired: boolean,
isListField: boolean
export class TagsFieldPropertiesDto extends FieldPropertiesDto {
public readonly fieldType = 'Tags';
public readonly minItems?: number;
public readonly maxItems?: number;
constructor(
props?: Partial<TagsFieldPropertiesDto>
) {
super('Json', label, hints, placeholder, editorUrl, isRequired, isListField);
super('Default', props);
}
public formatValue(value: any): string {
@ -571,7 +684,11 @@ export class JsonFieldPropertiesDto extends FieldPropertiesDto {
return '';
}
return '<Json />';
if (value.length) {
return value.join(', ');
} else {
return '';
}
}
public createValidators(isOptional: boolean): ValidatorFn[] {
@ -581,6 +698,14 @@ export class JsonFieldPropertiesDto extends FieldPropertiesDto {
validators.push(Validators.required);
}
if (this.minItems) {
validators.push(Validators.minLength(this.minItems));
}
if (this.maxItems) {
validators.push(Validators.maxLength(this.maxItems));
}
return validators;
}
}
@ -627,7 +752,7 @@ export class UpdateFieldDto {
export class CreateSchemaDto {
constructor(
public readonly name: string,
public readonly fields?: FieldDto[],
public readonly fields?: RootFieldDto[],
public readonly properties?: SchemaPropertiesDto
) {
}
@ -668,13 +793,10 @@ export class SchemasService {
return new SchemaDto(
item.id,
item.name,
item.category,
properties,
item.category, properties,
item.isPublished,
item.createdBy,
item.lastModifiedBy,
DateTime.parseISO_UTC(item.created),
DateTime.parseISO_UTC(item.lastModified),
DateTime.parseISO_UTC(item.created), item.createdBy,
DateTime.parseISO_UTC(item.lastModified), item.lastModifiedBy,
new Version(item.version.toString()));
});
})
@ -694,14 +816,34 @@ export class SchemasService {
item.properties.fieldType,
item.properties);
return new FieldDto(
let nested: NestedFieldDto[] | null = null;
if (item.nested && item.nested.length > 0) {
nested = item.nested.map((nestedItem: any) => {
const nestedPropertiesDto =
createProperties(
nestedItem.properties.fieldType,
nestedItem.properties);
return new NestedFieldDto(
nestedItem.fieldId,
nestedItem.name,
nestedPropertiesDto,
item.fieldId,
nestedItem.isHidden,
nestedItem.isDisabled);
});
}
return new RootFieldDto(
item.fieldId,
item.name,
item.isLocked,
propertiesDto,
item.partitioning,
item.isHidden,
item.isDisabled,
item.partitioning,
propertiesDto);
item.isLocked,
nested || []);
});
const properties = new SchemaPropertiesDto(body.properties.label, body.properties.hints);
@ -712,10 +854,8 @@ export class SchemasService {
body.category,
properties,
body.isPublished,
body.createdBy,
body.lastModifiedBy,
DateTime.parseISO_UTC(body.created),
DateTime.parseISO_UTC(body.lastModified),
DateTime.parseISO_UTC(body.created), body.createdBy,
DateTime.parseISO_UTC(body.lastModified), body.lastModifiedBy,
response.version,
fields,
body.scriptQuery,
@ -742,10 +882,8 @@ export class SchemasService {
'',
dto.properties || new SchemaPropertiesDto(),
false,
user,
user,
now,
now,
now, user,
now, user,
response.version,
dto.fields || [],
body.scriptQuery,
@ -760,30 +898,6 @@ export class SchemasService {
.pretifyError('Failed to create schema. Please reload.');
}
public postField(appName: string, schemaName: string, dto: AddFieldDto, version: Version): Observable<Versioned<FieldDto>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields`);
return HTTP.postVersioned<any>(this.http, url, dto, version)
.map(response => {
const body = response.payload.body;
const field = new FieldDto(
body.id,
dto.name,
false,
false,
false,
dto.partitioning,
dto.properties);
return new Versioned(response.version, field);
})
.do(() => {
this.analytics.trackEvent('Schema', 'FieldCreated', appName);
})
.pretifyError('Failed to add field. Please reload.');
}
public deleteSchema(appName: string, schemaName: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}`);
@ -814,16 +928,6 @@ export class SchemasService {
.pretifyError('Failed to update schema. Please reload.');
}
public putFieldOrdering(appName: string, schemaName: string, dto: number[], version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/ordering`);
return HTTP.putVersioned(this.http, url, { fieldIds: dto }, version)
.do(() => {
this.analytics.trackEvent('Schema', 'FieldsReordered', appName);
})
.pretifyError('Failed to reorder fields. Please reload.');
}
public publishSchema(appName: string, schemaName: string, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/publish`);
@ -854,8 +958,41 @@ export class SchemasService {
.pretifyError('Failed to change category. Please reload.');
}
public putField(appName: string, schemaName: string, fieldId: number, dto: UpdateFieldDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}`);
public postField(appName: string, schemaName: string, dto: AddFieldDto, parentId: number | undefined, version: Version): Observable<Versioned<AnyFieldDto>> {
const url = this.buildUrl(appName, schemaName, parentId, '');
return HTTP.postVersioned<any>(this.http, url, dto, version)
.map(response => {
const body = response.payload.body;
if (parentId) {
const field = new NestedFieldDto(body.id, dto.name, dto.properties, parentId);
return new Versioned(response.version, field);
} else {
const field = new RootFieldDto(body.id, dto.name, dto.properties, dto.partitioning);
return new Versioned(response.version, field);
}
})
.do(() => {
this.analytics.trackEvent('Schema', 'FieldCreated', appName);
})
.pretifyError('Failed to add field. Please reload.');
}
public putFieldOrdering(appName: string, schemaName: string, dto: number[], parentId: number | undefined, version: Version): Observable<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, '/ordering');
return HTTP.putVersioned(this.http, url, { fieldIds: dto }, version)
.do(() => {
this.analytics.trackEvent('Schema', 'FieldsReordered', appName);
})
.pretifyError('Failed to reorder fields. Please reload.');
}
public putField(appName: string, schemaName: string, fieldId: number, dto: UpdateFieldDto, parentId: number | undefined, version: Version): Observable<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}`);
return HTTP.putVersioned(this.http, url, dto, version)
.do(() => {
@ -864,38 +1001,38 @@ export class SchemasService {
.pretifyError('Failed to update field. Please reload.');
}
public enableField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/enable`);
public lockField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/lock`);
return HTTP.putVersioned(this.http, url, {}, version)
.do(() => {
this.analytics.trackEvent('Schema', 'FieldEnabled', appName);
this.analytics.trackEvent('Schema', 'FieldLocked', appName);
})
.pretifyError('Failed to enable field. Please reload.');
.pretifyError('Failed to lock field. Please reload.');
}
public disableField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/disable`);
public enableField(appName: string, schemaName: string, fieldId: number, parentId: number | undefined, version: Version): Observable<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/enable`);
return HTTP.putVersioned(this.http, url, {}, version)
.do(() => {
this.analytics.trackEvent('Schema', 'FieldDisabled', appName);
this.analytics.trackEvent('Schema', 'FieldEnabled', appName);
})
.pretifyError('Failed to disable field. Please reload.');
.pretifyError('Failed to enable field. Please reload.');
}
public lockField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/lock`);
public disableField(appName: string, schemaName: string, fieldId: number, parentId: number | undefined, version: Version): Observable<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/disable`);
return HTTP.putVersioned(this.http, url, {}, version)
.do(() => {
this.analytics.trackEvent('Schema', 'FieldLocked', appName);
this.analytics.trackEvent('Schema', 'FieldDisabled', appName);
})
.pretifyError('Failed to lock field. Please reload.');
.pretifyError('Failed to disable field. Please reload.');
}
public showField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/show`);
public showField(appName: string, schemaName: string, fieldId: number, parentId: number | undefined, version: Version): Observable<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/show`);
return HTTP.putVersioned(this.http, url, {}, version)
.do(() => {
@ -904,8 +1041,8 @@ export class SchemasService {
.pretifyError('Failed to show field. Please reload.');
}
public hideField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/hide`);
public hideField(appName: string, schemaName: string, fieldId: number, parentId: number | undefined, version: Version): Observable<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/hide`);
return HTTP.putVersioned(this.http, url, {}, version)
.do(() => {
@ -914,8 +1051,8 @@ export class SchemasService {
.pretifyError('Failed to hide field. Please reload.');
}
public deleteField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}`);
public deleteField(appName: string, schemaName: string, fieldId: number, parentId: number | undefined, version: Version): Observable<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}`);
return HTTP.deleteVersioned(this.http, url, version)
.do(() => {
@ -923,4 +1060,13 @@ export class SchemasService {
})
.pretifyError('Failed to delete field. Please reload.');
}
private buildUrl(appName: string, schemaName: string, parentId: number | undefined, suffix: string) {
const url =
parentId ?
this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${parentId}/nested${suffix}`) :
this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields${suffix}`);
return url;
}
}

2
src/Squidex/app/shared/state/clients.state.ts

@ -149,4 +149,4 @@ export class ClientsState extends State<Snapshot> {
}
const update = (client: AppClientDto, request: UpdateAppClientDto) =>
new AppClientDto(client.id, request.name || client.name, client.secret, request.permission || client.permission);
client.with({ name: request.name || client.name, permission: request.permission || client.permission });

101
src/Squidex/app/shared/state/contents.state.ts

@ -439,74 +439,55 @@ export class ManualContentsState extends ContentsStateBase {
}
}
const changeScheduleStatus = (content: ContentDto, status: string, dueTime: string, user: string, version: Version, now?: DateTime) =>
content.with({
scheduleJob: new ScheduleDto(status, user, DateTime.parseISO_UTC(dueTime)),
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
const changeStatus = (content: ContentDto, status: string, user: string, version: Version, now?: DateTime) =>
new ContentDto(
content.id,
content.with({
status,
content.createdBy, user,
content.created, now || DateTime.now(),
null,
content.isPending,
content.data,
content.dataDraft,
version);
const changeScheduleStatus = (content: ContentDto, status: string, dueTime: string, user: string, version: Version, now?: DateTime) =>
new ContentDto(
content.id,
content.status,
content.createdBy, user,
content.created, now || DateTime.now(),
new ScheduleDto(status, user, DateTime.parseISO_UTC(dueTime)),
content.isPending,
content.data,
content.dataDraft,
version);
scheduleJob: null,
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
const updateData = (content: ContentDto, data: any, user: string, version: Version, now?: DateTime) =>
new ContentDto(
content.id,
content.status,
content.createdBy, user,
content.created, now || DateTime.now(),
content.scheduleJob,
content.isPending,
data,
content.with({
data,
version);
dataDraft: data,
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
const updateDataDraft = (content: ContentDto, data: any, user: string, version: Version, now?: DateTime) =>
new ContentDto(
content.id,
content.status,
content.createdBy, user,
content.created, now || DateTime.now(),
content.scheduleJob,
true,
content.data,
data,
version);
content.with({
isPending: true,
dataDraft: data,
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
const confirmChanges = (content: ContentDto, user: string, version: Version, now?: DateTime) =>
new ContentDto(
content.id,
content.status,
content.createdBy, user,
content.created, now || DateTime.now(),
null,
false,
content.dataDraft,
content.dataDraft,
version);
content.with({
isPending: false,
data: content.dataDraft,
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
const discardChanges = (content: ContentDto, user: string, version: Version, now?: DateTime) =>
new ContentDto(
content.id,
content.status,
content.createdBy, user,
content.created, now || DateTime.now(),
content.scheduleJob,
false,
content.data,
content.data,
version);
content.with({
isPending: false,
dataDraft: content.data,
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});

44
src/Squidex/app/shared/state/rules.state.ts

@ -147,37 +147,27 @@ export class RulesState extends State<Snapshot> {
}
const updateTrigger = (rule: RuleDto, trigger: any, user: string, version: Version, now?: DateTime) =>
new RuleDto(
rule.id,
rule.createdBy, user,
rule.created, now || DateTime.now(),
version,
rule.isEnabled,
rule.with({
trigger,
trigger.triggerType,
rule.action,
rule.action.actionType);
triggerType: trigger.triggerType,
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
const updateAction = (rule: RuleDto, action: any, user: string, version: Version, now?: DateTime) =>
new RuleDto(
rule.id,
rule.createdBy, user,
rule.created, now || DateTime.now(),
version,
rule.isEnabled,
rule.trigger,
rule.trigger.triggerType,
rule.with({
action,
action.actionType);
actionType: action.triggerType,
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
const setEnabled = (rule: RuleDto, isEnabled: boolean, user: string, version: Version, now?: DateTime) =>
new RuleDto(
rule.id,
rule.createdBy, user,
rule.created, now || DateTime.now(),
version,
rule.with({
isEnabled,
rule.trigger,
rule.triggerType,
rule.action,
rule.actionType);
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});

46
src/Squidex/app/shared/state/schemas.state.spec.ts

@ -18,7 +18,8 @@ import {
CreateSchemaDto,
DateTime,
DialogService,
FieldDto,
NestedFieldDto,
RootFieldDto,
SchemaDetailsDto,
SchemaDto,
SchemasService,
@ -40,17 +41,20 @@ describe('SchemasState', () => {
const newVersion = new Version('2');
const oldSchemas = [
new SchemaDto('id1', 'name1', 'category1', {}, false, creator, creator, creation, creation, version),
new SchemaDto('id2', 'name2', 'category2', {}, true , creator, creator, creation, creation, version)
new SchemaDto('id1', 'name1', 'category1', {}, false, creation, creator, creation, creator, version),
new SchemaDto('id2', 'name2', 'category2', {}, true , creation, creator, creation, creator, version)
];
const field1 = new FieldDto(1, '1', false, false, false, 'l', createProperties('String'));
const field2 = new FieldDto(2, '2', true, true, true, 'l', createProperties('Number'));
const field1 = new RootFieldDto(1, '1', createProperties('String'), 'invariant');
const field2 = new RootFieldDto(2, '2', createProperties('Array'), 'invariant', false, false, false, [
new NestedFieldDto(3, '3', createProperties('Number'), 2),
new NestedFieldDto(4, '4', createProperties('String'), 2)
]);
const schema =
new SchemaDetailsDto('id2', 'name2', 'category2', {}, true,
creator, creator,
creation, creation,
creation, creator,
creation, creator,
version,
[field1, field2]);
@ -277,7 +281,7 @@ describe('SchemasState', () => {
it('should add schema to snapshot when created', () => {
const request = new CreateSchemaDto('newName');
const result = new SchemaDetailsDto('id4', 'newName', '', {}, false, modifier, modifier, modified, modified, version, []);
const result = new SchemaDetailsDto('id4', 'newName', '', {}, false, modified, modifier, modified, modifier, version, []);
schemasService.setup(x => x.postSchema(app, request, modifier, modified))
.returns(() => Observable.of(result));
@ -301,12 +305,12 @@ describe('SchemasState', () => {
it('should add field and update user info when field added', () => {
const request = new AddFieldDto(field1.name, field1.partitioning, field1.properties);
const newField = new FieldDto(3, '3', false, false, false, 'l', createProperties('String'));
const newField = new RootFieldDto(3, '3', createProperties('String'), 'invariant');
schemasService.setup(x => x.postField(app, schema.name, It.isAny(), version))
.returns(() => Observable.of(new Versioned<FieldDto>(newVersion, newField)));
schemasService.setup(x => x.postField(app, schema.name, It.isAny(), undefined, version))
.returns(() => Observable.of(new Versioned<RootFieldDto>(newVersion, newField)));
schemasState.addField(schema, request, modified).subscribe();
schemasState.addField(schema, request, undefined, modified).subscribe();
const schema_1 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
@ -315,7 +319,7 @@ describe('SchemasState', () => {
});
it('should remove field and update user info when field removed', () => {
schemasService.setup(x => x.deleteField(app, schema.name, field1.fieldId, version))
schemasService.setup(x => x.deleteField(app, schema.name, field1.fieldId, undefined, version))
.returns(() => Observable.of(new Versioned<any>(newVersion, {})));
schemasState.deleteField(schema, field1, modified).subscribe();
@ -327,10 +331,10 @@ describe('SchemasState', () => {
});
it('should sort fields and update user info when fields sorted', () => {
schemasService.setup(x => x.putFieldOrdering(app, schema.name, [field2.fieldId, field1.fieldId], version))
schemasService.setup(x => x.putFieldOrdering(app, schema.name, [field2.fieldId, field1.fieldId], undefined, version))
.returns(() => Observable.of(new Versioned<any>(newVersion, {})));
schemasState.sortFields(schema, [field2, field1], modified).subscribe();
schemasState.sortFields(schema, [field2, field1], undefined, modified).subscribe();
const schema_1 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
@ -341,7 +345,7 @@ describe('SchemasState', () => {
it('should update field properties and update user info when field updated', () => {
const request = new UpdateFieldDto(createProperties('String'));
schemasService.setup(x => x.putField(app, schema.name, field1.fieldId, request, version))
schemasService.setup(x => x.putField(app, schema.name, field1.fieldId, request, undefined, version))
.returns(() => Observable.of(new Versioned<any>(newVersion, {})));
schemasState.updateField(schema, field1, request, modified).subscribe();
@ -353,10 +357,10 @@ describe('SchemasState', () => {
});
it('should mark field hidden and update user info when field hidden', () => {
schemasService.setup(x => x.hideField(app, schema.name, field1.fieldId, version))
schemasService.setup(x => x.hideField(app, schema.name, field1.fieldId, undefined, version))
.returns(() => Observable.of(new Versioned<any>(newVersion, {})));
schemasState.hideField(schema, field1, modified).subscribe();
schemasState.hideField(schema, field1, undefined, modified).subscribe();
const schema_1 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
@ -365,7 +369,7 @@ describe('SchemasState', () => {
});
it('should mark field disabled and update user info when field disabled', () => {
schemasService.setup(x => x.disableField(app, schema.name, field1.fieldId, version))
schemasService.setup(x => x.disableField(app, schema.name, field1.fieldId, undefined, version))
.returns(() => Observable.of(new Versioned<any>(newVersion, {})));
schemasState.disableField(schema, field1, modified).subscribe();
@ -389,7 +393,7 @@ describe('SchemasState', () => {
});
it('should unmark field hidden and update user info when field shown', () => {
schemasService.setup(x => x.showField(app, schema.name, field2.fieldId, version))
schemasService.setup(x => x.showField(app, schema.name, field2.fieldId, undefined, version))
.returns(() => Observable.of(new Versioned<any>(newVersion, {})));
schemasState.showField(schema, field2, modified).subscribe();
@ -401,7 +405,7 @@ describe('SchemasState', () => {
});
it('should unmark field disabled and update user info when field enabled', () => {
schemasService.setup(x => x.enableField(app, schema.name, field2.fieldId, version))
schemasService.setup(x => x.enableField(app, schema.name, field2.fieldId, undefined, version))
.returns(() => Observable.of(new Versioned<any>(newVersion, {})));
schemasState.enableField(schema, field2, modified).subscribe();

382
src/Squidex/app/shared/state/schemas.state.ts

@ -27,10 +27,13 @@ import { AppsState } from './apps.state';
import {
AddFieldDto,
AnyFieldDto,
createProperties,
CreateSchemaDto,
FieldDto,
FieldPropertiesDto,
NestedFieldDto,
RootFieldDto,
SchemaDetailsDto,
SchemaDto,
SchemaPropertiesDto,
@ -282,13 +285,6 @@ export class SchemasState extends State<Snapshot> {
});
}
public addField(schema: SchemaDetailsDto, request: AddFieldDto, now?: DateTime): Observable<FieldDto> {
return this.schemasService.postField(this.appName, schema.name, request, schema.version)
.do(dto => {
this.replaceSchema(addField(schema, dto.payload, this.user, dto.version, now));
}).map(d => d.payload);
}
public publish(schema: SchemaDto, now?: DateTime): Observable<any> {
return this.schemasService.publishSchema(this.appName, schema.name, schema.version)
.do(dto => {
@ -313,23 +309,23 @@ export class SchemasState extends State<Snapshot> {
.notify(this.dialogs);
}
public enableField(schema: SchemaDetailsDto, field: FieldDto, now?: DateTime): Observable<any> {
return this.schemasService.enableField(this.appName, schema.name, field.fieldId, schema.version)
public configureScripts(schema: SchemaDetailsDto, request: UpdateSchemaScriptsDto, now?: DateTime): Observable<any> {
return this.schemasService.putScripts(this.appName, schema.name, request, schema.version)
.do(dto => {
this.replaceSchema(updateField(schema, setDisabled(field, false), this.user, dto.version, now));
this.replaceSchema(configureScripts(schema, request, this.user, dto.version, now));
})
.notify(this.dialogs);
}
public disableField(schema: SchemaDetailsDto, field: FieldDto, now?: DateTime): Observable<any> {
return this.schemasService.disableField(this.appName, schema.name, field.fieldId, schema.version)
public update(schema: SchemaDetailsDto, request: UpdateSchemaDto, now?: DateTime): Observable<any> {
return this.schemasService.putSchema(this.appName, schema.name, request, schema.version)
.do(dto => {
this.replaceSchema(updateField(schema, setDisabled(field, true), this.user, dto.version, now));
this.replaceSchema(updateProperties(schema, request, this.user, dto.version, now));
})
.notify(this.dialogs);
}
public lockField(schema: SchemaDetailsDto, field: FieldDto, now?: DateTime): Observable<any> {
public lockField(schema: SchemaDetailsDto, field: RootFieldDto, now?: DateTime): Observable<any> {
return this.schemasService.lockField(this.appName, schema.name, field.fieldId, schema.version)
.do(dto => {
this.replaceSchema(updateField(schema, setLocked(field, true), this.user, dto.version, now));
@ -337,62 +333,101 @@ export class SchemasState extends State<Snapshot> {
.notify(this.dialogs);
}
public showField(schema: SchemaDetailsDto, field: FieldDto, now?: DateTime): Observable<any> {
return this.schemasService.showField(this.appName, schema.name, field.fieldId, schema.version)
public addField(schema: SchemaDetailsDto, request: AddFieldDto, parent?: RootFieldDto, now?: DateTime): Observable<FieldDto> {
return this.schemasService.postField(this.appName, schema.name, request, pid(parent), schema.version)
.do(dto => {
if (Types.is(dto.payload, NestedFieldDto)) {
this.replaceSchema(updateField(schema, addNested(parent!, dto.payload), this.user, dto.version, now));
} else {
this.replaceSchema(addField(schema, dto.payload, this.user, dto.version, now));
}
}).map(d => d.payload);
}
public sortFields(schema: SchemaDetailsDto, fields: any[], parent?: RootFieldDto, now?: DateTime): Observable<any> {
return this.schemasService.putFieldOrdering(this.appName, schema.name, fields.map(t => t.fieldId), pid(parent), schema.version)
.do(dto => {
this.replaceSchema(updateField(schema, setHidden(field, false), this.user, dto.version, now));
if (!parent) {
this.replaceSchema(replaceFields(schema, fields, this.user, dto.version, now));
} else {
this.replaceSchema(updateField(schema, replaceNested(parent, fields), this.user, dto.version, now));
}
})
.notify(this.dialogs);
}
public hideField(schema: SchemaDetailsDto, field: FieldDto, now?: DateTime): Observable<any> {
return this.schemasService.hideField(this.appName, schema.name, field.fieldId, schema.version)
public enableField(schema: SchemaDetailsDto, field: AnyFieldDto, now?: DateTime): Observable<any> {
return this.schemasService.enableField(this.appName, schema.name, field.fieldId, pidof(field), schema.version)
.do(dto => {
this.replaceSchema(updateField(schema, setHidden(field, true), this.user, dto.version, now));
this.replaceField(schema, setDisabled(field, false), dto.version, now);
})
.notify(this.dialogs);
}
public deleteField(schema: SchemaDetailsDto, field: FieldDto, now?: DateTime): Observable<any> {
return this.schemasService.deleteField(this.appName, schema.name, field.fieldId, schema.version)
public disableField(schema: SchemaDetailsDto, field: AnyFieldDto, now?: DateTime): Observable<any> {
return this.schemasService.disableField(this.appName, schema.name, field.fieldId, pidof(field), schema.version)
.do(dto => {
this.replaceSchema(removeField(schema, field, this.user, dto.version, now));
this.replaceField(schema, setDisabled(field, true), dto.version, now);
})
.notify(this.dialogs);
}
public sortFields(schema: SchemaDetailsDto, fields: FieldDto[], now?: DateTime): Observable<any> {
return this.schemasService.putFieldOrdering(this.appName, schema.name, fields.map(t => t.fieldId), schema.version)
public showField(schema: SchemaDetailsDto, field: AnyFieldDto, now?: DateTime): Observable<any> {
return this.schemasService.showField(this.appName, schema.name, field.fieldId, pidof(field), schema.version)
.do(dto => {
this.replaceSchema(replaceFields(schema, fields, this.user, dto.version, now));
this.replaceField(schema, setHidden(field, false), dto.version, now);
})
.notify(this.dialogs);
}
public updateField(schema: SchemaDetailsDto, field: FieldDto, request: UpdateFieldDto, now?: DateTime): Observable<any> {
return this.schemasService.putField(this.appName, schema.name, field.fieldId, request, schema.version)
public hideField(schema: SchemaDetailsDto, field: AnyFieldDto, parent?: RootFieldDto, now?: DateTime): Observable<any> {
return this.schemasService.hideField(this.appName, schema.name, field.fieldId, pidof(field), schema.version)
.do(dto => {
this.replaceSchema(updateField(schema, update(field, request.properties), this.user, dto.version, now));
this.replaceField(schema, setHidden(field, true), dto.version, now);
})
.notify(this.dialogs);
}
public configureScripts(schema: SchemaDetailsDto, request: UpdateSchemaScriptsDto, now?: DateTime): Observable<any> {
return this.schemasService.putScripts(this.appName, schema.name, request, schema.version)
public updateField(schema: SchemaDetailsDto, field: AnyFieldDto, request: UpdateFieldDto, now?: DateTime): Observable<any> {
return this.schemasService.putField(this.appName, schema.name, field.fieldId, request, pidof(field), schema.version)
.do(dto => {
this.replaceSchema(configureScripts(schema, request, this.user, dto.version, now));
this.replaceField(schema, update(field, request.properties), dto.version, now);
})
.notify(this.dialogs);
}
public update(schema: SchemaDetailsDto, request: UpdateSchemaDto, now?: DateTime): Observable<any> {
return this.schemasService.putSchema(this.appName, schema.name, request, schema.version)
public deleteField(schema: SchemaDetailsDto, field: AnyFieldDto, now?: DateTime): Observable<any> {
return this.schemasService.deleteField(this.appName, schema.name, field.fieldId, pidof(field), schema.version)
.do(dto => {
this.replaceSchema(updateProperties(schema, request, this.user, dto.version, now));
this.removeField(schema, field, dto.version, now);
})
.notify(this.dialogs);
}
private replaceField(schema: SchemaDetailsDto, field: AnyFieldDto, version: Version, now?: DateTime) {
if (Types.is(field, RootFieldDto)) {
this.replaceSchema(updateField(schema, field, this.user, version, now));
} else {
const parent = schema.fields.find(x => x.fieldId === field.parentId);
if (parent) {
this.replaceSchema(updateField(schema, updatedNested(parent, field), this.user, version, now));
}
}
}
private removeField(schema: SchemaDetailsDto, field: AnyFieldDto, version: Version, now?: DateTime) {
if (Types.is(field, RootFieldDto)) {
this.replaceSchema(removeField(schema, field, this.user, version, now));
} else {
const parent = schema.fields.find(x => x.fieldId === field.parentId);
if (parent) {
this.replaceSchema(updateField(schema, removeNested(parent, field), this.user, version, now));
}
}
}
private replaceSchema(schema: SchemaDto) {
return this.next(s => {
const schemas = s.schemas.replaceBy('id', schema).sortByStringAsc(x => x.displayName);
@ -444,204 +479,97 @@ function removeCategory(categories: { [name: string]: boolean }, category: strin
return categories;
}
const setPublished = (schema: SchemaDto | SchemaDetailsDto, publish: boolean, user: string, version: Version, now?: DateTime) => {
if (Types.is(schema, SchemaDetailsDto)) {
return new SchemaDetailsDto(
schema.id,
schema.name,
schema.category,
schema.properties,
publish,
schema.createdBy, user,
schema.created, now || DateTime.now(),
version,
schema.fields,
schema.scriptQuery,
schema.scriptCreate,
schema.scriptUpdate,
schema.scriptDelete,
schema.scriptChange);
} else {
return new SchemaDto(
schema.id,
schema.name,
schema.category,
schema.properties,
publish,
schema.createdBy, user,
schema.created, now || DateTime.now(),
version);
}
};
const changeCategory = (schema: SchemaDto | SchemaDetailsDto, category: string, user: string, version: Version, now?: DateTime) => {
if (Types.is(schema, SchemaDetailsDto)) {
return new SchemaDetailsDto(
schema.id,
schema.name,
category,
schema.properties,
schema.isPublished,
schema.createdBy, user,
schema.created, now || DateTime.now(),
version,
schema.fields,
schema.scriptQuery,
schema.scriptCreate,
schema.scriptUpdate,
schema.scriptDelete,
schema.scriptChange);
} else {
return new SchemaDto(
schema.id,
schema.name,
category,
schema.properties,
schema.isPublished,
schema.createdBy, user,
schema.created, now || DateTime.now(),
version);
}
};
const setPublished = <T extends SchemaDto>(schema: T, isPublished: boolean, user: string, version: Version, now?: DateTime) =>
<T>schema.with({
isPublished,
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
const changeCategory = <T extends SchemaDto>(schema: T, category: string, user: string, version: Version, now?: DateTime) =>
<T>schema.with({
category,
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
const configureScripts = (schema: SchemaDetailsDto, scripts: UpdateSchemaScriptsDto, user: string, version: Version, now?: DateTime) =>
new SchemaDetailsDto(
schema.id,
schema.name,
schema.category,
schema.properties,
schema.isPublished,
schema.createdBy, user,
schema.created, now || DateTime.now(),
version,
schema.fields,
scripts.scriptQuery,
scripts.scriptCreate,
scripts.scriptUpdate,
scripts.scriptDelete,
scripts.scriptChange);
schema.with({
...scripts,
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
const updateProperties = (schema: SchemaDetailsDto, properties: SchemaPropertiesDto, user: string, version: Version, now?: DateTime) =>
new SchemaDetailsDto(
schema.id,
schema.name,
schema.category,
schema.with({
properties,
schema.isPublished,
schema.createdBy, user,
schema.created, now || DateTime.now(),
version,
schema.fields,
schema.scriptQuery,
schema.scriptCreate,
schema.scriptUpdate,
schema.scriptDelete,
schema.scriptChange);
const addField = (schema: SchemaDetailsDto, field: FieldDto, user: string, version: Version, now?: DateTime) =>
new SchemaDetailsDto(
schema.id,
schema.name,
schema.category,
schema.properties,
schema.isPublished,
schema.createdBy, user,
schema.created, now || DateTime.now(),
version,
[...schema.fields, field],
schema.scriptQuery,
schema.scriptCreate,
schema.scriptUpdate,
schema.scriptDelete,
schema.scriptChange);
const updateField = (schema: SchemaDetailsDto, field: FieldDto, user: string, version: Version, now?: DateTime) =>
new SchemaDetailsDto(
schema.id,
schema.name,
schema.category,
schema.properties,
schema.isPublished,
schema.createdBy, user,
schema.created, now || DateTime.now(),
version,
schema.fields.map(f => f.fieldId === field.fieldId ? field : f),
schema.scriptQuery,
schema.scriptCreate,
schema.scriptUpdate,
schema.scriptDelete,
schema.scriptChange);
const replaceFields = (schema: SchemaDetailsDto, fields: FieldDto[], user: string, version: Version, now?: DateTime) =>
new SchemaDetailsDto(
schema.id,
schema.name,
schema.category,
schema.properties,
schema.isPublished,
schema.createdBy, user,
schema.created, now || DateTime.now(),
version,
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
const addField = (schema: SchemaDetailsDto, field: RootFieldDto, user: string, version: Version, now?: DateTime) =>
schema.with({
fields: [...schema.fields, field],
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
const updateField = (schema: SchemaDetailsDto, field: RootFieldDto, user: string, version: Version, now?: DateTime) =>
schema.with({
fields: schema.fields.map(f => f.fieldId === field.fieldId ? field : f),
lastModified: now || DateTime.now(),
lastModifiedBy: user,
version
});
const replaceFields = (schema: SchemaDetailsDto, fields: RootFieldDto[], user: string, version: Version, now?: DateTime) =>
schema.with({
fields,
schema.scriptQuery,
schema.scriptCreate,
schema.scriptUpdate,
schema.scriptDelete,
schema.scriptChange);
version,
lastModified: now || DateTime.now(),
lastModifiedBy: user
});
const removeField = (schema: SchemaDetailsDto, field: FieldDto, user: string, version: Version, now?: DateTime) =>
new SchemaDetailsDto(
schema.id,
schema.name,
schema.category,
schema.properties,
schema.isPublished,
schema.createdBy, user,
schema.created, now || DateTime.now(),
schema.with({
fields: schema.fields.filter(f => f.fieldId !== field.fieldId),
version,
schema.fields.filter(f => f.fieldId !== field.fieldId),
schema.scriptQuery,
schema.scriptCreate,
schema.scriptUpdate,
schema.scriptDelete,
schema.scriptChange);
const setLocked = (field: FieldDto, isLocked: boolean) =>
new FieldDto(
field.fieldId,
field.name,
isLocked,
field.isHidden,
field.isDisabled,
field.partitioning,
field.properties);
const setHidden = (field: FieldDto, isHidden: boolean) =>
new FieldDto(
field.fieldId,
field.name,
field.isLocked,
isHidden,
field.isDisabled,
field.partitioning,
field.properties);
const setDisabled = (field: FieldDto, isDisabled: boolean) =>
new FieldDto(
field.fieldId,
field.name,
field.isLocked,
field.isDisabled,
isDisabled,
field.partitioning,
field.properties);
const update = (field: FieldDto, properties: FieldPropertiesDto) =>
new FieldDto(
field.fieldId,
field.name,
field.isLocked,
field.isHidden,
field.isDisabled,
field.partitioning,
properties);
lastModified: now || DateTime.now(),
lastModifiedBy: user
});
const addNested = (parent: RootFieldDto, nested: NestedFieldDto) =>
parent.with({ nested: [...parent.nested, nested] });
const updatedNested = (parent: RootFieldDto, nested: NestedFieldDto) =>
parent.with({ nested: parent.nested.map(f => f.fieldId === nested.fieldId ? nested : f) });
const replaceNested = (parent: RootFieldDto, nested: NestedFieldDto[]) =>
parent.with({ nested });
const removeNested = (parent: RootFieldDto, nested: NestedFieldDto) =>
parent.with({ nested: parent.nested.filter(f => f.fieldId !== nested.fieldId) });
const setLocked = (field: RootFieldDto, isLocked: boolean) =>
field.with({ isLocked });
const setHidden = <T extends FieldDto>(field: T, isHidden: boolean) =>
<T>field.with(<any>{ isHidden });
const setDisabled = <T extends FieldDto>(field: T, isDisabled: boolean) =>
<T>field.with(<any>{ isDisabled });
const update = <T extends FieldDto>(field: T, properties: FieldPropertiesDto) =>
<T>field.with({ properties });
const pid = (field?: RootFieldDto) =>
field ? field.fieldId : undefined;
const pidof = (field: FieldDto) =>
Types.is(field, NestedFieldDto) ? field.parentId : undefined;
Loading…
Cancel
Save