mirror of https://github.com/Squidex/squidex.git
20 changed files with 13 additions and 1268 deletions
@ -1,344 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Threading.Tasks; |
|
||||
using GraphQL; |
|
||||
using GraphQL.Resolvers; |
|
||||
using GraphQL.Types; |
|
||||
using Newtonsoft.Json.Linq; |
|
||||
using Squidex.Domain.Apps.Core.Contents; |
|
||||
using Squidex.Domain.Apps.Entities.Contents.Commands; |
|
||||
using Squidex.Domain.Apps.Entities.Schemas; |
|
||||
using Squidex.Infrastructure; |
|
||||
using Squidex.Infrastructure.Commands; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|
||||
{ |
|
||||
public sealed class AppMutationsGraphType : ObjectGraphType |
|
||||
{ |
|
||||
public AppMutationsGraphType(IGraphModel model, IEnumerable<ISchemaEntity> schemas) |
|
||||
{ |
|
||||
foreach (var schema in schemas) |
|
||||
{ |
|
||||
var schemaId = schema.NamedId(); |
|
||||
var schemaType = schema.TypeName(); |
|
||||
var schemaName = schema.DisplayName(); |
|
||||
|
|
||||
var contentType = model.GetContentType(schema.Id); |
|
||||
var contentDataType = model.GetContentDataType(schema.Id); |
|
||||
|
|
||||
var resultType = new ContentDataChangedResultGraphType(schemaType, schemaName, contentDataType); |
|
||||
|
|
||||
var inputType = new ContentDataGraphInputType(model, schema); |
|
||||
|
|
||||
AddContentCreate(schemaId, schemaType, schemaName, inputType, contentDataType, contentType); |
|
||||
AddContentUpdate(schemaType, schemaName, inputType, resultType); |
|
||||
AddContentPatch(schemaType, schemaName, inputType, resultType); |
|
||||
AddContentPublish(schemaType, schemaName); |
|
||||
AddContentUnpublish(schemaType, schemaName); |
|
||||
AddContentArchive(schemaType, schemaName); |
|
||||
AddContentRestore(schemaType, schemaName); |
|
||||
AddContentDelete(schemaType, schemaName); |
|
||||
} |
|
||||
|
|
||||
Description = "The app mutations."; |
|
||||
} |
|
||||
|
|
||||
private void AddContentCreate(NamedId<Guid> schemaId, string schemaType, string schemaName, ContentDataGraphInputType inputType, IGraphType contentDataType, IGraphType contentType) |
|
||||
{ |
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = $"create{schemaType}Content", |
|
||||
Arguments = new QueryArguments |
|
||||
{ |
|
||||
new QueryArgument(AllTypes.None) |
|
||||
{ |
|
||||
Name = "data", |
|
||||
Description = $"The data for the {schemaName} content.", |
|
||||
DefaultValue = null, |
|
||||
ResolvedType = new NonNullGraphType(inputType), |
|
||||
}, |
|
||||
new QueryArgument(AllTypes.None) |
|
||||
{ |
|
||||
Name = "publish", |
|
||||
Description = "Set to true to autopublish content.", |
|
||||
DefaultValue = false, |
|
||||
ResolvedType = AllTypes.Boolean |
|
||||
}, |
|
||||
new QueryArgument(AllTypes.None) |
|
||||
{ |
|
||||
Name = "expectedVersion", |
|
||||
Description = "The expected version", |
|
||||
DefaultValue = EtagVersion.Any, |
|
||||
ResolvedType = AllTypes.Int |
|
||||
} |
|
||||
}, |
|
||||
ResolvedType = new NonNullGraphType(contentType), |
|
||||
Resolver = ResolveAsync(async (c, publish) => |
|
||||
{ |
|
||||
var argPublish = c.GetArgument<bool>("publish"); |
|
||||
|
|
||||
var contentData = GetContentData(c); |
|
||||
|
|
||||
var command = new CreateContent { SchemaId = schemaId, Data = contentData, Publish = argPublish }; |
|
||||
var commandContext = await publish(command); |
|
||||
|
|
||||
var result = commandContext.Result<EntityCreatedResult<NamedContentData>>(); |
|
||||
var response = ContentEntity.Create(command, result); |
|
||||
|
|
||||
return (IContentEntity)ContentEntity.Create(command, result); |
|
||||
}), |
|
||||
Description = $"Creates an {schemaName} content." |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
private void AddContentUpdate(string schemaType, string schemaName, ContentDataGraphInputType inputType, IGraphType resultType) |
|
||||
{ |
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = $"update{schemaType}Content", |
|
||||
Arguments = new QueryArguments |
|
||||
{ |
|
||||
new QueryArgument(AllTypes.None) |
|
||||
{ |
|
||||
Name = "id", |
|
||||
Description = $"The id of the {schemaName} content (GUID)", |
|
||||
DefaultValue = string.Empty, |
|
||||
ResolvedType = AllTypes.NonNullGuid |
|
||||
}, |
|
||||
new QueryArgument(AllTypes.None) |
|
||||
{ |
|
||||
Name = "data", |
|
||||
Description = $"The data for the {schemaName} content.", |
|
||||
DefaultValue = null, |
|
||||
ResolvedType = new NonNullGraphType(inputType), |
|
||||
}, |
|
||||
new QueryArgument(AllTypes.None) |
|
||||
{ |
|
||||
Name = "expectedVersion", |
|
||||
Description = "The expected version", |
|
||||
DefaultValue = EtagVersion.Any, |
|
||||
ResolvedType = AllTypes.Int |
|
||||
} |
|
||||
}, |
|
||||
ResolvedType = new NonNullGraphType(resultType), |
|
||||
Resolver = ResolveAsync(async (c, publish) => |
|
||||
{ |
|
||||
var contentId = c.GetArgument<Guid>("id"); |
|
||||
var contentData = GetContentData(c); |
|
||||
|
|
||||
var command = new UpdateContent { ContentId = contentId, Data = contentData }; |
|
||||
var commandContext = await publish(command); |
|
||||
|
|
||||
var result = commandContext.Result<ContentDataChangedResult>(); |
|
||||
|
|
||||
return result; |
|
||||
}), |
|
||||
Description = $"Update an {schemaName} content by id." |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
private void AddContentPatch(string schemaType, string schemaName, ContentDataGraphInputType inputType, IGraphType resultType) |
|
||||
{ |
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = $"patch{schemaType}Content", |
|
||||
Arguments = new QueryArguments |
|
||||
{ |
|
||||
new QueryArgument(AllTypes.None) |
|
||||
{ |
|
||||
Name = "id", |
|
||||
Description = $"The id of the {schemaName} content (GUID)", |
|
||||
DefaultValue = string.Empty, |
|
||||
ResolvedType = AllTypes.NonNullGuid |
|
||||
}, |
|
||||
new QueryArgument(AllTypes.None) |
|
||||
{ |
|
||||
Name = "data", |
|
||||
Description = $"The data for the {schemaName} content.", |
|
||||
DefaultValue = null, |
|
||||
ResolvedType = new NonNullGraphType(inputType), |
|
||||
}, |
|
||||
new QueryArgument(AllTypes.None) |
|
||||
{ |
|
||||
Name = "expectedVersion", |
|
||||
Description = "The expected version", |
|
||||
DefaultValue = EtagVersion.Any, |
|
||||
ResolvedType = AllTypes.Int |
|
||||
} |
|
||||
}, |
|
||||
ResolvedType = new NonNullGraphType(resultType), |
|
||||
Resolver = ResolveAsync(async (c, publish) => |
|
||||
{ |
|
||||
var contentId = c.GetArgument<Guid>("id"); |
|
||||
var contentData = GetContentData(c); |
|
||||
|
|
||||
var command = new PatchContent { ContentId = contentId, Data = contentData }; |
|
||||
var commandContext = await publish(command); |
|
||||
|
|
||||
var result = commandContext.Result<ContentDataChangedResult>(); |
|
||||
|
|
||||
return result; |
|
||||
}), |
|
||||
Description = $"Patch a {schemaName} content." |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
private void AddContentPublish(string schemaType, string schemaName) |
|
||||
{ |
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = $"publish{schemaType}Content", |
|
||||
Arguments = CreateIdArguments(schemaName), |
|
||||
ResolvedType = AllTypes.CommandVersion, |
|
||||
Resolver = ResolveAsync((c, publish) => |
|
||||
{ |
|
||||
var contentId = c.GetArgument<Guid>("id"); |
|
||||
|
|
||||
var command = new ChangeContentStatus { ContentId = contentId, Status = Status.Published }; |
|
||||
|
|
||||
return publish(command); |
|
||||
}), |
|
||||
Description = $"Publish a {schemaName} content." |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
private void AddContentUnpublish(string schemaType, string schemaName) |
|
||||
{ |
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = $"unpublish{schemaType}Content", |
|
||||
Arguments = CreateIdArguments(schemaName), |
|
||||
ResolvedType = AllTypes.CommandVersion, |
|
||||
Resolver = ResolveAsync((c, publish) => |
|
||||
{ |
|
||||
var contentId = c.GetArgument<Guid>("id"); |
|
||||
|
|
||||
var command = new ChangeContentStatus { ContentId = contentId, Status = Status.Draft }; |
|
||||
|
|
||||
return publish(command); |
|
||||
}), |
|
||||
Description = $"Unpublish a {schemaName} content." |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
private void AddContentArchive(string schemaType, string schemaName) |
|
||||
{ |
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = $"archive{schemaType}Content", |
|
||||
Arguments = CreateIdArguments(schemaName), |
|
||||
ResolvedType = AllTypes.CommandVersion, |
|
||||
Resolver = ResolveAsync((c, publish) => |
|
||||
{ |
|
||||
var contentId = c.GetArgument<Guid>("id"); |
|
||||
|
|
||||
var command = new ChangeContentStatus { ContentId = contentId, Status = Status.Archived }; |
|
||||
|
|
||||
return publish(command); |
|
||||
}), |
|
||||
Description = $"Archive a {schemaName} content." |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
private void AddContentRestore(string schemaType, string schemaName) |
|
||||
{ |
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = $"restore{schemaType}Content", |
|
||||
Arguments = CreateIdArguments(schemaName), |
|
||||
ResolvedType = AllTypes.CommandVersion, |
|
||||
Resolver = ResolveAsync((c, publish) => |
|
||||
{ |
|
||||
var contentId = c.GetArgument<Guid>("id"); |
|
||||
|
|
||||
var command = new ChangeContentStatus { ContentId = contentId, Status = Status.Draft }; |
|
||||
|
|
||||
return publish(command); |
|
||||
}), |
|
||||
Description = $"Restore a {schemaName} content." |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
private void AddContentDelete(string schemaType, string schemaName) |
|
||||
{ |
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = $"delete{schemaType}Content", |
|
||||
Arguments = CreateIdArguments(schemaName), |
|
||||
ResolvedType = AllTypes.CommandVersion, |
|
||||
Resolver = ResolveAsync((c, publish) => |
|
||||
{ |
|
||||
var contentId = c.GetArgument<Guid>("id"); |
|
||||
|
|
||||
var command = new DeleteContent { ContentId = contentId }; |
|
||||
|
|
||||
return publish(command); |
|
||||
}), |
|
||||
Description = $"Delete an {schemaName} content." |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
private static QueryArguments CreateIdArguments(string schemaName) |
|
||||
{ |
|
||||
return new QueryArguments |
|
||||
{ |
|
||||
new QueryArgument(AllTypes.None) |
|
||||
{ |
|
||||
Name = "id", |
|
||||
Description = $"The id of the {schemaName} content (GUID)", |
|
||||
DefaultValue = string.Empty, |
|
||||
ResolvedType = AllTypes.NonNullGuid |
|
||||
}, |
|
||||
new QueryArgument(AllTypes.None) |
|
||||
{ |
|
||||
Name = "expectedVersion", |
|
||||
Description = "The expected version", |
|
||||
DefaultValue = EtagVersion.Any, |
|
||||
ResolvedType = AllTypes.Int |
|
||||
} |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
private static IFieldResolver ResolveAsync<T>(Func<ResolveFieldContext, Func<SquidexCommand, Task<CommandContext>>, Task<T>> action) |
|
||||
{ |
|
||||
return new FuncFieldResolver<Task<T>>(async c => |
|
||||
{ |
|
||||
var e = (GraphQLExecutionContext)c.UserContext; |
|
||||
|
|
||||
try |
|
||||
{ |
|
||||
return await action(c, command => |
|
||||
{ |
|
||||
command.ExpectedVersion = c.GetArgument("expectedVersion", EtagVersion.Any); |
|
||||
|
|
||||
return e.CommandBus.PublishAsync(command); |
|
||||
}); |
|
||||
} |
|
||||
catch (ValidationException ex) |
|
||||
{ |
|
||||
c.Errors.Add(new ExecutionError(ex.Message)); |
|
||||
|
|
||||
throw; |
|
||||
} |
|
||||
catch (DomainException ex) |
|
||||
{ |
|
||||
c.Errors.Add(new ExecutionError(ex.Message)); |
|
||||
|
|
||||
throw; |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
private static NamedContentData GetContentData(ResolveFieldContext c) |
|
||||
{ |
|
||||
return JObject.FromObject(c.GetArgument<object>("data")).ToObject<NamedContentData>(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,44 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using GraphQL.Resolvers; |
|
||||
using GraphQL.Types; |
|
||||
using Squidex.Infrastructure.Commands; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|
||||
{ |
|
||||
public sealed class CommandVersionGraphType : ObjectGraphType<CommandContext> |
|
||||
{ |
|
||||
public CommandVersionGraphType() |
|
||||
{ |
|
||||
Name = "CommandVersionDto"; |
|
||||
|
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = "version", |
|
||||
ResolvedType = AllTypes.Int, |
|
||||
Resolver = ResolveVersion(), |
|
||||
Description = "The new version of the item." |
|
||||
}); |
|
||||
|
|
||||
Description = "The result of a mutation"; |
|
||||
} |
|
||||
|
|
||||
private static IFieldResolver ResolveVersion() |
|
||||
{ |
|
||||
return new FuncFieldResolver<CommandContext, int?>(x => |
|
||||
{ |
|
||||
if (x.Source.Result<object>() is EntitySavedResult result) |
|
||||
{ |
|
||||
return (int)result.Version; |
|
||||
} |
|
||||
|
|
||||
return null; |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,44 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using GraphQL.Resolvers; |
|
||||
using GraphQL.Types; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|
||||
{ |
|
||||
public sealed class ContentDataChangedResultGraphType : ObjectGraphType<ContentDataChangedResult> |
|
||||
{ |
|
||||
public ContentDataChangedResultGraphType(string schemaType, string schemaName, IGraphType contentDataType) |
|
||||
{ |
|
||||
Name = $"{schemaName}DataChangedResultDto"; |
|
||||
|
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = "version", |
|
||||
ResolvedType = AllTypes.Int, |
|
||||
Resolver = Resolve(x => x.Version), |
|
||||
Description = $"The new version of the {schemaName} content." |
|
||||
}); |
|
||||
|
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = "data", |
|
||||
ResolvedType = new NonNullGraphType(contentDataType), |
|
||||
Resolver = Resolve(x => x.Data), |
|
||||
Description = $"The new data of the {schemaName} content." |
|
||||
}); |
|
||||
|
|
||||
Description = $"The result of the {schemaName} mutation"; |
|
||||
} |
|
||||
|
|
||||
private static IFieldResolver Resolve(Func<ContentDataChangedResult, object> action) |
|
||||
{ |
|
||||
return new FuncFieldResolver<ContentDataChangedResult, object>(c => action(c.Source)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,70 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System.Linq; |
|
||||
using GraphQL.Types; |
|
||||
using Squidex.Domain.Apps.Entities.Schemas; |
|
||||
using Squidex.Infrastructure; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|
||||
{ |
|
||||
public sealed class ContentDataGraphInputType : InputObjectGraphType |
|
||||
{ |
|
||||
public ContentDataGraphInputType(IGraphModel model, ISchemaEntity schema) |
|
||||
{ |
|
||||
var schemaType = schema.TypeName(); |
|
||||
var schemaName = schema.DisplayName(); |
|
||||
|
|
||||
Name = $"{schemaType}InputDto"; |
|
||||
|
|
||||
foreach (var field in schema.SchemaDef.Fields.Where(x => !x.IsHidden)) |
|
||||
{ |
|
||||
var inputType = model.GetInputGraphType(field); |
|
||||
|
|
||||
if (inputType != null) |
|
||||
{ |
|
||||
if (field.RawProperties.IsRequired) |
|
||||
{ |
|
||||
inputType = new NonNullGraphType(inputType); |
|
||||
} |
|
||||
|
|
||||
var fieldName = field.RawProperties.Label.WithFallback(field.Name); |
|
||||
|
|
||||
var fieldGraphType = new InputObjectGraphType |
|
||||
{ |
|
||||
Name = $"{schemaType}Data{field.Name.ToPascalCase()}InputDto" |
|
||||
}; |
|
||||
|
|
||||
var partition = model.ResolvePartition(field.Partitioning); |
|
||||
|
|
||||
foreach (var partitionItem in partition) |
|
||||
{ |
|
||||
fieldGraphType.AddField(new FieldType |
|
||||
{ |
|
||||
Name = partitionItem.Key, |
|
||||
Resolver = null, |
|
||||
ResolvedType = inputType, |
|
||||
Description = field.RawProperties.Hints |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
fieldGraphType.Description = $"The input structure of the {fieldName} of a {schemaName} content type."; |
|
||||
|
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = field.Name.ToCamelCase(), |
|
||||
Resolver = null, |
|
||||
ResolvedType = fieldGraphType, |
|
||||
Description = $"The {fieldName} field." |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Description = $"The structure of a {schemaName} content type."; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,31 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using GraphQL.Types; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|
||||
{ |
|
||||
public sealed class GeolocationInputGraphType : InputObjectGraphType |
|
||||
{ |
|
||||
public GeolocationInputGraphType() |
|
||||
{ |
|
||||
Name = "GeolocationInputDto"; |
|
||||
|
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = "latitude", |
|
||||
ResolvedType = AllTypes.NonNullFloat |
|
||||
}); |
|
||||
|
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = "longitude", |
|
||||
ResolvedType = AllTypes.NonNullFloat |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,20 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using GraphQL.Types; |
|
||||
using Squidex.Domain.Apps.Core.Schemas; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|
||||
{ |
|
||||
public static class InputFieldExtensions |
|
||||
{ |
|
||||
public static IGraphType GetInputGraphType(this IField field) |
|
||||
{ |
|
||||
return field.Accept(InputFieldVisitor.Default); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,71 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using GraphQL.Types; |
|
||||
using Squidex.Domain.Apps.Core.Schemas; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|
||||
{ |
|
||||
public sealed class InputFieldVisitor : IFieldVisitor<IGraphType> |
|
||||
{ |
|
||||
public static readonly InputFieldVisitor Default = new InputFieldVisitor(); |
|
||||
|
|
||||
private InputFieldVisitor() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public IGraphType Visit(IArrayField field) |
|
||||
{ |
|
||||
return AllTypes.NoopArray; |
|
||||
} |
|
||||
|
|
||||
public IGraphType Visit(IField<AssetsFieldProperties> field) |
|
||||
{ |
|
||||
return AllTypes.References; |
|
||||
} |
|
||||
|
|
||||
public IGraphType Visit(IField<BooleanFieldProperties> field) |
|
||||
{ |
|
||||
return AllTypes.Boolean; |
|
||||
} |
|
||||
|
|
||||
public IGraphType Visit(IField<DateTimeFieldProperties> field) |
|
||||
{ |
|
||||
return AllTypes.Date; |
|
||||
} |
|
||||
|
|
||||
public IGraphType Visit(IField<GeolocationFieldProperties> field) |
|
||||
{ |
|
||||
return AllTypes.GeolocationInput; |
|
||||
} |
|
||||
|
|
||||
public IGraphType Visit(IField<JsonFieldProperties> field) |
|
||||
{ |
|
||||
return AllTypes.Json; |
|
||||
} |
|
||||
|
|
||||
public IGraphType Visit(IField<NumberFieldProperties> field) |
|
||||
{ |
|
||||
return AllTypes.Float; |
|
||||
} |
|
||||
|
|
||||
public IGraphType Visit(IField<ReferencesFieldProperties> field) |
|
||||
{ |
|
||||
return AllTypes.References; |
|
||||
} |
|
||||
|
|
||||
public IGraphType Visit(IField<StringFieldProperties> field) |
|
||||
{ |
|
||||
return AllTypes.String; |
|
||||
} |
|
||||
|
|
||||
public IGraphType Visit(IField<TagsFieldProperties> field) |
|
||||
{ |
|
||||
return AllTypes.Tags; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,47 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System.Linq; |
|
||||
using GraphQL.Types; |
|
||||
using Squidex.Domain.Apps.Core.Schemas; |
|
||||
using Squidex.Domain.Apps.Entities.Schemas; |
|
||||
using Squidex.Infrastructure; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
|
||||
{ |
|
||||
public sealed class NestedInputGraphType : InputObjectGraphType |
|
||||
{ |
|
||||
public NestedInputGraphType(IGraphModel model, ISchemaEntity schema, IArrayField field) |
|
||||
{ |
|
||||
var schemaType = schema.TypeName(); |
|
||||
var schemaName = schema.DisplayName(); |
|
||||
|
|
||||
var fieldType = field.TypeName(); |
|
||||
var fieldName = field.DisplayName(); |
|
||||
|
|
||||
Name = $"{schemaType}{fieldName}ChildDto"; |
|
||||
|
|
||||
foreach (var nestedField in field.Fields.Where(x => !x.IsHidden)) |
|
||||
{ |
|
||||
var fieldInfo = model.GetGraphType(schema, nestedField); |
|
||||
|
|
||||
if (fieldInfo.ResolveType != null) |
|
||||
{ |
|
||||
AddField(new FieldType |
|
||||
{ |
|
||||
Name = nestedField.Name.ToCamelCase(), |
|
||||
Resolver = null, |
|
||||
ResolvedType = fieldInfo.ResolveType, |
|
||||
Description = $"The {fieldName}/{nestedField.DisplayName()} nested field." |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Description = $"The structure of a {schemaName}.{fieldName} nested schema."; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,73 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using GraphQL; |
|
||||
using Newtonsoft.Json.Linq; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils |
|
||||
{ |
|
||||
public static class InputConverter |
|
||||
{ |
|
||||
public static Inputs ToInputs(JObject input) |
|
||||
{ |
|
||||
var result = new Inputs(); |
|
||||
|
|
||||
if (input != null) |
|
||||
{ |
|
||||
foreach (var kvp in input) |
|
||||
{ |
|
||||
result.Add(kvp.Key, GetValue(kvp.Value, 1)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
private static object GetValue(object value, int level) |
|
||||
{ |
|
||||
if (level == 3) |
|
||||
{ |
|
||||
return value; |
|
||||
} |
|
||||
|
|
||||
switch (value) |
|
||||
{ |
|
||||
case JObject jObject: |
|
||||
{ |
|
||||
var result = new Dictionary<string, object>(); |
|
||||
|
|
||||
foreach (var kvp in jObject) |
|
||||
{ |
|
||||
result.Add(kvp.Key, GetValue(kvp.Value, level + 1)); |
|
||||
} |
|
||||
|
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
case JArray jArray: |
|
||||
{ |
|
||||
var result = new List<object>(); |
|
||||
|
|
||||
foreach (var item in jArray) |
|
||||
{ |
|
||||
result.Add(GetValue(item, level + 1)); |
|
||||
} |
|
||||
|
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
case JValue jValue: |
|
||||
{ |
|
||||
return jValue.Value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return value; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,487 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// 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 Newtonsoft.Json.Linq; |
|
||||
using Squidex.Domain.Apps.Core.Contents; |
|
||||
using Squidex.Domain.Apps.Entities.Contents.Commands; |
|
||||
using Squidex.Infrastructure; |
|
||||
using Squidex.Infrastructure.Commands; |
|
||||
using Xunit; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL |
|
||||
{ |
|
||||
public class GraphQLMutationTests : GraphQLTestBase |
|
||||
{ |
|
||||
private readonly Guid contentId = Guid.NewGuid(); |
|
||||
private readonly IContentEntity content; |
|
||||
private readonly CommandContext commandContext = new CommandContext(new PatchContent(), A.Dummy<ICommandBus>()); |
|
||||
|
|
||||
public GraphQLMutationTests() |
|
||||
{ |
|
||||
content = CreateContent(contentId, Guid.NewGuid(), Guid.NewGuid(), null); |
|
||||
|
|
||||
A.CallTo(() => commandBus.PublishAsync(A<ICommand>.Ignored)) |
|
||||
.Returns(commandContext); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task Should_return_single_content_when_creating_content() |
|
||||
{ |
|
||||
var query = $@"
|
|
||||
mutation OP($data: MySchemaInputDto!) {{ |
|
||||
createMySchemaContent(data: $data, expectedVersion: 10) {{ |
|
||||
version |
|
||||
data {{ |
|
||||
myString {{ |
|
||||
de |
|
||||
}} |
|
||||
myNumber {{ |
|
||||
iv |
|
||||
}} |
|
||||
myBoolean {{ |
|
||||
iv |
|
||||
}} |
|
||||
myDatetime {{ |
|
||||
iv |
|
||||
}} |
|
||||
myGeolocation {{ |
|
||||
iv |
|
||||
}} |
|
||||
myTags {{ |
|
||||
iv |
|
||||
}} |
|
||||
}} |
|
||||
}} |
|
||||
}}";
|
|
||||
|
|
||||
commandContext.Complete(new EntityCreatedResult<NamedContentData>(content.Data, 13)); |
|
||||
|
|
||||
var inputContent = GetInputContent(content); |
|
||||
|
|
||||
var variables = |
|
||||
new JObject( |
|
||||
new JProperty("data", inputContent)); |
|
||||
|
|
||||
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query, Variables = variables }); |
|
||||
|
|
||||
var expected = new |
|
||||
{ |
|
||||
data = new |
|
||||
{ |
|
||||
createMySchemaContent = new |
|
||||
{ |
|
||||
version = 13, |
|
||||
data = new |
|
||||
{ |
|
||||
myString = new |
|
||||
{ |
|
||||
de = "value" |
|
||||
}, |
|
||||
myNumber = new |
|
||||
{ |
|
||||
iv = 1 |
|
||||
}, |
|
||||
myBoolean = new |
|
||||
{ |
|
||||
iv = true |
|
||||
}, |
|
||||
myDatetime = new |
|
||||
{ |
|
||||
iv = content.LastModified.ToDateTimeUtc() |
|
||||
}, |
|
||||
myGeolocation = new |
|
||||
{ |
|
||||
iv = new |
|
||||
{ |
|
||||
latitude = 10, |
|
||||
longitude = 20 |
|
||||
} |
|
||||
}, |
|
||||
myTags = new |
|
||||
{ |
|
||||
iv = new[] |
|
||||
{ |
|
||||
"tag1", |
|
||||
"tag2" |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
AssertResult(expected, result); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task Should_return_single_content_when_updating_content() |
|
||||
{ |
|
||||
var query = $@"
|
|
||||
mutation OP($data: MySchemaInputDto!) {{ |
|
||||
updateMySchemaContent(id: ""{contentId}"", data: $data, expectedVersion: 10) {{ |
|
||||
version |
|
||||
data {{ |
|
||||
myString {{ |
|
||||
de |
|
||||
}} |
|
||||
myNumber {{ |
|
||||
iv |
|
||||
}} |
|
||||
myBoolean {{ |
|
||||
iv |
|
||||
}} |
|
||||
myDatetime {{ |
|
||||
iv |
|
||||
}} |
|
||||
myGeolocation {{ |
|
||||
iv |
|
||||
}} |
|
||||
myTags {{ |
|
||||
iv |
|
||||
}} |
|
||||
}} |
|
||||
}} |
|
||||
}}";
|
|
||||
|
|
||||
commandContext.Complete(new ContentDataChangedResult(content.Data, 13)); |
|
||||
|
|
||||
var inputContent = GetInputContent(content); |
|
||||
|
|
||||
var variables = |
|
||||
new JObject( |
|
||||
new JProperty("data", inputContent)); |
|
||||
|
|
||||
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query, Variables = variables }); |
|
||||
|
|
||||
var expected = new |
|
||||
{ |
|
||||
data = new |
|
||||
{ |
|
||||
updateMySchemaContent = new |
|
||||
{ |
|
||||
version = 13, |
|
||||
data = new |
|
||||
{ |
|
||||
myString = new |
|
||||
{ |
|
||||
de = "value" |
|
||||
}, |
|
||||
myNumber = new |
|
||||
{ |
|
||||
iv = 1 |
|
||||
}, |
|
||||
myBoolean = new |
|
||||
{ |
|
||||
iv = true |
|
||||
}, |
|
||||
myDatetime = new |
|
||||
{ |
|
||||
iv = content.LastModified.ToDateTimeUtc() |
|
||||
}, |
|
||||
myGeolocation = new |
|
||||
{ |
|
||||
iv = new |
|
||||
{ |
|
||||
latitude = 10, |
|
||||
longitude = 20 |
|
||||
} |
|
||||
}, |
|
||||
myTags = new |
|
||||
{ |
|
||||
iv = new[] |
|
||||
{ |
|
||||
"tag1", |
|
||||
"tag2" |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
AssertResult(expected, result); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task Should_return_single_content_when_patching_content() |
|
||||
{ |
|
||||
var query = $@"
|
|
||||
mutation OP($data: MySchemaInputDto!) {{ |
|
||||
patchMySchemaContent(id: ""{contentId}"", data: $data, expectedVersion: 10) {{ |
|
||||
version |
|
||||
data {{ |
|
||||
myString {{ |
|
||||
de |
|
||||
}} |
|
||||
myNumber {{ |
|
||||
iv |
|
||||
}} |
|
||||
myBoolean {{ |
|
||||
iv |
|
||||
}} |
|
||||
myDatetime {{ |
|
||||
iv |
|
||||
}} |
|
||||
myGeolocation {{ |
|
||||
iv |
|
||||
}} |
|
||||
myTags {{ |
|
||||
iv |
|
||||
}} |
|
||||
}} |
|
||||
}} |
|
||||
}}";
|
|
||||
|
|
||||
commandContext.Complete(new ContentDataChangedResult(content.Data, 13)); |
|
||||
|
|
||||
var inputContent = GetInputContent(content); |
|
||||
|
|
||||
var variables = |
|
||||
new JObject( |
|
||||
new JProperty("data", inputContent)); |
|
||||
|
|
||||
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query, Variables = variables }); |
|
||||
|
|
||||
var expected = new |
|
||||
{ |
|
||||
data = new |
|
||||
{ |
|
||||
patchMySchemaContent = new |
|
||||
{ |
|
||||
version = 13, |
|
||||
data = new |
|
||||
{ |
|
||||
myString = new |
|
||||
{ |
|
||||
de = "value" |
|
||||
}, |
|
||||
myNumber = new |
|
||||
{ |
|
||||
iv = 1 |
|
||||
}, |
|
||||
myBoolean = new |
|
||||
{ |
|
||||
iv = true |
|
||||
}, |
|
||||
myDatetime = new |
|
||||
{ |
|
||||
iv = content.LastModified.ToDateTimeUtc() |
|
||||
}, |
|
||||
myGeolocation = new |
|
||||
{ |
|
||||
iv = new |
|
||||
{ |
|
||||
latitude = 10, |
|
||||
longitude = 20 |
|
||||
} |
|
||||
}, |
|
||||
myTags = new |
|
||||
{ |
|
||||
iv = new[] |
|
||||
{ |
|
||||
"tag1", |
|
||||
"tag2" |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
AssertResult(expected, result); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task Should_publish_command_for_publish() |
|
||||
{ |
|
||||
var query = $@"
|
|
||||
mutation {{ |
|
||||
publishMySchemaContent(id: ""{contentId}"", expectedVersion: 10) {{ |
|
||||
version |
|
||||
}} |
|
||||
}}";
|
|
||||
|
|
||||
commandContext.Complete(new EntitySavedResult(13)); |
|
||||
|
|
||||
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); |
|
||||
|
|
||||
var expected = new |
|
||||
{ |
|
||||
data = new |
|
||||
{ |
|
||||
publishMySchemaContent = new |
|
||||
{ |
|
||||
version = 13 |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
AssertResult(expected, result); |
|
||||
|
|
||||
A.CallTo(() => commandBus.PublishAsync( |
|
||||
A<ChangeContentStatus>.That.Matches(x => |
|
||||
x.ContentId == contentId && |
|
||||
x.Status == Status.Published && |
|
||||
x.ExpectedVersion == 10))) |
|
||||
.MustHaveHappened(); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task Should_publish_command_for_unpublish() |
|
||||
{ |
|
||||
var query = $@"
|
|
||||
mutation {{ |
|
||||
unpublishMySchemaContent(id: ""{contentId}"", expectedVersion: 10) {{ |
|
||||
version |
|
||||
}} |
|
||||
}}";
|
|
||||
|
|
||||
commandContext.Complete(new EntitySavedResult(13)); |
|
||||
|
|
||||
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); |
|
||||
|
|
||||
var expected = new |
|
||||
{ |
|
||||
data = new |
|
||||
{ |
|
||||
unpublishMySchemaContent = new |
|
||||
{ |
|
||||
version = 13 |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
AssertResult(expected, result); |
|
||||
|
|
||||
A.CallTo(() => commandBus.PublishAsync( |
|
||||
A<ChangeContentStatus>.That.Matches(x => |
|
||||
x.ContentId == contentId && |
|
||||
x.Status == Status.Draft && |
|
||||
x.ExpectedVersion == 10))) |
|
||||
.MustHaveHappened(); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task Should_publish_command_for_archive() |
|
||||
{ |
|
||||
var query = $@"
|
|
||||
mutation {{ |
|
||||
archiveMySchemaContent(id: ""{contentId}"", expectedVersion: 10) {{ |
|
||||
version |
|
||||
}} |
|
||||
}}";
|
|
||||
|
|
||||
commandContext.Complete(new EntitySavedResult(13)); |
|
||||
|
|
||||
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); |
|
||||
|
|
||||
var expected = new |
|
||||
{ |
|
||||
data = new |
|
||||
{ |
|
||||
archiveMySchemaContent = new |
|
||||
{ |
|
||||
version = 13 |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
AssertResult(expected, result); |
|
||||
|
|
||||
A.CallTo(() => commandBus.PublishAsync( |
|
||||
A<ChangeContentStatus>.That.Matches(x => |
|
||||
x.ContentId == contentId && |
|
||||
x.Status == Status.Archived && |
|
||||
x.ExpectedVersion == 10))) |
|
||||
.MustHaveHappened(); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task Should_publish_command_for_restore() |
|
||||
{ |
|
||||
var query = $@"
|
|
||||
mutation {{ |
|
||||
restoreMySchemaContent(id: ""{contentId}"", expectedVersion: 10) {{ |
|
||||
version |
|
||||
}} |
|
||||
}}";
|
|
||||
|
|
||||
commandContext.Complete(new EntitySavedResult(13)); |
|
||||
|
|
||||
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); |
|
||||
|
|
||||
var expected = new |
|
||||
{ |
|
||||
data = new |
|
||||
{ |
|
||||
restoreMySchemaContent = new |
|
||||
{ |
|
||||
version = 13 |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
AssertResult(expected, result); |
|
||||
|
|
||||
A.CallTo(() => commandBus.PublishAsync( |
|
||||
A<ChangeContentStatus>.That.Matches(x => |
|
||||
x.ContentId == contentId && |
|
||||
x.Status == Status.Draft && |
|
||||
x.ExpectedVersion == 10))) |
|
||||
.MustHaveHappened(); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task Should_publish_command_for_delete() |
|
||||
{ |
|
||||
var query = $@"
|
|
||||
mutation {{ |
|
||||
deleteMySchemaContent(id: ""{contentId}"", expectedVersion: 10) {{ |
|
||||
version |
|
||||
}} |
|
||||
}}";
|
|
||||
|
|
||||
commandContext.Complete(new EntitySavedResult(13)); |
|
||||
|
|
||||
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); |
|
||||
|
|
||||
var expected = new |
|
||||
{ |
|
||||
data = new |
|
||||
{ |
|
||||
deleteMySchemaContent = new |
|
||||
{ |
|
||||
version = 13 |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
AssertResult(expected, result); |
|
||||
|
|
||||
A.CallTo(() => commandBus.PublishAsync( |
|
||||
A<DeleteContent>.That.Matches(x => |
|
||||
x.ContentId == contentId && |
|
||||
x.ExpectedVersion == 10))) |
|
||||
.MustHaveHappened(); |
|
||||
} |
|
||||
|
|
||||
private static JObject GetInputContent(IContentEntity content) |
|
||||
{ |
|
||||
var camelContent = new NamedContentData(); |
|
||||
|
|
||||
foreach (var kvp in content.Data) |
|
||||
{ |
|
||||
camelContent[kvp.Key.ToCamelCase()] = kvp.Value; |
|
||||
} |
|
||||
|
|
||||
return JObject.FromObject(camelContent); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue