mirror of https://github.com/Squidex/squidex.git
14 changed files with 358 additions and 128 deletions
@ -0,0 +1,116 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
||||
|
*/ |
||||
|
|
||||
|
import { Observable } from 'rxjs'; |
||||
|
import { IMock, Mock } from 'typemoq'; |
||||
|
|
||||
|
import { |
||||
|
AppsState, |
||||
|
AppContributorDto, |
||||
|
AppContributorsDto, |
||||
|
AppContributorsService, |
||||
|
AuthService, |
||||
|
ContributorsState, |
||||
|
DialogService, |
||||
|
Version, |
||||
|
Versioned |
||||
|
} from '@app/shared'; |
||||
|
|
||||
|
describe('ContributorsState', () => { |
||||
|
const app = 'my-app'; |
||||
|
const version = new Version('1'); |
||||
|
const newVersion = new Version('2'); |
||||
|
|
||||
|
const oldContributors = [ |
||||
|
new AppContributorDto('id1', 'Developer'), |
||||
|
new AppContributorDto('id2', 'Developer') |
||||
|
]; |
||||
|
|
||||
|
let dialogs: IMock<DialogService>; |
||||
|
let appsState: IMock<AppsState>; |
||||
|
let authService: IMock<AuthService>; |
||||
|
let contributorsService: IMock<AppContributorsService>; |
||||
|
let contributorsState: ContributorsState; |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
dialogs = Mock.ofType<DialogService>(); |
||||
|
|
||||
|
authService = Mock.ofType<AuthService>(); |
||||
|
|
||||
|
authService.setup(x => x.user) |
||||
|
.returns(() => <any>{ id: 'id2' }); |
||||
|
|
||||
|
appsState = Mock.ofType<AppsState>(); |
||||
|
|
||||
|
appsState.setup(x => x.appName) |
||||
|
.returns(() => app); |
||||
|
|
||||
|
contributorsService = Mock.ofType<AppContributorsService>(); |
||||
|
|
||||
|
contributorsService.setup(x => x.getContributors(app)) |
||||
|
.returns(() => Observable.of(new AppContributorsDto(oldContributors, 3, version))); |
||||
|
|
||||
|
contributorsState = new ContributorsState(contributorsService.object, appsState.object, authService.object, dialogs.object); |
||||
|
contributorsState.load().subscribe(); |
||||
|
}); |
||||
|
|
||||
|
it('should load contributors', () => { |
||||
|
expect(contributorsState.snapshot.contributors.values).toEqual(oldContributors.map(x => c(x))); |
||||
|
expect(contributorsState.snapshot.isLoaded).toBeTruthy(); |
||||
|
expect(contributorsState.snapshot.isMaxReached).toBeFalsy(); |
||||
|
expect(contributorsState.snapshot.maxContributors).toBe(3); |
||||
|
expect(contributorsState.snapshot.version).toEqual(version); |
||||
|
}); |
||||
|
|
||||
|
it('should add contributor to snapshot', () => { |
||||
|
const newContributor = new AppContributorDto('id3', 'Developer'); |
||||
|
|
||||
|
const request = new AppContributorDto('mail2stehle@gmail.com', 'Developer'); |
||||
|
|
||||
|
contributorsService.setup(x => x.postContributor(app, request, version)) |
||||
|
.returns(() => Observable.of(new Versioned<AppContributorDto>(newVersion, newContributor))); |
||||
|
|
||||
|
contributorsState.assign(request).subscribe(); |
||||
|
|
||||
|
expect(contributorsState.snapshot.contributors.values).toEqual([...oldContributors.map(x => c(x)), c(newContributor)]); |
||||
|
expect(contributorsState.snapshot.isLoaded).toBeTruthy(); |
||||
|
expect(contributorsState.snapshot.isMaxReached).toBeTruthy(); |
||||
|
expect(contributorsState.snapshot.maxContributors).toBe(3); |
||||
|
expect(contributorsState.snapshot.version).toEqual(newVersion); |
||||
|
}); |
||||
|
|
||||
|
it('should update contributor in snapshot', () => { |
||||
|
const newContributor = new AppContributorDto('id2', 'Owner'); |
||||
|
|
||||
|
const request = new AppContributorDto('mail2stehle@gmail.com', 'Owner'); |
||||
|
|
||||
|
contributorsService.setup(x => x.postContributor(app, request, version)) |
||||
|
.returns(() => Observable.of(new Versioned<AppContributorDto>(newVersion, newContributor))); |
||||
|
|
||||
|
contributorsState.assign(request).subscribe(); |
||||
|
|
||||
|
expect(contributorsState.snapshot.contributors.values).toEqual([c(oldContributors[0]), c(newContributor)]); |
||||
|
expect(contributorsState.snapshot.isLoaded).toBeTruthy(); |
||||
|
expect(contributorsState.snapshot.isMaxReached).toBeFalsy(); |
||||
|
expect(contributorsState.snapshot.maxContributors).toBe(3); |
||||
|
expect(contributorsState.snapshot.version).toEqual(newVersion); |
||||
|
}); |
||||
|
|
||||
|
it('should remove contributor from snapshot', () => { |
||||
|
contributorsService.setup(x => x.deleteContributor(app, oldContributors[0].contributorId, version)) |
||||
|
.returns(() => Observable.of(new Versioned<any>(newVersion, {}))); |
||||
|
|
||||
|
contributorsState.revoke(oldContributors[0]).subscribe(); |
||||
|
|
||||
|
expect(contributorsState.snapshot.contributors.values).toEqual([c(oldContributors[1])]); |
||||
|
expect(contributorsState.snapshot.version).toEqual(newVersion); |
||||
|
}); |
||||
|
|
||||
|
function c(contributor: AppContributorDto) { |
||||
|
return { contributor, isCurrentUser: contributor.contributorId === 'id2' }; |
||||
|
} |
||||
|
}); |
||||
@ -0,0 +1,137 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
||||
|
*/ |
||||
|
|
||||
|
import { Injectable } from '@angular/core'; |
||||
|
import { FormBuilder, Validators, FormGroup } from '@angular/forms'; |
||||
|
import { Observable } from 'rxjs'; |
||||
|
|
||||
|
import '@app/framework/utils/rxjs-extensions'; |
||||
|
|
||||
|
import { |
||||
|
DialogService, |
||||
|
ImmutableArray, |
||||
|
Form, |
||||
|
State, |
||||
|
Version |
||||
|
} from '@app/framework'; |
||||
|
|
||||
|
import { AuthService } from './../services/auth.service'; |
||||
|
import { AppsState } from './apps.state'; |
||||
|
import { AppContributorDto, AppContributorsService } from './../services/app-contributors.service'; |
||||
|
|
||||
|
export class AssignContributorForm extends Form<FormGroup> { |
||||
|
public hasNoUser = |
||||
|
this.form.controls['user'].valueChanges.startWith(null).map(x => !x); |
||||
|
|
||||
|
constructor(formBuilder: FormBuilder) { |
||||
|
super(formBuilder.group({ |
||||
|
user: [null, |
||||
|
[ |
||||
|
Validators.required |
||||
|
] |
||||
|
] |
||||
|
})); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
interface SnapshotContributor { |
||||
|
contributor: AppContributorDto; |
||||
|
|
||||
|
isCurrentUser: boolean; |
||||
|
} |
||||
|
|
||||
|
interface Snapshot { |
||||
|
contributors: ImmutableArray<SnapshotContributor>; |
||||
|
|
||||
|
isLoaded: boolean; |
||||
|
isMaxReached: boolean; |
||||
|
|
||||
|
maxContributors: number; |
||||
|
|
||||
|
version: Version; |
||||
|
} |
||||
|
|
||||
|
@Injectable() |
||||
|
export class ContributorsState extends State<Snapshot> { |
||||
|
public contributors = |
||||
|
this.changes.map(x => x.contributors); |
||||
|
|
||||
|
public isLoaded = |
||||
|
this.changes.map(x => x.isLoaded); |
||||
|
|
||||
|
public isMaxReached = |
||||
|
this.changes.map(x => x.isMaxReached); |
||||
|
|
||||
|
public maxContributors = |
||||
|
this.changes.map(x => x.maxContributors); |
||||
|
|
||||
|
constructor( |
||||
|
private readonly appContributorsService: AppContributorsService, |
||||
|
private readonly appsState: AppsState, |
||||
|
private readonly authState: AuthService, |
||||
|
private readonly dialogs: DialogService |
||||
|
) { |
||||
|
super({ contributors: ImmutableArray.empty(), version: new Version(''), isLoaded: false, isMaxReached: true, maxContributors: -1 }); |
||||
|
} |
||||
|
|
||||
|
public load(): Observable<any> { |
||||
|
return this.appContributorsService.getContributors(this.appName) |
||||
|
.do(dtos => { |
||||
|
const contributors = ImmutableArray.of(dtos.contributors.map(x => this.createContributor(x))); |
||||
|
|
||||
|
this.replaceContributors(contributors, dtos.version, dtos.maxContributors); |
||||
|
}) |
||||
|
.notify(this.dialogs); |
||||
|
} |
||||
|
|
||||
|
public revoke(contributor: AppContributorDto): Observable<any> { |
||||
|
return this.appContributorsService.deleteContributor(this.appName, contributor.contributorId, this.snapshot.version) |
||||
|
.do(dto => { |
||||
|
const contributors = this.snapshot.contributors.filter(x => x.contributor.contributorId !== contributor.contributorId); |
||||
|
|
||||
|
this.replaceContributors(contributors, dto.version); |
||||
|
}) |
||||
|
.notify(this.dialogs); |
||||
|
} |
||||
|
|
||||
|
public assign(request: AppContributorDto): Observable<any> { |
||||
|
return this.appContributorsService.postContributor(this.appName, request, this.snapshot.version) |
||||
|
.do(dto => { |
||||
|
const contributor = this.createContributor(new AppContributorDto(dto.payload.contributorId, request.permission)); |
||||
|
|
||||
|
let contributors = this.snapshot.contributors; |
||||
|
|
||||
|
if (contributors.find(x => x.contributor.contributorId === dto.payload.contributorId)) { |
||||
|
contributors = contributors.map(c => c.contributor.contributorId === dto.payload.contributorId ? contributor : c); |
||||
|
} else { |
||||
|
contributors = contributors.push(contributor); |
||||
|
} |
||||
|
|
||||
|
this.replaceContributors(contributors, dto.version); |
||||
|
}) |
||||
|
.notify(this.dialogs); |
||||
|
} |
||||
|
|
||||
|
private replaceContributors(contributors: ImmutableArray<SnapshotContributor>, version: Version, maxContributors?: number) { |
||||
|
this.next(s => { |
||||
|
maxContributors = maxContributors || s.maxContributors; |
||||
|
|
||||
|
const isLoaded = true; |
||||
|
const isMaxReached = maxContributors > 0 && maxContributors <= contributors.length; |
||||
|
|
||||
|
return { ...s, contributors, maxContributors, isLoaded, isMaxReached, version }; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private get appName() { |
||||
|
return this.appsState.appName; |
||||
|
} |
||||
|
|
||||
|
private createContributor(contributor: AppContributorDto): SnapshotContributor { |
||||
|
return { contributor, isCurrentUser: contributor.contributorId === this.authState.user!.id }; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue