mirror of https://github.com/Squidex/squidex.git
40 changed files with 965 additions and 259 deletions
@ -0,0 +1,67 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Orleans; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Json.Objects; |
|||
using Squidex.Infrastructure.Orleans; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps |
|||
{ |
|||
public sealed class AppUISettings : IAppUISettings |
|||
{ |
|||
private readonly IGrainFactory grainFactory; |
|||
|
|||
public AppUISettings(IGrainFactory grainFactory) |
|||
{ |
|||
Guard.NotNull(grainFactory, nameof(grainFactory)); |
|||
|
|||
this.grainFactory = grainFactory; |
|||
} |
|||
|
|||
public async Task<JsonObject> GetAsync(Guid appId, string userId) |
|||
{ |
|||
var result = await GetGrain(appId, userId).GetAsync(); |
|||
|
|||
return result.Value; |
|||
} |
|||
|
|||
public Task RemoveAsync(Guid appId, string userId, string path) |
|||
{ |
|||
return GetGrain(appId, userId).RemoveAsync(path); |
|||
} |
|||
|
|||
public Task SetAsync(Guid appId, string userId, string path, IJsonValue value) |
|||
{ |
|||
return GetGrain(appId, userId).SetAsync(path, value.AsJ()); |
|||
} |
|||
|
|||
public Task SetAsync(Guid appId, string userId, JsonObject settings) |
|||
{ |
|||
return GetGrain(appId, userId).SetAsync(settings.AsJ()); |
|||
} |
|||
|
|||
private IAppUISettingsGrain GetGrain(Guid appId, string userId) |
|||
{ |
|||
return grainFactory.GetGrain<IAppUISettingsGrain>(Key(appId, userId)); |
|||
} |
|||
|
|||
private string Key(Guid appId, string userId) |
|||
{ |
|||
if (!string.IsNullOrWhiteSpace(userId)) |
|||
{ |
|||
return $"{appId}_{userId}"; |
|||
} |
|||
else |
|||
{ |
|||
return $"{appId}"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Infrastructure.Json.Objects; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps |
|||
{ |
|||
public interface IAppUISettings |
|||
{ |
|||
Task<JsonObject> GetAsync(Guid appId, string userId); |
|||
|
|||
Task SetAsync(Guid appId, string userId, string path, IJsonValue value); |
|||
|
|||
Task SetAsync(Guid appId, string userId, JsonObject settings); |
|||
|
|||
Task RemoveAsync(Guid appId, string userId, string path); |
|||
} |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Component, EventEmitter, Input, Output } from '@angular/core'; |
|||
|
|||
import { Queries } from '@app/shared/internal'; |
|||
import { SavedQuery } from '../state/queries'; |
|||
|
|||
@Component({ |
|||
selector: 'sqx-shared-queries', |
|||
template: ` |
|||
<div class="sidebar-section"> |
|||
<h3>Shared queries</h3> |
|||
|
|||
<ng-container *ngIf="queries.queriesShared | async; let queryList"> |
|||
<ng-container *ngIf="queryList.length > 0; else noQuery"> |
|||
<a class="sidebar-item" *ngFor="let saved of queryList; trackBy: trackByQuery" (click)="search.emit(saved)" |
|||
[class.active]="isSelectedQuery(saved)"> |
|||
|
|||
{{saved.name}} |
|||
|
|||
<a class="sidebar-item-remove float-right" (click)="queries.removeShared(saved)" sqxStopClick> |
|||
<i class="icon-close"></i> |
|||
</a> |
|||
</a> |
|||
</ng-container> |
|||
|
|||
<ng-template #noQuery> |
|||
<div class="sidebar-item text-muted"> |
|||
Search for {{types}} and use <i class="icon-star-empty"></i> icon in search form to save query for all contributors. |
|||
</div> |
|||
</ng-template> |
|||
</ng-container> |
|||
</div> |
|||
|
|||
<hr /> |
|||
|
|||
<div class="sidebar-section"> |
|||
<h3>My queries</h3> |
|||
|
|||
<ng-container *ngIf="queries.queriesUser | async; let queryList"> |
|||
<ng-container *ngIf="queryList.length > 0; else noQuery"> |
|||
<a class="sidebar-item" *ngFor="let saved of queryList; trackBy: trackByQuery" (click)="search.emit(saved)" |
|||
[class.active]="isSelectedQuery(saved)"> |
|||
|
|||
{{saved.name}} |
|||
|
|||
<a class="sidebar-item-remove float-right" (click)="queries.removeUser(saved)" sqxStopClick> |
|||
<i class="icon-close"></i> |
|||
</a> |
|||
</a> |
|||
</ng-container> |
|||
|
|||
<ng-template #noQuery> |
|||
<div class="sidebar-item text-muted"> |
|||
Search for {{types}} and use <i class="icon-star-empty"></i> icon in search form to save query for yourself. |
|||
</div> |
|||
</ng-template> |
|||
</ng-container> |
|||
</div> |
|||
` |
|||
}) |
|||
export class SavedQueriesComponent { |
|||
@Input() |
|||
public queryUsed: (saved: SavedQuery) => boolean; |
|||
|
|||
@Input() |
|||
public queries: Queries; |
|||
|
|||
@Input() |
|||
public types: string; |
|||
|
|||
@Output() |
|||
public search = new EventEmitter<SavedQuery>(); |
|||
|
|||
public isSelectedQuery(saved: SavedQuery) { |
|||
return this.queryUsed && this.queryUsed(saved); |
|||
} |
|||
|
|||
public trackByQuery(index: number, query: { name: string }) { |
|||
return query.name; |
|||
} |
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Orleans; |
|||
using Squidex.Infrastructure.Json.Objects; |
|||
using Squidex.Infrastructure.Orleans; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps |
|||
{ |
|||
public class AppUISettingsTests |
|||
{ |
|||
private readonly IGrainFactory grainFactory = A.Fake<IGrainFactory>(); |
|||
private readonly IAppUISettingsGrain grain = A.Fake<IAppUISettingsGrain>(); |
|||
private readonly AppUISettings sut; |
|||
|
|||
public AppUISettingsTests() |
|||
{ |
|||
A.CallTo(() => grainFactory.GetGrain<IAppUISettingsGrain>(A<string>.Ignored, null)) |
|||
.Returns(grain); |
|||
|
|||
sut = new AppUISettings(grainFactory); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_call_grain_when_retrieving_settings() |
|||
{ |
|||
var settings = JsonValue.Object(); |
|||
|
|||
A.CallTo(() => grain.GetAsync()) |
|||
.Returns(settings.AsJ()); |
|||
|
|||
var result = await sut.GetAsync(Guid.NewGuid(), "user"); |
|||
|
|||
Assert.Same(settings, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_call_grain_when_setting_value() |
|||
{ |
|||
var value = JsonValue.Object(); |
|||
|
|||
await sut.SetAsync(Guid.NewGuid(), "user", "the.path", value); |
|||
|
|||
A.CallTo(() => grain.SetAsync("the.path", A<J<IJsonValue>>.That.Matches(x => x.Value == value))) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_call_grain_when_replacing_settings() |
|||
{ |
|||
var value = JsonValue.Object(); |
|||
|
|||
await sut.SetAsync(Guid.NewGuid(), "user", value); |
|||
|
|||
A.CallTo(() => grain.SetAsync(A<J<JsonObject>>.That.Matches(x => x.Value == value))) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_call_grain_when_removing_value() |
|||
{ |
|||
await sut.RemoveAsync(Guid.NewGuid(), "user", "the.path"); |
|||
|
|||
A.CallTo(() => grain.RemoveAsync("the.path")) |
|||
.MustHaveHappened(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue