mirror of https://github.com/Squidex/squidex.git
43 changed files with 376 additions and 283 deletions
@ -0,0 +1,99 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Observable } from 'rxjs'; |
|||
import { IMock, Mock } from 'typemoq'; |
|||
|
|||
import { |
|||
AppsState, |
|||
AppClientDto, |
|||
AppClientsDto, |
|||
AppClientsService, |
|||
ClientsState, |
|||
DialogService, |
|||
UpdateAppClientDto, |
|||
Version, |
|||
Versioned, |
|||
CreateAppClientDto |
|||
} from '@app/shared'; |
|||
|
|||
describe('ClientsState', () => { |
|||
const app = 'my-app'; |
|||
const version = new Version('1'); |
|||
const newVersion = new Version('2'); |
|||
|
|||
const oldClients = [ |
|||
new AppClientDto('id1', 'name1', 'secret1', 'Developer'), |
|||
new AppClientDto('id2', 'name2', 'secret2', 'Developer') |
|||
]; |
|||
|
|||
let dialogs: IMock<DialogService>; |
|||
let appsState: IMock<AppsState>; |
|||
let clientsService: IMock<AppClientsService>; |
|||
let clientsState: ClientsState; |
|||
|
|||
beforeEach(() => { |
|||
dialogs = Mock.ofType<DialogService>(); |
|||
|
|||
appsState = Mock.ofType<AppsState>(); |
|||
|
|||
appsState.setup(x => x.appName) |
|||
.returns(() => app); |
|||
|
|||
clientsService = Mock.ofType<AppClientsService>(); |
|||
|
|||
clientsService.setup(x => x.getClients(app)) |
|||
.returns(() => Observable.of(new AppClientsDto(oldClients, version))); |
|||
|
|||
clientsState = new ClientsState(clientsService.object, appsState.object, dialogs.object); |
|||
clientsState.load().subscribe(); |
|||
}); |
|||
|
|||
it('should load clients', () => { |
|||
expect(clientsState.snapshot.clients.values).toEqual(oldClients); |
|||
expect(clientsState.snapshot.version).toEqual(version); |
|||
}); |
|||
|
|||
it('should add client to snapshot', () => { |
|||
const newClient = new AppClientDto('id3', 'name3', 'secret3', 'Developer'); |
|||
|
|||
const request = new CreateAppClientDto('id3'); |
|||
|
|||
clientsService.setup(x => x.postClient(app, request, version)) |
|||
.returns(() => Observable.of(new Versioned<AppClientDto>(newVersion, newClient))); |
|||
|
|||
clientsState.attach(request).subscribe(); |
|||
|
|||
expect(clientsState.snapshot.clients.values).toEqual([...oldClients, newClient]); |
|||
expect(clientsState.snapshot.version).toEqual(newVersion); |
|||
}); |
|||
|
|||
it('should update client in snapshot', () => { |
|||
const request = new UpdateAppClientDto('NewName', 'NewPermission'); |
|||
|
|||
clientsService.setup(x => x.putClient(app, oldClients[0].id, request, version)) |
|||
.returns(() => Observable.of(new Versioned<any>(newVersion, {}))); |
|||
|
|||
clientsState.update(oldClients[0], request).subscribe(); |
|||
|
|||
const client_1 = clientsState.snapshot.clients.at(0); |
|||
|
|||
expect(client_1.name).toBe('NewName'); |
|||
expect(client_1.permission).toBe('NewPermission'); |
|||
expect(clientsState.snapshot.version).toEqual(newVersion); |
|||
}); |
|||
|
|||
it('should remove client from snapshot', () => { |
|||
clientsService.setup(x => x.deleteClient(app, oldClients[0].id, version)) |
|||
.returns(() => Observable.of(new Versioned<any>(newVersion, {}))); |
|||
|
|||
clientsState.revoke(oldClients[0]).subscribe(); |
|||
|
|||
expect(clientsState.snapshot.clients.values).toEqual([oldClients[1]]); |
|||
expect(clientsState.snapshot.version).toEqual(newVersion); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,124 @@ |
|||
/* |
|||
* 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, |
|||
ValidatorsEx, |
|||
Version |
|||
} from '@app/framework'; |
|||
|
|||
import { AppsState } from './apps.state'; |
|||
|
|||
import { |
|||
AppClientDto, |
|||
AppClientsService, |
|||
CreateAppClientDto, |
|||
UpdateAppClientDto |
|||
} from './../services/app-clients.service'; |
|||
|
|||
export class AttachClientForm extends Form<FormGroup> { |
|||
public hasNoName = |
|||
this.form.controls['name'].valueChanges.startWith('').map(x => !x || x.length === 0); |
|||
|
|||
constructor(formBuilder: FormBuilder) { |
|||
super(formBuilder.group({ |
|||
name: ['', |
|||
[ |
|||
Validators.maxLength(40), |
|||
ValidatorsEx.pattern('[a-z0-9]+(\-[a-z0-9]+)*', 'Name can contain lower case letters (a-z), numbers and dashes (not at the end).') |
|||
] |
|||
] |
|||
})); |
|||
} |
|||
} |
|||
|
|||
interface Snapshot { |
|||
clients: ImmutableArray<AppClientDto>; |
|||
|
|||
isLoaded: boolean; |
|||
|
|||
version: Version; |
|||
} |
|||
|
|||
@Injectable() |
|||
export class ClientsState extends State<Snapshot> { |
|||
public clients = |
|||
this.changes.map(x => x.clients); |
|||
|
|||
public isLoaded = |
|||
this.changes.map(x => x.isLoaded); |
|||
|
|||
constructor( |
|||
private readonly appClientsService: AppClientsService, |
|||
private readonly appsState: AppsState, |
|||
private readonly dialogs: DialogService |
|||
) { |
|||
super({ clients: ImmutableArray.empty(), version: new Version(''), isLoaded: false }); |
|||
} |
|||
|
|||
public load(): Observable<any> { |
|||
return this.appClientsService.getClients(this.appName) |
|||
.do(dtos => { |
|||
this.next(s => { |
|||
return { clients: ImmutableArray.of(dtos.clients), isLoaded: true, version: dtos.version }; |
|||
}); |
|||
}) |
|||
.notify(this.dialogs); |
|||
} |
|||
|
|||
public attach(request: CreateAppClientDto): Observable<any> { |
|||
return this.appClientsService.postClient(this.appName, request, this.snapshot.version) |
|||
.do(dto => { |
|||
this.next(s => { |
|||
const clients = s.clients.push(dto.payload); |
|||
|
|||
return { ...s, clients, version: dto.version }; |
|||
}); |
|||
}) |
|||
.notify(this.dialogs); |
|||
} |
|||
|
|||
public revoke(client: AppClientDto): Observable<any> { |
|||
return this.appClientsService.deleteClient(this.appName, client.id, this.snapshot.version) |
|||
.do(dto => { |
|||
this.next(s => { |
|||
const clients = s.clients.filter(c => c.id !== client.id); |
|||
|
|||
return { ...s, clients, version: dto.version }; |
|||
}); |
|||
}) |
|||
.notify(this.dialogs); |
|||
} |
|||
|
|||
public update(client: AppClientDto, request: UpdateAppClientDto): Observable<any> { |
|||
return this.appClientsService.putClient(this.appName, client.id, request, this.snapshot.version) |
|||
.do(dto => { |
|||
this.next(s => { |
|||
const clients = s.clients.replaceBy('id', update(client, request)); |
|||
|
|||
return { ...s, clients, version: dto.version }; |
|||
}); |
|||
}) |
|||
.notify(this.dialogs); |
|||
} |
|||
|
|||
private get appName() { |
|||
return this.appsState.appName; |
|||
} |
|||
} |
|||
|
|||
const update = (client: AppClientDto, request: UpdateAppClientDto) => |
|||
new AppClientDto(client.id, request.name || client.name, client.secret, request.permission || client.permission); |
|||
Loading…
Reference in new issue