mirror of https://github.com/Squidex/squidex.git
32 changed files with 939 additions and 91 deletions
@ -0,0 +1,99 @@ |
|||||
|
// ==========================================================================
|
||||
|
// MongoContentEntity.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using MongoDB.Bson; |
||||
|
using MongoDB.Bson.Serialization.Attributes; |
||||
|
using Newtonsoft.Json; |
||||
|
using Newtonsoft.Json.Linq; |
||||
|
using Squidex.Core.Contents; |
||||
|
using Squidex.Infrastructure.MongoDb; |
||||
|
using Squidex.Read.Contents; |
||||
|
// ReSharper disable InvertIf
|
||||
|
|
||||
|
namespace Squidex.Store.MongoDb.Contents |
||||
|
{ |
||||
|
public sealed class MongoContentEntity : MongoEntity, IContentEntity |
||||
|
{ |
||||
|
private BsonDocument data; |
||||
|
private ContentData contentData; |
||||
|
|
||||
|
[BsonRequired] |
||||
|
[BsonElement] |
||||
|
public bool IsDeleted { get; set; } |
||||
|
|
||||
|
[BsonRequired] |
||||
|
[BsonElement] |
||||
|
public bool IsPublished { get; set; } |
||||
|
|
||||
|
[BsonRequired] |
||||
|
[BsonElement] |
||||
|
public string Text { get; set; } |
||||
|
|
||||
|
[BsonRequired] |
||||
|
[BsonElement] |
||||
|
public BsonDocument Data |
||||
|
{ |
||||
|
get { return data; } |
||||
|
set |
||||
|
{ |
||||
|
data = value; |
||||
|
|
||||
|
contentData = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ContentData IContentEntity.Data |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (contentData == null) |
||||
|
{ |
||||
|
if (data != null) |
||||
|
{ |
||||
|
contentData = JsonConvert.DeserializeObject<ContentData>(data.ToJson()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return contentData; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void SetData(ContentData newContentData) |
||||
|
{ |
||||
|
data = null; |
||||
|
|
||||
|
if (newContentData != null) |
||||
|
{ |
||||
|
data = BsonDocument.Parse(JsonConvert.SerializeObject(newContentData)); |
||||
|
} |
||||
|
|
||||
|
Text = ExtractText(newContentData); |
||||
|
} |
||||
|
|
||||
|
private static string ExtractText(ContentData data) |
||||
|
{ |
||||
|
if (data == null) |
||||
|
{ |
||||
|
return string.Empty; |
||||
|
} |
||||
|
var stringBuilder = new StringBuilder(); |
||||
|
|
||||
|
foreach (var text in data.Fields.Values.SelectMany(x => x.ValueByLanguage.Values).Where(x => x != null).OfType<JValue>()) |
||||
|
{ |
||||
|
if (text.Type == JTokenType.String) |
||||
|
{ |
||||
|
stringBuilder.Append(text); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return stringBuilder.ToString(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,214 @@ |
|||||
|
// ==========================================================================
|
||||
|
// MongoContentRepository.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using MongoDB.Driver; |
||||
|
using Squidex.Events; |
||||
|
using Squidex.Events.Contents; |
||||
|
using Squidex.Events.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.CQRS; |
||||
|
using Squidex.Infrastructure.CQRS.Events; |
||||
|
using Squidex.Infrastructure.CQRS.Replay; |
||||
|
using Squidex.Infrastructure.Dispatching; |
||||
|
using Squidex.Infrastructure.Reflection; |
||||
|
using Squidex.Read.Contents; |
||||
|
using Squidex.Read.Contents.Repositories; |
||||
|
using Squidex.Store.MongoDb.Utils; |
||||
|
|
||||
|
namespace Squidex.Store.MongoDb.Contents |
||||
|
{ |
||||
|
public class MongoContentRepository : IContentRepository, ICatchEventConsumer, IReplayableStore |
||||
|
{ |
||||
|
private const string Prefix = "Projections_Content_"; |
||||
|
private readonly IMongoDatabase database; |
||||
|
|
||||
|
protected ProjectionDefinitionBuilder<MongoContentEntity> Projection |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return Builders<MongoContentEntity>.Projection; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected SortDefinitionBuilder<MongoContentEntity> Sort |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return Builders<MongoContentEntity>.Sort; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected UpdateDefinitionBuilder<MongoContentEntity> Update |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return Builders<MongoContentEntity>.Update; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected FilterDefinitionBuilder<MongoContentEntity> Filter |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return Builders<MongoContentEntity>.Filter; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected IndexKeysDefinitionBuilder<MongoContentEntity> IndexKeys |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return Builders<MongoContentEntity>.IndexKeys; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public MongoContentRepository(IMongoDatabase database) |
||||
|
{ |
||||
|
Guard.NotNull(database, nameof(database)); |
||||
|
|
||||
|
this.database = database; |
||||
|
} |
||||
|
|
||||
|
public async Task ClearAsync() |
||||
|
{ |
||||
|
using (var collections = await database.ListCollectionsAsync()) |
||||
|
{ |
||||
|
while (await collections.MoveNextAsync()) |
||||
|
{ |
||||
|
foreach (var collection in collections.Current) |
||||
|
{ |
||||
|
var name = collection["name"].ToString(); |
||||
|
|
||||
|
if (name.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
await database.DropCollectionAsync(name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public async Task<List<IContentEntity>> QueryAsync(Guid schemaId, bool nonPublished, int? take, int? skip, string query) |
||||
|
{ |
||||
|
var cursor = BuildQuery(schemaId, nonPublished, query); |
||||
|
|
||||
|
if (take.HasValue) |
||||
|
{ |
||||
|
cursor.Limit(take.Value); |
||||
|
} |
||||
|
|
||||
|
if (skip.HasValue) |
||||
|
{ |
||||
|
cursor.Skip(skip.Value); |
||||
|
} |
||||
|
|
||||
|
cursor.SortByDescending(x => x.LastModified); |
||||
|
|
||||
|
var entities = |
||||
|
await cursor.ToListAsync(); |
||||
|
|
||||
|
return entities.OfType<IContentEntity>().ToList(); |
||||
|
} |
||||
|
|
||||
|
public Task<long> CountAsync(Guid schemaId, bool nonPublished, string query) |
||||
|
{ |
||||
|
var cursor = BuildQuery(schemaId, nonPublished, query); |
||||
|
|
||||
|
return cursor.CountAsync(); |
||||
|
} |
||||
|
|
||||
|
private IFindFluent<MongoContentEntity, MongoContentEntity> BuildQuery(Guid schemaId, bool nonPublished, string query) |
||||
|
{ |
||||
|
var filters = new List<FilterDefinition<MongoContentEntity>> |
||||
|
{ |
||||
|
Filter.Eq(x => x.IsDeleted, false) |
||||
|
}; |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(query)) |
||||
|
{ |
||||
|
filters.Add(Filter.Text(query, "en")); |
||||
|
} |
||||
|
|
||||
|
if (!nonPublished) |
||||
|
{ |
||||
|
filters.Add(Filter.Eq(x => x.IsPublished, false)); |
||||
|
} |
||||
|
|
||||
|
var collection = GetCollection(schemaId); |
||||
|
|
||||
|
var cursor = collection.Find(Filter.And(filters)); |
||||
|
|
||||
|
return cursor; |
||||
|
} |
||||
|
|
||||
|
public async Task<IContentEntity> FindContentAsync(Guid schemaId, Guid id) |
||||
|
{ |
||||
|
var collection = GetCollection(schemaId); |
||||
|
|
||||
|
var entity = |
||||
|
await collection.Find(x => x.Id == id).FirstOrDefaultAsync(); |
||||
|
|
||||
|
return entity; |
||||
|
} |
||||
|
|
||||
|
protected Task On(ContentCreated @event, EnvelopeHeaders headers) |
||||
|
{ |
||||
|
var collection = GetCollection(headers.SchemaId()); |
||||
|
|
||||
|
return collection.CreateAsync(headers, x => |
||||
|
{ |
||||
|
SimpleMapper.Map(@event, x); |
||||
|
|
||||
|
x.SetData(@event.Data); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected Task On(ContentUpdated @event, EnvelopeHeaders headers) |
||||
|
{ |
||||
|
var collection = GetCollection(headers.SchemaId()); |
||||
|
|
||||
|
return collection.UpdateAsync(headers, x => |
||||
|
{ |
||||
|
x.SetData(@event.Data); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected Task On(ContentDeleted @event, EnvelopeHeaders headers) |
||||
|
{ |
||||
|
var collection = GetCollection(headers.SchemaId()); |
||||
|
|
||||
|
return collection.UpdateAsync(headers, x => |
||||
|
{ |
||||
|
x.IsDeleted = true; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected Task On(SchemaCreated @event, EnvelopeHeaders headers) |
||||
|
{ |
||||
|
var collection = GetCollection(headers.AggregateId()); |
||||
|
|
||||
|
return collection.Indexes.CreateOneAsync(IndexKeys.Ascending(x => x.IsPublished).Text(x => x.Text)); |
||||
|
} |
||||
|
|
||||
|
public Task On(Envelope<IEvent> @event) |
||||
|
{ |
||||
|
return this.DispatchActionAsync(@event.Payload, @event.Headers); |
||||
|
} |
||||
|
|
||||
|
private IMongoCollection<MongoContentEntity> GetCollection(Guid schemaId) |
||||
|
{ |
||||
|
var name = $"{Prefix}{schemaId}"; |
||||
|
|
||||
|
return database.GetCollection<MongoContentEntity>(name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
// ==========================================================================
|
||||
|
// EnrichWithSchemaIdProcessor.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Events; |
||||
|
using Squidex.Infrastructure.CQRS; |
||||
|
using Squidex.Infrastructure.CQRS.Commands; |
||||
|
using Squidex.Infrastructure.CQRS.Events; |
||||
|
using Squidex.Infrastructure.Tasks; |
||||
|
using Squidex.Write.Schemas; |
||||
|
|
||||
|
namespace Squidex.Write |
||||
|
{ |
||||
|
public sealed class EnrichWithSchemaIdProcessor : IEventProcessor |
||||
|
{ |
||||
|
public Task ProcessEventAsync(Envelope<IEvent> @event, IAggregate aggregate, ICommand command) |
||||
|
{ |
||||
|
var schemaDomainObject = aggregate as SchemaDomainObject; |
||||
|
|
||||
|
if (schemaDomainObject != null) |
||||
|
{ |
||||
|
@event.SetSchemaId(aggregate.Id); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var schemaCommand = command as ISchemaCommand; |
||||
|
|
||||
|
if (schemaCommand != null) |
||||
|
{ |
||||
|
@event.SetSchemaId(schemaCommand.SchemaId); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return TaskHelper.Done; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,140 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ContentsController.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Authorization; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Newtonsoft.Json.Linq; |
||||
|
using Squidex.Controllers.Api; |
||||
|
using Squidex.Controllers.ContentApi.Models; |
||||
|
using Squidex.Core.Contents; |
||||
|
using Squidex.Infrastructure.CQRS.Commands; |
||||
|
using Squidex.Infrastructure.Reflection; |
||||
|
using Squidex.Pipeline; |
||||
|
using Squidex.Read.Contents.Repositories; |
||||
|
using Squidex.Read.Schemas.Services; |
||||
|
using Squidex.Write.Contents.Commands; |
||||
|
|
||||
|
namespace Squidex.Controllers.ContentApi |
||||
|
{ |
||||
|
[Authorize(Roles = "app-editor,app-owner,app-developer")] |
||||
|
[ApiExceptionFilter] |
||||
|
[ServiceFilter(typeof(AppFilterAttribute))] |
||||
|
public class ContentsController : ControllerBase |
||||
|
{ |
||||
|
private readonly ISchemaProvider schemaProvider; |
||||
|
private readonly IContentRepository contentRepository; |
||||
|
|
||||
|
public ContentsController(ICommandBus commandBus, ISchemaProvider schemaProvider, IContentRepository contentRepository) |
||||
|
: base(commandBus) |
||||
|
{ |
||||
|
this.schemaProvider = schemaProvider; |
||||
|
this.contentRepository = contentRepository; |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("content/{app}/{name}")] |
||||
|
public async Task<IActionResult> GetContents(string name, [FromQuery] string query = null, [FromQuery] int? take = null, [FromQuery] int? skip = null, [FromQuery] bool nonPublished = false) |
||||
|
{ |
||||
|
var schemaEntity = await schemaProvider.FindSchemaByNameAsync(AppId, name); |
||||
|
|
||||
|
if (schemaEntity == null) |
||||
|
{ |
||||
|
return NotFound(); |
||||
|
} |
||||
|
|
||||
|
var taskForContents = contentRepository.QueryAsync(schemaEntity.Id, nonPublished, take, skip, query); |
||||
|
var taskForCount = contentRepository.CountAsync(schemaEntity.Id, nonPublished, query); |
||||
|
|
||||
|
await Task.WhenAll(taskForContents, taskForCount); |
||||
|
|
||||
|
var model = new ContentsDto |
||||
|
{ |
||||
|
Total = taskForCount.Result, |
||||
|
Items = taskForContents.Result.Select(x => |
||||
|
{ |
||||
|
var itemModel = SimpleMapper.Map(x, new ContentDto()); |
||||
|
|
||||
|
if (x.Data != null) |
||||
|
{ |
||||
|
itemModel.Data = x.Data.ToRaw(); |
||||
|
} |
||||
|
|
||||
|
return itemModel; |
||||
|
}).ToArray() |
||||
|
}; |
||||
|
|
||||
|
return Ok(model); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("content/{app}/{name}/{id}")] |
||||
|
public async Task<IActionResult> GetContent(string name, Guid id) |
||||
|
{ |
||||
|
var schemaEntity = await schemaProvider.FindSchemaByNameAsync(AppId, name); |
||||
|
|
||||
|
if (schemaEntity == null) |
||||
|
{ |
||||
|
return NotFound(); |
||||
|
} |
||||
|
|
||||
|
var content = await contentRepository.FindContentAsync(schemaEntity.Id, id); |
||||
|
|
||||
|
if (content == null) |
||||
|
{ |
||||
|
return NotFound(); |
||||
|
} |
||||
|
|
||||
|
var model = SimpleMapper.Map(content, new ContentDto()); |
||||
|
|
||||
|
if (content.Data != null) |
||||
|
{ |
||||
|
model.Data = content.Data.ToRaw(); |
||||
|
} |
||||
|
|
||||
|
return Ok(model); |
||||
|
} |
||||
|
|
||||
|
[HttpPost] |
||||
|
[Route("content/{app}/{name}/")] |
||||
|
public async Task<IActionResult> PostContent([FromBody] Dictionary<string, Dictionary<string, JToken>> request) |
||||
|
{ |
||||
|
var command = new CreateContent { Data = ContentData.Create(request), AggregateId = Guid.NewGuid() }; |
||||
|
|
||||
|
var context = await CommandBus.PublishAsync(command); |
||||
|
var result = context.Result<Guid>(); |
||||
|
|
||||
|
return CreatedAtAction(nameof(GetContent), new { id = result }, new EntityCreatedDto { Id = result.ToString() }); |
||||
|
} |
||||
|
|
||||
|
[HttpPut] |
||||
|
[Route("content/{app}/{name}/{id}")] |
||||
|
public async Task<IActionResult> PutContent(Guid id, [FromBody] Dictionary<string, Dictionary<string, JToken>> request) |
||||
|
{ |
||||
|
var command = new UpdateContent { AggregateId = id, Data = ContentData.Create(request) }; |
||||
|
|
||||
|
await CommandBus.PublishAsync(command); |
||||
|
|
||||
|
return NoContent(); |
||||
|
} |
||||
|
|
||||
|
[HttpDelete] |
||||
|
[Route("content/{app}/{name}/{id}")] |
||||
|
public async Task<IActionResult> PutContent(Guid id) |
||||
|
{ |
||||
|
var command = new DeleteContent { AggregateId = id }; |
||||
|
|
||||
|
await CommandBus.PublishAsync(command); |
||||
|
|
||||
|
return NoContent(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ContentsDto.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
namespace Squidex.Controllers.ContentApi.Models |
||||
|
{ |
||||
|
public class ContentsDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The total number of content items.
|
||||
|
/// </summary>
|
||||
|
public long Total { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The content items.
|
||||
|
/// </summary>
|
||||
|
public ContentDto[] Items { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,106 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Sebastian Stehle. All rights reserved |
||||
|
*/ |
||||
|
|
||||
|
import { Injectable } from '@angular/core'; |
||||
|
import { Observable } from 'rxjs'; |
||||
|
|
||||
|
import 'framework/angular/http-extensions'; |
||||
|
|
||||
|
import { |
||||
|
ApiUrlConfig, |
||||
|
DateTime, |
||||
|
EntityCreatedDto |
||||
|
} from 'framework'; |
||||
|
|
||||
|
import { AuthService } from './auth.service'; |
||||
|
|
||||
|
export class ContentDto { |
||||
|
constructor( |
||||
|
public readonly id: string, |
||||
|
public readonly isPublished: boolean, |
||||
|
public readonly createdBy: string, |
||||
|
public readonly lastModifiedBy: string, |
||||
|
public readonly created: DateTime, |
||||
|
public readonly lastModified: DateTime, |
||||
|
public readonly data: any |
||||
|
) { |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Injectable() |
||||
|
export class ContentsService { |
||||
|
constructor( |
||||
|
private readonly authService: AuthService, |
||||
|
private readonly apiUrl: ApiUrlConfig |
||||
|
) { |
||||
|
} |
||||
|
|
||||
|
public getContents(appName: string, schemaName: string, take: number, skip: number, query: string): Observable<ContentDto[]> { |
||||
|
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/?query=${query}&take=${take}&skip=${skip}&query=${query}&nonPublished=true`); |
||||
|
|
||||
|
return this.authService.authGet(url) |
||||
|
.map(response => response.json()) |
||||
|
.map(response => { |
||||
|
const items: any[] = response; |
||||
|
|
||||
|
return items.map(item => { |
||||
|
return new ContentDto( |
||||
|
item.id, |
||||
|
item.isPublished, |
||||
|
item.createdBy, |
||||
|
item.lastModifiedBy, |
||||
|
DateTime.parseISO(item.created), |
||||
|
DateTime.parseISO(item.lastModified), |
||||
|
item.data); |
||||
|
}); |
||||
|
}) |
||||
|
.catchError('Failed to load contents. Please reload.'); |
||||
|
} |
||||
|
|
||||
|
public getContent(appName: string, schemaName: string, id: string): Observable<ContentDto> { |
||||
|
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}/`); |
||||
|
|
||||
|
return this.authService.authGet(url) |
||||
|
.map(response => response.json()) |
||||
|
.map(response => { |
||||
|
return new ContentDto( |
||||
|
response.id, |
||||
|
response.isPublished, |
||||
|
response.createdBy, |
||||
|
response.lastModifiedBy, |
||||
|
DateTime.parseISO(response.created), |
||||
|
DateTime.parseISO(response.lastModified), |
||||
|
response.data); |
||||
|
}) |
||||
|
.catchError('Failed to load content. Please reload.'); |
||||
|
} |
||||
|
|
||||
|
public postContent(appName: string, schemaName: string, dto: any): Observable<EntityCreatedDto> { |
||||
|
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/`); |
||||
|
|
||||
|
return this.authService.authPost(url, dto) |
||||
|
.map(response => response.json()) |
||||
|
.map(response => { |
||||
|
return new EntityCreatedDto(response.id); |
||||
|
}) |
||||
|
.catchError('Failed to create content. Please reload.'); |
||||
|
} |
||||
|
|
||||
|
public putContent(appName: string, schemaName: string, id: string, dto: any): Observable<any> { |
||||
|
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}/`); |
||||
|
|
||||
|
return this.authService.authPut(url, dto) |
||||
|
.catchError('Failed to update Content. Please reload.'); |
||||
|
} |
||||
|
|
||||
|
public deleteContent(appName: string, schemaName: string, id: string, dto: any): Observable<any> { |
||||
|
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}/`); |
||||
|
|
||||
|
return this.authService.authDelete(url, dto) |
||||
|
.catchError('Failed to delete Content. Please reload.'); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
// ==========================================================================
|
||||
|
// ContentDataTests.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using FluentAssertions; |
||||
|
using Newtonsoft.Json.Linq; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Core.Contents |
||||
|
{ |
||||
|
public class ContentDataTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Should_convert_from_dictionary() |
||||
|
{ |
||||
|
var input = |
||||
|
new Dictionary<string, Dictionary<string, JToken>> |
||||
|
{ |
||||
|
["field1"] = new Dictionary<string, JToken> |
||||
|
{ |
||||
|
["en"] = "en_string", |
||||
|
["de"] = "de_string" |
||||
|
}, |
||||
|
["field2"] = new Dictionary<string, JToken> |
||||
|
{ |
||||
|
["en"] = 1, |
||||
|
["de"] = 2 |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var actual = ContentData.Create(input); |
||||
|
|
||||
|
var expected = |
||||
|
ContentData.Empty |
||||
|
.AddField("field1", |
||||
|
ContentFieldData.Empty |
||||
|
.AddValue("en", "en_string") |
||||
|
.AddValue("de", "de_string")) |
||||
|
.AddField("field2", |
||||
|
ContentFieldData.Empty |
||||
|
.AddValue("en", 1) |
||||
|
.AddValue("de", 2)); |
||||
|
|
||||
|
var output = actual.ToRaw(); |
||||
|
|
||||
|
actual.ShouldBeEquivalentTo(expected); |
||||
|
output.ShouldBeEquivalentTo(input); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
// ==========================================================================
|
||||
|
// EnrichWithSchemaIdProcessorTests.cs
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex Group
|
||||
|
// All rights reserved.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Core.Schemas; |
||||
|
using Squidex.Events; |
||||
|
using Squidex.Infrastructure.CQRS; |
||||
|
using Squidex.Infrastructure.CQRS.Commands; |
||||
|
using Squidex.Infrastructure.CQRS.Events; |
||||
|
using Squidex.Write.Schemas; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Write |
||||
|
{ |
||||
|
public class EnrichWithSchemaIdProcessorTests |
||||
|
{ |
||||
|
public sealed class MySchemaCommand : SchemaAggregateCommand |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public sealed class MyNormalCommand : ICommand |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public sealed class MyEvent : IEvent |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
private readonly EnrichWithSchemaIdProcessor sut = new EnrichWithSchemaIdProcessor(); |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_do_anything_if_not_app_command() |
||||
|
{ |
||||
|
var envelope = new Envelope<IEvent>(new MyEvent()); |
||||
|
|
||||
|
await sut.ProcessEventAsync(envelope, null, new MyNormalCommand()); |
||||
|
|
||||
|
Assert.False(envelope.Headers.Contains("SchemaId")); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_attach_app_id_from_domain_object() |
||||
|
{ |
||||
|
var appId = Guid.NewGuid(); |
||||
|
|
||||
|
var envelope = new Envelope<IEvent>(new MyEvent()); |
||||
|
|
||||
|
await sut.ProcessEventAsync(envelope, new SchemaDomainObject(appId, 1, new FieldRegistry()), new MyNormalCommand()); |
||||
|
|
||||
|
Assert.Equal(appId, envelope.Headers.SchemaId()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_attach_app_id_to_event_envelope() |
||||
|
{ |
||||
|
var appId = Guid.NewGuid(); |
||||
|
var appCommand = new MySchemaCommand { AggregateId = appId }; |
||||
|
|
||||
|
var envelope = new Envelope<IEvent>(new MyEvent()); |
||||
|
|
||||
|
await sut.ProcessEventAsync(envelope, null, appCommand); |
||||
|
|
||||
|
Assert.Equal(appId, envelope.Headers.SchemaId()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue