diff --git a/src/Squidex/app/features/administration/module.ts b/src/Squidex/app/features/administration/module.ts index a97719a34..b130cb62f 100644 --- a/src/Squidex/app/features/administration/module.ts +++ b/src/Squidex/app/features/administration/module.ts @@ -87,4 +87,4 @@ const routes: Routes = [ UsersState ] }) -export class SqxFeatureAdministrationModule { } \ No newline at end of file +export class SqxFeatureAdministrationModule {} \ No newline at end of file diff --git a/src/Squidex/app/features/api/module.ts b/src/Squidex/app/features/api/module.ts index f4701b425..471c3efd6 100644 --- a/src/Squidex/app/features/api/module.ts +++ b/src/Squidex/app/features/api/module.ts @@ -45,4 +45,4 @@ const routes: Routes = [ GraphQLPageComponent ] }) -export class SqxFeatureApiModule { } \ No newline at end of file +export class SqxFeatureApiModule {} \ No newline at end of file diff --git a/src/Squidex/app/features/apps/module.ts b/src/Squidex/app/features/apps/module.ts index 16387df1f..cf43f604e 100644 --- a/src/Squidex/app/features/apps/module.ts +++ b/src/Squidex/app/features/apps/module.ts @@ -35,4 +35,4 @@ const routes: Routes = [ OnboardingDialogComponent ] }) -export class SqxFeatureAppsModule { } \ No newline at end of file +export class SqxFeatureAppsModule {} \ No newline at end of file diff --git a/src/Squidex/app/features/assets/module.ts b/src/Squidex/app/features/assets/module.ts index 755987200..bcfb24292 100644 --- a/src/Squidex/app/features/assets/module.ts +++ b/src/Squidex/app/features/assets/module.ts @@ -39,4 +39,4 @@ const routes: Routes = [ AssetsPageComponent ] }) -export class SqxFeatureAssetsModule { } \ No newline at end of file +export class SqxFeatureAssetsModule {} \ No newline at end of file diff --git a/src/Squidex/app/features/content/module.ts b/src/Squidex/app/features/content/module.ts index 4c6f8a9d1..488f2a6c5 100644 --- a/src/Squidex/app/features/content/module.ts +++ b/src/Squidex/app/features/content/module.ts @@ -128,4 +128,4 @@ const routes: Routes = [ SchemasPageComponent ] }) -export class SqxFeatureContentModule { } \ No newline at end of file +export class SqxFeatureContentModule {} \ No newline at end of file diff --git a/src/Squidex/app/features/dashboard/module.ts b/src/Squidex/app/features/dashboard/module.ts index 0942cc3b6..d821eb1f6 100644 --- a/src/Squidex/app/features/dashboard/module.ts +++ b/src/Squidex/app/features/dashboard/module.ts @@ -33,4 +33,4 @@ const routes: Routes = [ DashboardPageComponent ] }) -export class SqxFeatureDashboardModule { } \ No newline at end of file +export class SqxFeatureDashboardModule {} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/module.ts b/src/Squidex/app/features/rules/module.ts index 7ee3218b3..78b84b860 100644 --- a/src/Squidex/app/features/rules/module.ts +++ b/src/Squidex/app/features/rules/module.ts @@ -66,4 +66,4 @@ const routes: Routes = [ UsageTriggerComponent ] }) -export class SqxFeatureRulesModule { } \ No newline at end of file +export class SqxFeatureRulesModule {} \ No newline at end of file diff --git a/src/Squidex/app/features/schemas/module.ts b/src/Squidex/app/features/schemas/module.ts index b328efd96..ad7b2c59e 100644 --- a/src/Squidex/app/features/schemas/module.ts +++ b/src/Squidex/app/features/schemas/module.ts @@ -112,4 +112,4 @@ const routes: Routes = [ TagsValidationComponent ] }) -export class SqxFeatureSchemasModule { } \ No newline at end of file +export class SqxFeatureSchemasModule {} \ No newline at end of file diff --git a/src/Squidex/app/features/settings/module.ts b/src/Squidex/app/features/settings/module.ts index a0b8214be..d8c37211d 100644 --- a/src/Squidex/app/features/settings/module.ts +++ b/src/Squidex/app/features/settings/module.ts @@ -209,4 +209,4 @@ const routes: Routes = [ WorkflowStepComponent ] }) -export class SqxFeatureSettingsModule { } \ No newline at end of file +export class SqxFeatureSettingsModule {} \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/code.component.ts b/src/Squidex/app/framework/angular/code.component.ts index f78d5b637..7d3a9dc99 100644 --- a/src/Squidex/app/framework/angular/code.component.ts +++ b/src/Squidex/app/framework/angular/code.component.ts @@ -13,4 +13,4 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; templateUrl: './code.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) -export class CodeComponent { } \ No newline at end of file +export class CodeComponent {} \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/pipes/name.pipe.spec.ts b/src/Squidex/app/framework/angular/pipes/name.pipe.spec.ts index 72760d786..ab816a1f4 100644 --- a/src/Squidex/app/framework/angular/pipes/name.pipe.spec.ts +++ b/src/Squidex/app/framework/angular/pipes/name.pipe.spec.ts @@ -54,6 +54,6 @@ describe('DisplayNamePipe', () => { it('should return empty string if also fallback not found', () => { const pipe = new DisplayNamePipe(); - expect(pipe.transform({ })).toBe(''); + expect(pipe.transform({})).toBe(''); }); }); \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/routers/router-utils.ts b/src/Squidex/app/framework/angular/routers/router-utils.ts index d81393c22..ef6d0cff8 100644 --- a/src/Squidex/app/framework/angular/routers/router-utils.ts +++ b/src/Squidex/app/framework/angular/routers/router-utils.ts @@ -10,7 +10,7 @@ import { ActivatedRoute, ActivatedRouteSnapshot, Data, Params, RouterStateSnapsh export function allData(value: ActivatedRouteSnapshot | ActivatedRoute): Data { let snapshot: ActivatedRouteSnapshot | null = value['snapshot'] || value; - const result: { [key: string]: any } = { }; + const result: { [key: string]: any } = {}; while (snapshot) { for (let key in snapshot.data) { @@ -27,7 +27,7 @@ export function allData(value: ActivatedRouteSnapshot | ActivatedRoute): Data { export function allParams(value: ActivatedRouteSnapshot | ActivatedRoute): Params { let snapshot: ActivatedRouteSnapshot | null = value['snapshot'] || value; - const result: { [key: string]: any } = { }; + const result: { [key: string]: any } = {}; while (snapshot) { for (let key in snapshot.params) { diff --git a/src/Squidex/app/framework/configurations.ts b/src/Squidex/app/framework/configurations.ts index f46f4857a..4bb75d0bf 100644 --- a/src/Squidex/app/framework/configurations.ts +++ b/src/Squidex/app/framework/configurations.ts @@ -35,13 +35,13 @@ export class CurrencyConfig { } export class AnalyticsIdConfig { - constructor(public value: string) { } + constructor(public value: string) {} } export class DecimalSeparatorConfig { - constructor(public readonly value: string) { } + constructor(public readonly value: string) {} } export class ProductionModeConfig { - constructor(public readonly isProductionMode: boolean) { } + constructor(public readonly isProductionMode: boolean) {} } \ No newline at end of file diff --git a/src/Squidex/app/framework/services/message-bus.service.spec.ts b/src/Squidex/app/framework/services/message-bus.service.spec.ts index 120b36cda..840f99b0c 100644 --- a/src/Squidex/app/framework/services/message-bus.service.spec.ts +++ b/src/Squidex/app/framework/services/message-bus.service.spec.ts @@ -7,8 +7,8 @@ import { MessageBus, MessageBusFactory } from './message-bus.service'; -class Event1 { } -class Event2 { } +class Event1 {} +class Event2 {} describe('MessageBus', () => { it('should instantiate from factory', () => { diff --git a/src/Squidex/app/framework/services/title.service.ts b/src/Squidex/app/framework/services/title.service.ts index 6cd37d9a2..7fa7e7f49 100644 --- a/src/Squidex/app/framework/services/title.service.ts +++ b/src/Squidex/app/framework/services/title.service.ts @@ -22,7 +22,7 @@ export const TitleServiceFactory = (titles: TitlesConfig) => { @Injectable() export class TitleService { - constructor(private readonly titles: TitlesConfig) { } + constructor(private readonly titles: TitlesConfig) {} public setTitle(key: string, parameters?: { [key: string]: string }) { let title = this.titles.value[key] || key; diff --git a/src/Squidex/app/framework/utils/interpolator.spec.ts b/src/Squidex/app/framework/utils/interpolator.spec.ts index 6da899ee1..19ffbb9f7 100644 --- a/src/Squidex/app/framework/utils/interpolator.spec.ts +++ b/src/Squidex/app/framework/utils/interpolator.spec.ts @@ -58,7 +58,7 @@ describe('interpolate', () => { }); it('should return undefined if it resolved to object', () => { - const result = interpolate('hello ${data}', { data: { } }); + const result = interpolate('hello ${data}', { data: {} }); expect(result).toEqual('hello undefined'); }); diff --git a/src/Squidex/app/shared/internal.ts b/src/Squidex/app/shared/internal.ts index 9050ba58a..71a0a1561 100644 --- a/src/Squidex/app/shared/internal.ts +++ b/src/Squidex/app/shared/internal.ts @@ -63,6 +63,7 @@ export * from './state/rules.state'; export * from './state/schemas.forms'; export * from './state/schemas.state'; export * from './state/ui.state'; +export * from './state/workflows.state'; export * from './utils/messages'; diff --git a/src/Squidex/app/shared/module.ts b/src/Squidex/app/shared/module.ts index 19fb7f461..76bd8171a 100644 --- a/src/Squidex/app/shared/module.ts +++ b/src/Squidex/app/shared/module.ts @@ -93,7 +93,9 @@ import { UserPicturePipe, UserPictureRefPipe, UsersProviderService, - UsersService + UsersService, + WorkflowsService, + WorkflowsState } from './declarations'; @NgModule({ @@ -224,6 +226,8 @@ export class SqxSharedModule { UsagesService, UsersProviderService, UsersService, + WorkflowsService, + WorkflowsState, { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, diff --git a/src/Squidex/app/shared/services/graphql.service.spec.ts b/src/Squidex/app/shared/services/graphql.service.spec.ts index 4221f51a9..d90390351 100644 --- a/src/Squidex/app/shared/services/graphql.service.spec.ts +++ b/src/Squidex/app/shared/services/graphql.service.spec.ts @@ -32,7 +32,7 @@ describe('GraphQlService', () => { let graphQlResult: any = null; - graphQlService.query('my-app', { }).subscribe(result => { + graphQlService.query('my-app', {}).subscribe(result => { graphQlResult = result; }); diff --git a/src/Squidex/app/shared/services/ui.service.ts b/src/Squidex/app/shared/services/ui.service.ts index 13b23b28d..cdadc29a1 100644 --- a/src/Squidex/app/shared/services/ui.service.ts +++ b/src/Squidex/app/shared/services/ui.service.ts @@ -41,7 +41,7 @@ export class UIService { return this.http.get(url).pipe( catchError(() => { - return of({ }); + return of({}); })); } diff --git a/src/Squidex/app/shared/services/workflows.service.spec.ts b/src/Squidex/app/shared/services/workflows.service.spec.ts index c6d04745a..f6584bd8a 100644 --- a/src/Squidex/app/shared/services/workflows.service.spec.ts +++ b/src/Squidex/app/shared/services/workflows.service.spec.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { WorkflowDto } from '@app/shared/internal'; +import { WorkflowDto, WorkflowPayload } from '@app/shared/internal'; describe('Workflow', () => { it('should create empty workflow', () => { @@ -296,4 +296,22 @@ describe('Workflow', () => { initial: '2' }); }); -}); \ No newline at end of file +}); + +export function createWorkflow(name: string): WorkflowPayload { + return { + workflow: new WorkflowDto({ + update: { method: 'PUT', href: '/api/workflows' } + }, + `${name}1`, + [ + { name: `${name}1`, color: `${name}1`, noUpdate: true, isLocked: true }, + { name: `${name}2`, color: `${name}1`, noUpdate: true, isLocked: true } + ], + [ + { from: `${name}1`, to: `${name}2`, expression: 'Expression1', role: 'Role1' }, + { from: `${name}2`, to: `${name}1`, expression: 'Expression2', role: 'Role2' } + ]), + _links: {} + }; +} \ No newline at end of file diff --git a/src/Squidex/app/shared/state/asset-uploader.state.ts b/src/Squidex/app/shared/state/asset-uploader.state.ts index 4b16ade66..5280b2d26 100644 --- a/src/Squidex/app/shared/state/asset-uploader.state.ts +++ b/src/Squidex/app/shared/state/asset-uploader.state.ts @@ -43,7 +43,7 @@ interface Snapshot { uploads: UploadList; } -export class UploadCanceled { } +export class UploadCanceled {} type UploadList = ImmutableArray; type UploadResult = AssetDto | number; diff --git a/src/Squidex/app/shared/state/workflows.state.spec.ts b/src/Squidex/app/shared/state/workflows.state.spec.ts new file mode 100644 index 000000000..c4e52dcaa --- /dev/null +++ b/src/Squidex/app/shared/state/workflows.state.spec.ts @@ -0,0 +1,100 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { of } from 'rxjs'; +import { IMock, It, Mock, Times } from 'typemoq'; + +import { + DialogService, + versioned, + WorkflowPayload, + WorkflowsService, + WorkflowsState +} from '@app/shared/internal'; + +import { createWorkflow } from '../services/workflows.service.spec'; + +import { TestValues } from './_test-helpers'; + +describe('WorkflowsState', () => { + const { + app, + appsState, + newVersion, + version + } = TestValues; + + const oldWorkflow = createWorkflow('test'); + + let dialogs: IMock; + let workflowsService: IMock; + let workflowsState: WorkflowsState; + + beforeEach(() => { + dialogs = Mock.ofType(); + + workflowsService = Mock.ofType(); + workflowsState = new WorkflowsState(workflowsService.object, appsState.object, dialogs.object); + }); + + afterEach(() => { + workflowsService.verifyAll(); + }); + + describe('Loading', () => { + it('should load workflow', () => { + workflowsService.setup(x => x.getWorkflow(app)) + .returns(() => of(versioned(version, oldWorkflow))).verifiable(); + + workflowsState.load().subscribe(); + + expect(workflowsState.snapshot.workflow).toEqual(oldWorkflow.workflow); + expect(workflowsState.snapshot.isLoaded).toBeTruthy(); + expect(workflowsState.snapshot.version).toEqual(version); + + dialogs.verify(x => x.notifyInfo(It.isAnyString()), Times.never()); + }); + + it('should show notification on load when reload is true', () => { + workflowsService.setup(x => x.getWorkflow(app)) + .returns(() => of(versioned(version, oldWorkflow))).verifiable(); + + workflowsState.load(true).subscribe(); + + expect().nothing(); + + dialogs.verify(x => x.notifyInfo(It.isAnyString()), Times.once()); + }); + }); + + describe('Updates', () => { + beforeEach(() => { + workflowsService.setup(x => x.getWorkflow(app)) + .returns(() => of(versioned(version, oldWorkflow))).verifiable(); + + workflowsState.load().subscribe(); + }); + + it('should update workflows when saved', () => { + const updated = createWorkflow('updated'); + + const request = oldWorkflow.workflow.serialize(); + + workflowsService.setup(x => x.putWorkflow(app, oldWorkflow, request, version)) + .returns(() => of(versioned(newVersion, updated))).verifiable(); + + workflowsState.save().subscribe(); + + expectNewWorkflows(updated); + }); + + function expectNewWorkflows(updated: WorkflowPayload) { + expect(workflowsState.snapshot.workflow).toEqual(updated.workflow); + expect(workflowsState.snapshot.version).toEqual(newVersion); + } + }); +}); \ No newline at end of file diff --git a/src/Squidex/app/shared/state/workflows.state.ts b/src/Squidex/app/shared/state/workflows.state.ts new file mode 100644 index 000000000..2829f06f7 --- /dev/null +++ b/src/Squidex/app/shared/state/workflows.state.ts @@ -0,0 +1,97 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +// tslint:disable: no-shadowed-variable + +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +import { + DialogService, + shareSubscribed, + State, + Version +} from '@app/framework'; + +import { AppsState } from './apps.state'; + +import { + WorkflowDto, + WorkflowPayload, + WorkflowsService +} from './../services/workflows.service'; + +interface Snapshot { + // The current workflow. + workflow?: WorkflowDto; + + // The app version. + version: Version; + + // Indicates if the workflows are loaded. + isLoaded?: boolean; +} + +@Injectable() +export class WorkflowsState extends State { + public workflow = + this.project(x => x.workflow); + + public isLoaded = + this.project(x => !!x.isLoaded); + + constructor( + private readonly workflowsService: WorkflowsService, + private readonly appsState: AppsState, + private readonly dialogs: DialogService + ) { + super({ version: Version.EMPTY }); + } + + public load(isReload = false): Observable { + if (!isReload) { + this.resetState(); + } + + return this.workflowsService.getWorkflow(this.appName).pipe( + tap(({ version, payload }) => { + if (isReload) { + this.dialogs.notifyInfo('Workflow reloaded.'); + } + + this.replaceWorkflow(payload, version); + }), + shareSubscribed(this.dialogs)); + } + + public save(): Observable { + const workflow = this.snapshot.workflow!; + + return this.workflowsService.putWorkflow(this.appName, workflow, workflow.serialize(), this.version).pipe( + tap(({ version, payload }) => { + this.replaceWorkflow(payload, version); + }), + shareSubscribed(this.dialogs)); + } + + private replaceWorkflow(payload: WorkflowPayload, version: Version) { + const { workflow } = payload; + + this.next(s => { + return { ...s, workflow, isLoaded: true, version }; + }); + } + + private get appName() { + return this.appsState.appName; + } + + private get version() { + return this.snapshot.version; + } +} \ No newline at end of file diff --git a/src/Squidex/app/shared/utils/messages.ts b/src/Squidex/app/shared/utils/messages.ts index 3716abc49..0e0696eb8 100644 --- a/src/Squidex/app/shared/utils/messages.ts +++ b/src/Squidex/app/shared/utils/messages.ts @@ -5,4 +5,4 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -export class HistoryChannelUpdated { } \ No newline at end of file +export class HistoryChannelUpdated {} \ No newline at end of file