Browse Source

Move business logic from service to states.

pull/356/head
Sebastian 8 years ago
parent
commit
2493ca400f
  1. 14
      src/Squidex/app/shared/services/apps.service.spec.ts
  2. 27
      src/Squidex/app/shared/services/apps.service.ts
  3. 34
      src/Squidex/app/shared/services/rules.service.spec.ts
  4. 38
      src/Squidex/app/shared/services/rules.service.ts
  5. 27
      src/Squidex/app/shared/services/schemas.service.spec.ts
  6. 49
      src/Squidex/app/shared/services/schemas.service.ts
  7. 3
      src/Squidex/app/shared/state/_test-helpers.ts
  8. 12
      src/Squidex/app/shared/state/apps.state.spec.ts
  9. 22
      src/Squidex/app/shared/state/apps.state.ts
  10. 26
      src/Squidex/app/shared/state/asset-uploader.state.spec.ts
  11. 30
      src/Squidex/app/shared/state/comments.state.spec.ts
  12. 6
      src/Squidex/app/shared/state/contributors.state.spec.ts
  13. 10
      src/Squidex/app/shared/state/rules.state.spec.ts
  14. 39
      src/Squidex/app/shared/state/rules.state.ts
  15. 17
      src/Squidex/app/shared/state/schemas.state.spec.ts
  16. 29
      src/Squidex/app/shared/state/schemas.state.ts
  17. 24
      src/Squidex/package-lock.json
  18. 8
      src/Squidex/package.json
  19. 4
      src/Squidex/tslint.json

14
src/Squidex/app/shared/services/apps.service.spec.ts

@ -11,6 +11,7 @@ import { inject, TestBed } from '@angular/core/testing';
import { import {
AnalyticsService, AnalyticsService,
ApiUrlConfig, ApiUrlConfig,
AppCreatedDto,
AppDto, AppDto,
AppsService, AppsService,
DateTime, DateTime,
@ -18,8 +19,6 @@ import {
} from '@app/shared/internal'; } from '@app/shared/internal';
describe('AppsService', () => { describe('AppsService', () => {
const now = DateTime.now();
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
@ -84,9 +83,9 @@ describe('AppsService', () => {
const dto = { name: 'new-app' }; const dto = { name: 'new-app' };
let app: AppDto; let app: AppCreatedDto;
appsService.postApp(dto, now).subscribe(result => { appsService.postApp(dto).subscribe(result => {
app = result; app = result;
}); });
@ -102,7 +101,12 @@ describe('AppsService', () => {
planUpgrade: 'Enterprise' planUpgrade: 'Enterprise'
}); });
expect(app!).toEqual(new AppDto('123', dto.name, [new Permission('Reader')], now, now, 'Basic', 'Enterprise')); expect(app!).toEqual({
id: '123',
permissions: ['Reader'],
planName: 'Basic',
planUpgrade: 'Enterprise'
});
})); }));
it('should make delete request to archive app', it('should make delete request to archive app',

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

@ -38,6 +38,13 @@ export interface CreateAppDto {
readonly template?: string; readonly template?: string;
} }
export interface AppCreatedDto {
readonly id: string;
readonly permissions: string[];
readonly planName?: string;
readonly planUpgrade?: string;
}
@Injectable() @Injectable()
export class AppsService { export class AppsService {
constructor( constructor(
@ -70,26 +77,10 @@ export class AppsService {
pretifyError('Failed to load apps. Please reload.')); pretifyError('Failed to load apps. Please reload.'));
} }
public postApp(dto: CreateAppDto, now?: DateTime): Observable<AppDto> { public postApp(dto: CreateAppDto): Observable<AppCreatedDto> {
const url = this.apiUrl.buildUrl('api/apps'); const url = this.apiUrl.buildUrl('api/apps');
return this.http.post<any>(url, dto).pipe( return this.http.post<AppCreatedDto>(url, dto).pipe(
map(body => {
now = now || DateTime.now();
const permissions = (<string[]>body.permissions).map(x => new Permission(x));
const app = new AppDto(
body.id,
dto.name,
permissions,
now,
now,
body.planName,
body.planUpgrade);
return app;
}),
tap(() => { tap(() => {
this.analytics.trackEvent('App', 'Created', dto.name); this.analytics.trackEvent('App', 'Created', dto.name);
}), }),

34
src/Squidex/app/shared/services/rules.service.spec.ts

@ -19,12 +19,12 @@ import {
RuleEventDto, RuleEventDto,
RuleEventsDto, RuleEventsDto,
RulesService, RulesService,
Version Version,
Versioned
} from '@app/shared/internal'; } from '@app/shared/internal';
import { RuleCreatedDto } from './rules.service';
describe('RulesService', () => { describe('RulesService', () => {
const now = DateTime.now();
const user = 'me';
const version = new Version('1'); const version = new Version('1');
beforeEach(() => { beforeEach(() => {
@ -179,9 +179,9 @@ describe('RulesService', () => {
} }
}; };
let rule: RuleDto; let rule: Versioned<RuleCreatedDto>;
rulesService.postRule('my-app', dto, user, now).subscribe(result => { rulesService.postRule('my-app', dto).subscribe(result => {
rule = result; rule = result;
}); });
@ -190,28 +190,18 @@ describe('RulesService', () => {
expect(req.request.method).toEqual('POST'); expect(req.request.method).toEqual('POST');
expect(req.request.headers.get('If-Match')).toBeNull(); expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({ id: 'id1', sharedSecret: 'token1', schemaId: 'schema1' }, { req.flush({ id: 'id1' }, {
headers: { headers: {
etag: '1' etag: '1'
} }
}); });
expect(rule!).toEqual( expect(rule!).toEqual({
new RuleDto('id1', user, user, now, now, payload: {
version, id: 'id1'
true, },
{ version
param1: 1, });
param2: 2,
triggerType: 'ContentChanged'
},
'ContentChanged',
{
param3: 3,
param4: 4,
actionType: 'Webhook'
},
'Webhook'));
})); }));
it('should make put request to update rule', it('should make put request to update rule',

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

@ -15,6 +15,7 @@ import {
ApiUrlConfig, ApiUrlConfig,
DateTime, DateTime,
HTTP, HTTP,
mapVersioned,
Model, Model,
pretifyError, pretifyError,
ResultSet, ResultSet,
@ -109,16 +110,18 @@ export class RuleEventDto extends Model<RuleEventDto> {
} }
} }
export interface CreateRuleDto { export interface UpsertRuleDto {
readonly trigger: any; readonly trigger: RuleAction;
readonly action: any; readonly action: RuleAction;
} }
export interface UpdateRuleDto { export interface RuleCreatedDto {
readonly trigger?: any; readonly id: string;
readonly action?: any;
} }
export type RuleAction = { actionType: string } & any;
export type RuleTrigger = { triggerType: string } & any;
@Injectable() @Injectable()
export class RulesService { export class RulesService {
constructor( constructor(
@ -190,33 +193,18 @@ export class RulesService {
pretifyError('Failed to load Rules. Please reload.')); pretifyError('Failed to load Rules. Please reload.'));
} }
public postRule(appName: string, dto: CreateRuleDto, user: string, now: DateTime): Observable<RuleDto> { public postRule(appName: string, dto: UpsertRuleDto): Observable<Versioned<RuleCreatedDto>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/rules`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/rules`);
return HTTP.postVersioned<any>(this.http, url, dto).pipe( return HTTP.postVersioned<RuleCreatedDto>(this.http, url, dto).pipe(
map(({ version, payload }) => { mapVersioned(({ body }) => body!),
const body = payload.body;
return new RuleDto(
body.id,
user,
user,
now,
now,
version,
true,
dto.trigger,
dto.trigger.triggerType,
dto.action,
dto.action.actionType);
}),
tap(() => { tap(() => {
this.analytics.trackEvent('Rule', 'Created', appName); this.analytics.trackEvent('Rule', 'Created', appName);
}), }),
pretifyError('Failed to create rule. Please reload.')); pretifyError('Failed to create rule. Please reload.'));
} }
public putRule(appName: string, id: string, dto: UpdateRuleDto, version: Version): Observable<Versioned<any>> { public putRule(appName: string, id: string, dto: Partial<UpsertRuleDto>, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/rules/${id}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/rules/${id}`);
return HTTP.putVersioned(this.http, url, dto, version).pipe( return HTTP.putVersioned(this.http, url, dto, version).pipe(

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

@ -9,11 +9,9 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/
import { inject, TestBed } from '@angular/core/testing'; import { inject, TestBed } from '@angular/core/testing';
import { import {
AddFieldDto,
AnalyticsService, AnalyticsService,
ApiUrlConfig, ApiUrlConfig,
createProperties, createProperties,
CreateSchemaDto,
DateTime, DateTime,
FieldDto, FieldDto,
NestedFieldDto, NestedFieldDto,
@ -22,12 +20,12 @@ import {
SchemaDto, SchemaDto,
SchemaPropertiesDto, SchemaPropertiesDto,
SchemasService, SchemasService,
Version Version,
Versioned
} from '@app/shared/internal'; } from '@app/shared/internal';
import { SchemaCreatedDto } from './schemas.service';
describe('SchemasService', () => { describe('SchemasService', () => {
const now = DateTime.now();
const user = 'me';
const version = new Version('1'); const version = new Version('1');
beforeEach(() => { beforeEach(() => {
@ -330,11 +328,11 @@ describe('SchemasService', () => {
it('should make post request to create schema', it('should make post request to create schema',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
const dto = new CreateSchemaDto('name', undefined, undefined, true); const dto = { name: 'name' };
let schema: SchemaDetailsDto; let schema: Versioned<SchemaCreatedDto>;
schemasService.postSchema('my-app', dto, user, now).subscribe(result => { schemasService.postSchema('my-app', dto).subscribe(result => {
schema = result; schema = result;
}); });
@ -347,11 +345,16 @@ describe('SchemasService', () => {
id: '1' id: '1'
}, { }, {
headers: { headers: {
etag: '2' etag: '1'
} }
}); });
expect(schema!).toEqual(new SchemaDetailsDto('1', dto.name, '', new SchemaPropertiesDto(), true, false, now, user, now, user, new Version('2'), [], {}, {})); expect(schema!).toEqual({
payload: {
id: '1'
},
version
});
})); }));
it('should make put request to update schema', it('should make put request to update schema',
@ -417,7 +420,7 @@ describe('SchemasService', () => {
it('should make post request to add field', it('should make post request to add field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
const dto = new AddFieldDto('name', 'invariant', createProperties('Number')); const dto = { name: 'name', partitioning: 'invariant', properties: createProperties('Number') };
let field: FieldDto; let field: FieldDto;
@ -464,7 +467,7 @@ describe('SchemasService', () => {
it('should make post request to add nested field', it('should make post request to add nested field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
const dto = new AddFieldDto('name', 'invariant', createProperties('Number')); const dto = { name: 'name', partitioning: 'invariant', properties: createProperties('Number') };
let field: FieldDto; let field: FieldDto;

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

@ -171,23 +171,21 @@ export class SchemaPropertiesDto {
} }
} }
export class AddFieldDto { export interface AddFieldDto {
constructor( readonly name: string;
public readonly name: string, readonly partitioning: string;
public readonly partitioning: string, readonly properties: FieldPropertiesDto;
public readonly properties: FieldPropertiesDto
) {
}
} }
export class CreateSchemaDto { export interface CreateSchemaDto {
constructor( readonly name: string;
public readonly name: string, readonly fields?: RootFieldDto[];
public readonly fields?: RootFieldDto[], readonly properties?: SchemaPropertiesDto;
public readonly properties?: SchemaPropertiesDto, readonly isSingleton?: boolean;
public readonly isSingleton?: boolean }
) {
} export interface SchemaCreatedDto {
readonly id: string;
} }
export interface UpdateSchemaCategoryDto { export interface UpdateSchemaCategoryDto {
@ -303,28 +301,11 @@ export class SchemasService {
pretifyError('Failed to load schema. Please reload.')); pretifyError('Failed to load schema. Please reload.'));
} }
public postSchema(appName: string, dto: CreateSchemaDto, user: string, now: DateTime): Observable<SchemaDetailsDto> { public postSchema(appName: string, dto: CreateSchemaDto): Observable<Versioned<SchemaCreatedDto>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas`);
return HTTP.postVersioned<any>(this.http, url, dto).pipe( return HTTP.postVersioned<any>(this.http, url, dto).pipe(
map(({ version, payload }) => { mapVersioned(({ body }) => body!),
const body = payload.body;
now = now || DateTime.now();
const schema = new SchemaDetailsDto(
body.id,
dto.name, '',
dto.properties || new SchemaPropertiesDto(),
dto.isSingleton === true,
false,
now, user,
now, user,
version,
dto.fields || []);
return schema;
}),
tap(() => { tap(() => {
this.analytics.trackEvent('Schema', 'Created', appName); this.analytics.trackEvent('Schema', 'Created', appName);
}), }),

3
src/Squidex/app/shared/state/_test-helpers.ts

@ -20,7 +20,6 @@ const creation = DateTime.today().addDays(-2);
const creator = 'me'; const creator = 'me';
const modified = DateTime.now().addDays(-1); const modified = DateTime.now().addDays(-1);
const modifier = 'now-me'; const modifier = 'now-me';
const now = DateTime.now();
const version = new Version('1'); const version = new Version('1');
const newVersion = new Version('2'); const newVersion = new Version('2');
@ -45,8 +44,6 @@ export const TestValues = {
creator, creator,
modified, modified,
modifier, modifier,
now,
newVersion, newVersion,
userId: modifier,
version version
}; };

12
src/Squidex/app/shared/state/apps.state.spec.ts

@ -88,9 +88,9 @@ describe('AppsState', () => {
const request = { ...newApp }; const request = { ...newApp };
appsService.setup(x => x.postApp(request)) appsService.setup(x => x.postApp(request))
.returns(() => of(newApp)).verifiable(); .returns(() => of({ ...newApp, permissions: ['Owner'] })).verifiable();
appsState.create(request).subscribe(); appsState.create(request, now).subscribe();
expect(appsState.snapshot.apps.values).toEqual([newApp, ...oldApps]); expect(appsState.snapshot.apps.values).toEqual([newApp, ...oldApps]);
}); });
@ -99,12 +99,12 @@ describe('AppsState', () => {
const request = { ...newApp }; const request = { ...newApp };
appsService.setup(x => x.postApp(request)) appsService.setup(x => x.postApp(request))
.returns(() => of(newApp)).verifiable(); .returns(() => of({ ...newApp, permissions: ['Owner'] })).verifiable();
appsService.setup(x => x.deleteApp(newApp.name)) appsService.setup(x => x.deleteApp(newApp.name))
.returns(() => of({})).verifiable(); .returns(() => of({})).verifiable();
appsState.create(request).subscribe(); appsState.create(request, now).subscribe();
const appsAfterCreate = appsState.snapshot.apps.values; const appsAfterCreate = appsState.snapshot.apps.values;
@ -120,12 +120,12 @@ describe('AppsState', () => {
const request = { ...newApp }; const request = { ...newApp };
appsService.setup(x => x.postApp(request)) appsService.setup(x => x.postApp(request))
.returns(() => of(newApp)).verifiable(); .returns(() => of({ ...newApp, permissions: ['Owner'] })).verifiable();
appsService.setup(x => x.deleteApp(newApp.name)) appsService.setup(x => x.deleteApp(newApp.name))
.returns(() => of({})).verifiable(); .returns(() => of({})).verifiable();
appsState.create(request).subscribe(); appsState.create(request, now).subscribe();
appsState.select(newApp.name).subscribe(); appsState.select(newApp.name).subscribe();
appsState.delete(newApp.name).subscribe(); appsState.delete(newApp.name).subscribe();

22
src/Squidex/app/shared/state/apps.state.ts

@ -10,13 +10,16 @@ import { Observable, of } from 'rxjs';
import { distinctUntilChanged, map, tap } from 'rxjs/operators'; import { distinctUntilChanged, map, tap } from 'rxjs/operators';
import { import {
DateTime,
DialogService, DialogService,
ImmutableArray, ImmutableArray,
Permission,
shareSubscribed, shareSubscribed,
State State
} from '@app/framework'; } from '@app/framework';
import { import {
AppCreatedDto,
AppDto, AppDto,
AppsService, AppsService,
CreateAppDto CreateAppDto
@ -79,11 +82,11 @@ export class AppsState extends State<Snapshot> {
shareSubscribed(this.dialogs)); shareSubscribed(this.dialogs));
} }
public create(request: CreateAppDto): Observable<AppDto> { public create(request: CreateAppDto, now?: DateTime): Observable<AppDto> {
return this.appsService.postApp(request).pipe( return this.appsService.postApp(request).pipe(
tap(payload => { tap(payload => {
this.next(s => { this.next(s => {
const apps = s.apps.push(payload).sortByStringAsc(x => x.name); const apps = s.apps.push(createApp(request, payload, now)).sortByStringAsc(x => x.name);
return { ...s, apps }; return { ...s, apps };
}); });
@ -104,4 +107,19 @@ export class AppsState extends State<Snapshot> {
}), }),
shareSubscribed(this.dialogs)); shareSubscribed(this.dialogs));
} }
}
function createApp(request: CreateAppDto, response: AppCreatedDto, now?: DateTime) {
now = now || DateTime.now();
const app = new AppDto(
response.id,
request.name,
response.permissions.map(x => new Permission(x)),
now,
now,
response.planName,
response.planUpgrade);
return app;
} }

26
src/Squidex/app/shared/state/asset-uploader.state.spec.ts

@ -31,9 +31,7 @@ describe('AssetsState', () => {
creator, creator,
creation, creation,
modified, modified,
modifier, modifier
now,
userId
} = TestValues; } = TestValues;
let assetsService: IMock<AssetsService>; let assetsService: IMock<AssetsService>;
@ -74,10 +72,10 @@ describe('AssetsState', () => {
it('should create initial state when uploading file', () => { it('should create initial state when uploading file', () => {
const file: File = <any>{ name: 'my-file' }; const file: File = <any>{ name: 'my-file' };
assetsService.setup(x => x.uploadFile(app, file, userId, now)) assetsService.setup(x => x.uploadFile(app, file, modifier, modified))
.returns(() => never()).verifiable(); .returns(() => never()).verifiable();
assetUploader.uploadFile(file, undefined, now).subscribe(); assetUploader.uploadFile(file, undefined, modified).subscribe();
const upload = assetUploader.snapshot.uploads.at(0); const upload = assetUploader.snapshot.uploads.at(0);
@ -88,10 +86,10 @@ describe('AssetsState', () => {
it('should update progress when uploading file makes progress', () => { it('should update progress when uploading file makes progress', () => {
const file: File = <any>{ name: 'my-file' }; const file: File = <any>{ name: 'my-file' };
assetsService.setup(x => x.uploadFile(app, file, userId, now)) assetsService.setup(x => x.uploadFile(app, file, modifier, modified))
.returns(() => ofForever(10, 20)).verifiable(); .returns(() => ofForever(10, 20)).verifiable();
assetUploader.uploadFile(file, undefined, now).subscribe(); assetUploader.uploadFile(file, undefined, modified).subscribe();
const upload = assetUploader.snapshot.uploads.at(0); const upload = assetUploader.snapshot.uploads.at(0);
@ -102,10 +100,10 @@ describe('AssetsState', () => {
it('should update status when uploading file failed', () => { it('should update status when uploading file failed', () => {
const file: File = <any>{ name: 'my-file' }; const file: File = <any>{ name: 'my-file' };
assetsService.setup(x => x.uploadFile(app, file, userId, now)) assetsService.setup(x => x.uploadFile(app, file, modifier, modified))
.returns(() => throwError('Error')).verifiable(); .returns(() => throwError('Error')).verifiable();
assetUploader.uploadFile(file, undefined, now).pipe(onErrorResumeNext()).subscribe(); assetUploader.uploadFile(file, undefined, modified).pipe(onErrorResumeNext()).subscribe();
const upload = assetUploader.snapshot.uploads.at(0); const upload = assetUploader.snapshot.uploads.at(0);
@ -116,12 +114,12 @@ describe('AssetsState', () => {
it('should update status when uploading file completes', (cb) => { it('should update status when uploading file completes', (cb) => {
const file: File = <any>{ name: 'my-file' }; const file: File = <any>{ name: 'my-file' };
assetsService.setup(x => x.uploadFile(app, file, userId, now)) assetsService.setup(x => x.uploadFile(app, file, modifier, modified))
.returns(() => of(10, 20, asset)).verifiable(); .returns(() => of(10, 20, asset)).verifiable();
let uploadedAsset: AssetDto; let uploadedAsset: AssetDto;
assetUploader.uploadFile(file, undefined, now).subscribe(dto => { assetUploader.uploadFile(file, undefined, modified).subscribe(dto => {
if (Types.is(dto, AssetDto)) { if (Types.is(dto, AssetDto)) {
uploadedAsset = dto; uploadedAsset = dto;
} }
@ -142,7 +140,7 @@ describe('AssetsState', () => {
assetsService.setup(x => x.replaceFile(app, asset.id, file, asset.version)) assetsService.setup(x => x.replaceFile(app, asset.id, file, asset.version))
.returns(() => never()).verifiable(); .returns(() => never()).verifiable();
assetUploader.uploadAsset(asset, file, now).subscribe(); assetUploader.uploadAsset(asset, file, modified).subscribe();
const upload = assetUploader.snapshot.uploads.at(0); const upload = assetUploader.snapshot.uploads.at(0);
@ -156,7 +154,7 @@ describe('AssetsState', () => {
assetsService.setup(x => x.replaceFile(app, asset.id, file, asset.version)) assetsService.setup(x => x.replaceFile(app, asset.id, file, asset.version))
.returns(() => ofForever(10, 20)).verifiable(); .returns(() => ofForever(10, 20)).verifiable();
assetUploader.uploadAsset(asset, file, now).subscribe(); assetUploader.uploadAsset(asset, file, modified).subscribe();
const upload = assetUploader.snapshot.uploads.at(0); const upload = assetUploader.snapshot.uploads.at(0);
@ -170,7 +168,7 @@ describe('AssetsState', () => {
assetsService.setup(x => x.replaceFile(app, asset.id, file, asset.version)) assetsService.setup(x => x.replaceFile(app, asset.id, file, asset.version))
.returns(() => throwError('Error')).verifiable(); .returns(() => throwError('Error')).verifiable();
assetUploader.uploadAsset(asset, file, now).pipe(onErrorResumeNext()).subscribe(); assetUploader.uploadAsset(asset, file, modified).pipe(onErrorResumeNext()).subscribe();
const upload = assetUploader.snapshot.uploads.at(0); const upload = assetUploader.snapshot.uploads.at(0);

30
src/Squidex/app/shared/state/comments.state.spec.ts

@ -25,14 +25,14 @@ describe('CommentsState', () => {
app, app,
appsState, appsState,
creator, creator,
now modified
} = TestValues; } = TestValues;
const commentsId = 'my-comments'; const commentsId = 'my-comments';
const oldComments = new CommentsDto([ const oldComments = new CommentsDto([
new CommentDto('1', now, 'text1', creator), new CommentDto('1', modified, 'text1', creator),
new CommentDto('2', now, 'text2', creator) new CommentDto('2', modified, 'text2', creator)
], [], [], new Version('1')); ], [], [], new Version('1'));
let dialogs: IMock<DialogService>; let dialogs: IMock<DialogService>;
@ -53,9 +53,9 @@ describe('CommentsState', () => {
describe('Loading', () => { describe('Loading', () => {
it('should load and merge comments', () => { it('should load and merge comments', () => {
const newComments = new CommentsDto([ const newComments = new CommentsDto([
new CommentDto('3', now, 'text3', creator) new CommentDto('3', modified, 'text3', creator)
], [ ], [
new CommentDto('2', now, 'text2_2', creator) new CommentDto('2', modified, 'text2_2', creator)
], ['1'], new Version('2')); ], ['1'], new Version('2'));
commentsService.setup(x => x.getComments(app, commentsId, new Version('-1'))) commentsService.setup(x => x.getComments(app, commentsId, new Version('-1')))
@ -69,8 +69,8 @@ describe('CommentsState', () => {
expect(commentsState.snapshot.isLoaded).toBeTruthy(); expect(commentsState.snapshot.isLoaded).toBeTruthy();
expect(commentsState.snapshot.comments).toEqual(ImmutableArray.of([ expect(commentsState.snapshot.comments).toEqual(ImmutableArray.of([
new CommentDto('2', now, 'text2_2', creator), new CommentDto('2', modified, 'text2_2', creator),
new CommentDto('3', now, 'text3', creator) new CommentDto('3', modified, 'text3', creator)
])); ]));
}); });
}); });
@ -84,7 +84,7 @@ describe('CommentsState', () => {
}); });
it('should add comment to snapshot when created', () => { it('should add comment to snapshot when created', () => {
const newComment = new CommentDto('3', now, 'text3', creator); const newComment = new CommentDto('3', modified, 'text3', creator);
const request = { text: 'text3' }; const request = { text: 'text3' };
@ -94,9 +94,9 @@ describe('CommentsState', () => {
commentsState.create('text3').subscribe(); commentsState.create('text3').subscribe();
expect(commentsState.snapshot.comments).toEqual(ImmutableArray.of([ expect(commentsState.snapshot.comments).toEqual(ImmutableArray.of([
new CommentDto('1', now, 'text1', creator), new CommentDto('1', modified, 'text1', creator),
new CommentDto('2', now, 'text2', creator), new CommentDto('2', modified, 'text2', creator),
new CommentDto('3', now, 'text3', creator) new CommentDto('3', modified, 'text3', creator)
])); ]));
}); });
@ -106,11 +106,11 @@ describe('CommentsState', () => {
commentsService.setup(x => x.putComment(app, commentsId, '2', request)) commentsService.setup(x => x.putComment(app, commentsId, '2', request))
.returns(() => of({})).verifiable(); .returns(() => of({})).verifiable();
commentsState.update(oldComments.createdComments[1], 'text2_2', now).subscribe(); commentsState.update(oldComments.createdComments[1], 'text2_2', modified).subscribe();
expect(commentsState.snapshot.comments).toEqual(ImmutableArray.of([ expect(commentsState.snapshot.comments).toEqual(ImmutableArray.of([
new CommentDto('1', now, 'text1', creator), new CommentDto('1', modified, 'text1', creator),
new CommentDto('2', now, 'text2_2', creator) new CommentDto('2', modified, 'text2_2', creator)
])); ]));
}); });
@ -121,7 +121,7 @@ describe('CommentsState', () => {
commentsState.delete(oldComments.createdComments[1]).subscribe(); commentsState.delete(oldComments.createdComments[1]).subscribe();
expect(commentsState.snapshot.comments).toEqual(ImmutableArray.of([ expect(commentsState.snapshot.comments).toEqual(ImmutableArray.of([
new CommentDto('1', now, 'text1', creator) new CommentDto('1', modified, 'text1', creator)
])); ]));
}); });
}); });

6
src/Squidex/app/shared/state/contributors.state.spec.ts

@ -23,14 +23,14 @@ describe('ContributorsState', () => {
app, app,
appsState, appsState,
authService, authService,
modifier,
newVersion, newVersion,
userId,
version version
} = TestValues; } = TestValues;
const oldContributors = [ const oldContributors = [
new ContributorDto('id1', 'Developer'), new ContributorDto('id1', 'Developer'),
new ContributorDto(userId, 'Developer') new ContributorDto(modifier, 'Developer')
]; ];
let dialogs: IMock<DialogService>; let dialogs: IMock<DialogService>;
@ -109,7 +109,7 @@ describe('ContributorsState', () => {
}); });
it('should update contributor in snapshot when assigned and already added', () => { it('should update contributor in snapshot when assigned and already added', () => {
const newContributor = new ContributorDto(userId, 'Owner'); const newContributor = new ContributorDto(modifier, 'Owner');
const request = { ...newContributor }; const request = { ...newContributor };
const response = { contributorId: newContributor.contributorId, isCreated: true }; const response = { contributorId: newContributor.contributorId, isCreated: true };

10
src/Squidex/app/shared/state/rules.state.spec.ts

@ -87,14 +87,14 @@ describe('RulesState', () => {
}); });
it('should add rule to snapshot when created', () => { it('should add rule to snapshot when created', () => {
const newRule = new RuleDto('id3', creator, creator, creation, creation, version, false, {}, 'trigger3', {}, 'action3'); const newRule = new RuleDto('id3', modifier, modifier, modified, modified, version, true, { value: 3 }, 'trigger3', { value: 1 }, 'action3');
const request = { action: {}, trigger: {} }; const request = { trigger: { triggerType: 'trigger3', value: 3 }, action: { actionType: 'action3', value: 1 } };
rulesService.setup(x => x.postRule(app, request, modifier, creation)) rulesService.setup(x => x.postRule(app, request))
.returns(() => of(newRule)); .returns(() => of(versioned(version, { id: 'id3' })));
rulesState.create(request, creation).subscribe(); rulesState.create(request, modified).subscribe();
expect(rulesState.snapshot.rules.values).toEqual([...oldRules, newRule]); expect(rulesState.snapshot.rules.values).toEqual([...oldRules, newRule]);
}); });

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

@ -15,16 +15,18 @@ import {
ImmutableArray, ImmutableArray,
shareSubscribed, shareSubscribed,
State, State,
Version Version,
Versioned
} from '@app/framework'; } from '@app/framework';
import { AuthService} from './../services/auth.service'; import { AuthService} from './../services/auth.service';
import { AppsState } from './apps.state'; import { AppsState } from './apps.state';
import { import {
CreateRuleDto, RuleCreatedDto,
RuleDto, RuleDto,
RulesService RulesService,
UpsertRuleDto
} from './../services/rules.service'; } from './../services/rules.service';
interface Snapshot { interface Snapshot {
@ -76,11 +78,11 @@ export class RulesState extends State<Snapshot> {
shareSubscribed(this.dialogs)); shareSubscribed(this.dialogs));
} }
public create(request: CreateRuleDto, now?: DateTime): Observable<RuleDto> { public create(request: UpsertRuleDto, now?: DateTime): Observable<RuleDto> {
return this.rulesService.postRule(this.appName, request, this.user, now || DateTime.now()).pipe( return this.rulesService.postRule(this.appName, request).pipe(
tap(rule => { tap(response => {
this.next(s => { this.next(s => {
const rules = s.rules.push(rule); const rules = s.rules.push(createRule(request, response, this.user, now));
return { ...s, rules }; return { ...s, rules };
}); });
@ -178,3 +180,26 @@ const setEnabled = (rule: RuleDto, isEnabled: boolean, user: string, version: Ve
lastModifiedBy: user, lastModifiedBy: user,
version version
}); });
function createRule(request: UpsertRuleDto, { payload, version }: Versioned<RuleCreatedDto>, user: string, now?: DateTime) {
now = now || DateTime.now();
const { triggerType, ...trigger } = request.trigger;
const { actionType, ...action } = request.action;
const rule = new RuleDto(
payload.id,
user,
user,
now,
now,
version,
true,
trigger,
triggerType,
action,
actionType);
return rule;
}

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

@ -11,14 +11,13 @@ import { IMock, It, Mock, Times } from 'typemoq';
import { SchemasState } from './schemas.state'; import { SchemasState } from './schemas.state';
import { import {
AddFieldDto,
createProperties, createProperties,
CreateSchemaDto,
DialogService, DialogService,
NestedFieldDto, NestedFieldDto,
RootFieldDto, RootFieldDto,
SchemaDetailsDto, SchemaDetailsDto,
SchemaDto, SchemaDto,
SchemaPropertiesDto,
SchemasService, SchemasService,
UpdateSchemaCategoryDto, UpdateSchemaCategoryDto,
versioned versioned
@ -298,17 +297,17 @@ describe('SchemasState', () => {
}); });
it('should add schema to snapshot when created', () => { it('should add schema to snapshot when created', () => {
const request = new CreateSchemaDto('newName'); const request = { name: 'newName' };
const result = new SchemaDetailsDto('id4', 'newName', '', {}, false, false, modified, modifier, modified, modifier, version); const result = new SchemaDetailsDto('id4', 'newName', '', new SchemaPropertiesDto(), false, false, modified, modifier, modified, modifier, version);
schemasService.setup(x => x.postSchema(app, request, modifier, modified)) schemasService.setup(x => x.postSchema(app, request))
.returns(() => of(result)).verifiable(); .returns(() => of(versioned(version, { id: 'id4' }))).verifiable();
schemasState.create(request, modified).subscribe(); schemasState.create(request, modified).subscribe();
expect(schemasState.snapshot.schemas.values.length).toBe(3); expect(schemasState.snapshot.schemas.values.length).toBe(3);
expect(schemasState.snapshot.schemas.at(2)).toBe(result); expect(schemasState.snapshot.schemas.at(2)).toEqual(result);
}); });
it('should remove schema from snapshot when deleted', () => { it('should remove schema from snapshot when deleted', () => {
@ -322,7 +321,7 @@ describe('SchemasState', () => {
}); });
it('should add field and update user info when field added', () => { it('should add field and update user info when field added', () => {
const request = new AddFieldDto(field1.name, field1.partitioning, field1.properties); const request = { ...field1 };
const newField = new RootFieldDto(3, '3', createProperties('String'), 'invariant'); const newField = new RootFieldDto(3, '3', createProperties('String'), 'invariant');
@ -338,7 +337,7 @@ describe('SchemasState', () => {
}); });
it('should add field and update user info when nested field added', () => { it('should add field and update user info when nested field added', () => {
const request = new AddFieldDto(field1.name, field1.partitioning, field1.properties); const request = { ...field1 };
const newField = new NestedFieldDto(3, '3', createProperties('String'), 2); const newField = new NestedFieldDto(3, '3', createProperties('String'), 2);

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

@ -17,7 +17,8 @@ import {
shareSubscribed, shareSubscribed,
State, State,
Types, Types,
Version Version,
Versioned
} from '@app/framework'; } from '@app/framework';
import { AuthService } from './../services/auth.service'; import { AuthService } from './../services/auth.service';
@ -29,6 +30,7 @@ import {
FieldDto, FieldDto,
NestedFieldDto, NestedFieldDto,
RootFieldDto, RootFieldDto,
SchemaCreatedDto,
SchemaDetailsDto, SchemaDetailsDto,
SchemaDto, SchemaDto,
SchemaPropertiesDto, SchemaPropertiesDto,
@ -136,10 +138,10 @@ export class SchemasState extends State<Snapshot> {
} }
public create(request: CreateSchemaDto, now?: DateTime): Observable<SchemaDetailsDto> { public create(request: CreateSchemaDto, now?: DateTime): Observable<SchemaDetailsDto> {
return this.schemasService.postSchema(this.appName, request, this.user, now || DateTime.now()).pipe( return this.schemasService.postSchema(this.appName, request).pipe(
tap(payload => { tap(response => {
this.next(s => { this.next(s => {
const schemas = s.schemas.push(payload).sortByStringAsc(x => x.displayName); const schemas = s.schemas.push(createSchema(request, response, this.user, now)).sortByStringAsc(x => x.displayName);
return { ...s, schemas }; return { ...s, schemas };
}); });
@ -513,4 +515,21 @@ const pidof = <T extends FieldDto>(field: T) =>
Types.is(field, NestedFieldDto) ? field.parentId : undefined; Types.is(field, NestedFieldDto) ? field.parentId : undefined;
const pid = (field?: RootFieldDto) => const pid = (field?: RootFieldDto) =>
field ? field.fieldId : undefined; field ? field.fieldId : undefined;
function createSchema(request: CreateSchemaDto, { payload, version }: Versioned<SchemaCreatedDto>, user: string, now?: DateTime) {
now = now || DateTime.now();
const schema = new SchemaDetailsDto(
payload.id,
request.name, '',
request.properties || new SchemaPropertiesDto(),
request.isSingleton === true,
false,
now, user,
now, user,
version,
request.fields || []);
return schema;
}

24
src/Squidex/package-lock.json

@ -1670,9 +1670,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "11.13.8", "version": "12.0.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.8.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.0.tgz",
"integrity": "sha512-szA3x/3miL90ZJxUCzx9haNbK5/zmPieGraZEe4WI+3srN0eGLiT22NXeMHmyhNEopn+IrxqMc7wdVwvPl8meg==", "integrity": "sha512-Jrb/x3HT4PTJp6a4avhmJCDEVrPdqLfl3e8GGMbpkGGdwAV5UGlIs4vVEfsHHfylZVOKZWpOqmqFH8CbfOZ6kg==",
"dev": true "dev": true
}, },
"@types/prop-types": { "@types/prop-types": {
@ -1688,9 +1688,9 @@
"dev": true "dev": true
}, },
"@types/react": { "@types/react": {
"version": "16.8.14", "version": "16.8.16",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.14.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.16.tgz",
"integrity": "sha512-26tFVJ1omGmzIdFTFmnC5zhz1GTaqCjxgUxV4KzWvsybF42P7/j4RBn6UeO3KbHPXqKWZszMXMoI65xIWm954A==", "integrity": "sha512-A0+6kS6zwPtvubOLiCJmZ8li5bm3wKIkoKV0h3RdMDOnCj9cYkUnj3bWbE03/lcICdQmwBmUfoFiHeNhbFiyHQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/prop-types": "*", "@types/prop-types": "*",
@ -15023,9 +15023,9 @@
} }
}, },
"ts-loader": { "ts-loader": {
"version": "5.4.4", "version": "5.4.5",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.4.4.tgz", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.4.5.tgz",
"integrity": "sha512-haLwWMts/tCruBFbLUWOR9hZezZ89TMW3EntrAEtYARDnPZSQt6dPA5tN3fvlsmUIYXVWDHO5SUhw8ndAomVlw==", "integrity": "sha512-XYsjfnRQCBum9AMRZpk2rTYSVpdZBpZK+kDh0TeT3kxmQNBDVIeUjdPjY5RZry4eIAb8XHc4gYSUiUWPYvzSRw==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "^2.3.0", "chalk": "^2.3.0",
@ -18772,9 +18772,9 @@
"dev": true "dev": true
}, },
"zone.js": { "zone.js": {
"version": "0.9.0", "version": "0.9.1",
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.9.0.tgz", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.9.1.tgz",
"integrity": "sha512-EfygvVnLxPSCMSgJ4h7SoY+XNr7ybdwvvwEQ70lvMFl9coNnciXSyWi8Kg6znK1ubyUSffkCKvleSQpLuUKw0Q==" "integrity": "sha512-GkPiJL8jifSrKReKaTZ5jkhrMEgXbXYC+IPo1iquBjayRa0q86w3Dipjn8b415jpitMExe9lV8iTsv8tk3DGag=="
} }
} }
} }

8
src/Squidex/package.json

@ -44,7 +44,7 @@
"slugify": "1.3.4", "slugify": "1.3.4",
"sortablejs": "1.9.0", "sortablejs": "1.9.0",
"tslib": "1.9.3", "tslib": "1.9.3",
"zone.js": "0.9.0" "zone.js": "0.9.1"
}, },
"devDependencies": { "devDependencies": {
"@angular/compiler": "7.2.14", "@angular/compiler": "7.2.14",
@ -54,8 +54,8 @@
"@types/jasmine": "3.3.12", "@types/jasmine": "3.3.12",
"@types/marked": "0.6.5", "@types/marked": "0.6.5",
"@types/mousetrap": "1.6", "@types/mousetrap": "1.6",
"@types/node": "11.13.8", "@types/node": "12.0.0",
"@types/react": "16.8.14", "@types/react": "16.8.16",
"@types/react-dom": "16.8.4", "@types/react-dom": "16.8.4",
"@types/sortablejs": "1.7.2", "@types/sortablejs": "1.7.2",
"angular-router-loader": "0.8.5", "angular-router-loader": "0.8.5",
@ -91,7 +91,7 @@
"sass-lint": "1.13.1", "sass-lint": "1.13.1",
"sass-loader": "7.1.0", "sass-loader": "7.1.0",
"style-loader": "0.23.1", "style-loader": "0.23.1",
"ts-loader": "^5.4.4", "ts-loader": "5.4.5",
"tsconfig-paths-webpack-plugin": "3.2.0", "tsconfig-paths-webpack-plugin": "3.2.0",
"tslint": "5.16.0", "tslint": "5.16.0",
"tslint-webpack-plugin": "2.0.4", "tslint-webpack-plugin": "2.0.4",

4
src/Squidex/tslint.json

@ -65,6 +65,7 @@
], ],
"no-input-rename": false, "no-input-rename": false,
"no-output-rename": false, "no-output-rename": false,
"no-pipe-impure": true,
"no-shadowed-variable": true, "no-shadowed-variable": true,
"no-string-literal": false, "no-string-literal": false,
"no-switch-case-fall-through": true, "no-switch-case-fall-through": true,
@ -103,7 +104,8 @@
true, true,
"always" "always"
], ],
"templates-no-negated-async": true, "template-no-negated-async": true,
"template-use-track-by-function": true,
"trailing-comma": [ "trailing-comma": [
true, true,
{ {

Loading…
Cancel
Save