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