Browse Source

API for schema categories. (#286)

pull/289/head
Sebastian Stehle 8 years ago
committed by GitHub
parent
commit
7264ea2407
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 50
      Squidex.sln.DotSettings
  2. 14
      src/Squidex.Domain.Apps.Entities/Schemas/Commands/ChangeCategory.cs
  3. 5
      src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs
  4. 2
      src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs
  5. 13
      src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs
  6. 8
      src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs
  7. 17
      src/Squidex.Domain.Apps.Events/Schemas/SchemaCategoryChanged.cs
  8. 25
      src/Squidex/Areas/Api/Controllers/Schemas/Models/ChangeCategoryDto.cs
  9. 5
      src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs
  10. 22
      src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs
  11. 2
      src/Squidex/app/shared/services/schemas.fields.spec.ts
  12. 29
      src/Squidex/app/shared/services/schemas.service.spec.ts
  13. 33
      src/Squidex/app/shared/services/schemas.service.ts
  14. 42
      src/Squidex/app/shared/state/schemas.state.spec.ts
  15. 49
      src/Squidex/app/shared/state/schemas.state.ts
  16. 8
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs
  17. 19
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaGrainTests.cs

50
Squidex.sln.DotSettings

@ -1,50 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Djs/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Djs/@EntryIndexRemoved">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ecshtml/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ecshtml/@EntryIndexRemoved">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ecss/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ecss/@EntryIndexRemoved">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ehtml/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ehtml/@EntryIndexRemoved">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ejs/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ejs/@EntryIndexRemoved">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ejson/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ejson/@EntryIndexRemoved">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Escss/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Escss/@EntryIndexRemoved">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ets/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ets/@EntryIndexRemoved">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002E_002A/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=Header/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;Profile name="Header"&gt;&lt;CSUpdateFileHeader&gt;True&lt;/CSUpdateFileHeader&gt;&lt;/Profile&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=Namespaces/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;Profile name="Namespaces"&gt;&lt;CSOptimizeUsings&gt;&lt;OptimizeUsings&gt;True&lt;/OptimizeUsings&gt;&lt;EmbraceInRegion&gt;False&lt;/EmbraceInRegion&gt;&lt;RegionName&gt;&lt;/RegionName&gt;&lt;/CSOptimizeUsings&gt;&lt;CSUpdateFileHeader&gt;True&lt;/CSUpdateFileHeader&gt;&lt;/Profile&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=Typescript/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;Profile name="Typescript"&gt;&lt;JsInsertSemicolon&gt;True&lt;/JsInsertSemicolon&gt;&lt;FormatAttributeQuoteDescriptor&gt;True&lt;/FormatAttributeQuoteDescriptor&gt;&lt;CorrectVariableKindsDescriptor&gt;True&lt;/CorrectVariableKindsDescriptor&gt;&lt;VariablesToInnerScopesDescriptor&gt;True&lt;/VariablesToInnerScopesDescriptor&gt;&lt;StringToTemplatesDescriptor&gt;True&lt;/StringToTemplatesDescriptor&gt;&lt;RemoveRedundantQualifiersTs&gt;True&lt;/RemoveRedundantQualifiersTs&gt;&lt;OptimizeImportsTs&gt;True&lt;/OptimizeImportsTs&gt;&lt;/Profile&gt;</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">==========================================================================&#xD;
Squidex Headless CMS&#xD;
==========================================================================&#xD;
Copyright (c) Squidex UG (haftungsbeschraenkt)&#xD;
All rights reserved. Licensed under the MIT license.&#xD;
==========================================================================&#xD;
</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>

14
src/Squidex.Domain.Apps.Entities/Schemas/Commands/ChangeCategory.cs

@ -0,0 +1,14 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Domain.Apps.Entities.Schemas.Commands
{
public sealed class ChangeCategory : SchemaCommand
{
public string Name { get; set; }
}
}

5
src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs

@ -119,6 +119,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
Guard.NotNull(command, nameof(command)); Guard.NotNull(command, nameof(command));
} }
public static void CanChangeCategory(Schema schema, ChangeCategory command)
{
Guard.NotNull(command, nameof(command));
}
public static void CanDelete(Schema schema, DeleteSchema command) public static void CanDelete(Schema schema, DeleteSchema command)
{ {
Guard.NotNull(command, nameof(command)); Guard.NotNull(command, nameof(command));

2
src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs

@ -21,6 +21,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas
string Name { get; } string Name { get; }
string Category { get; }
bool IsPublished { get; } bool IsPublished { get; }
bool IsDeleted { get; } bool IsDeleted { get; }

13
src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs

@ -161,6 +161,14 @@ namespace Squidex.Domain.Apps.Entities.Schemas
ConfigureScripts(c); ConfigureScripts(c);
}); });
case ChangeCategory changeCategory:
return UpdateAsync(changeCategory, c =>
{
GuardSchema.CanChangeCategory(Snapshot.SchemaDef, c);
ChangeCategory(c);
});
case DeleteSchema deleteSchema: case DeleteSchema deleteSchema:
return UpdateAsync(deleteSchema, c => return UpdateAsync(deleteSchema, c =>
{ {
@ -253,6 +261,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas
RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured())); RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured()));
} }
public void ChangeCategory(ChangeCategory command)
{
RaiseEvent(SimpleMapper.Map(command, new SchemaCategoryChanged()));
}
public void Delete(DeleteSchema command) public void Delete(DeleteSchema command)
{ {
RaiseEvent(SimpleMapper.Map(command, new SchemaDeleted())); RaiseEvent(SimpleMapper.Map(command, new SchemaDeleted()));

8
src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs

@ -28,6 +28,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State
[JsonProperty] [JsonProperty]
public string Name { get; set; } public string Name { get; set; }
[JsonProperty]
public string Category { get; set; }
[JsonProperty] [JsonProperty]
public int TotalFields { get; set; } = 0; public int TotalFields { get; set; } = 0;
@ -126,6 +129,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State
TotalFields++; TotalFields++;
} }
protected void On(SchemaCategoryChanged @event, FieldRegistry registry)
{
Category = @event.Name;
}
protected void On(SchemaPublished @event, FieldRegistry registry) protected void On(SchemaPublished @event, FieldRegistry registry)
{ {
SchemaDef = SchemaDef.Publish(); SchemaDef = SchemaDef.Publish();

17
src/Squidex.Domain.Apps.Events/Schemas/SchemaCategoryChanged.cs

@ -0,0 +1,17 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Events.Schemas
{
[EventType(nameof(SchemaCategoryChanged))]
public sealed class SchemaCategoryChanged : SchemaEvent
{
public string Name { get; set; }
}
}

25
src/Squidex/Areas/Api/Controllers/Schemas/Models/ChangeCategoryDto.cs

@ -0,0 +1,25 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Schemas.Models
{
public sealed class ChangeCategoryDto
{
/// <summary>
/// The name of the category.
/// </summary>
public string Name { get; set; }
public ChangeCategory ToCommand()
{
return SimpleMapper.Map(this, new ChangeCategory());
}
}
}

5
src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs

@ -28,6 +28,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
[RegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")] [RegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")]
public string Name { get; set; } public string Name { get; set; }
/// <summary>
/// The name of the category.
/// </summary>
public string Category { get; set; }
/// <summary> /// <summary>
/// The schema properties. /// The schema properties.
/// </summary> /// </summary>

22
src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs

@ -147,6 +147,28 @@ namespace Squidex.Areas.Api.Controllers.Schemas
return NoContent(); return NoContent();
} }
/// <summary>
/// Update a schema category.
/// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="name">The name of the schema.</param>
/// <param name="request">The schema object that needs to updated.</param>
/// <returns>
/// 204 => Schema has been updated.
/// 400 => Schema properties are not valid.
/// 404 => Schema or app not found.
/// </returns>
[MustBeAppDeveloper]
[HttpPut]
[Route("apps/{app}/schemas/{name}/category")]
[ApiCosts(1)]
public async Task<IActionResult> PutCategory(string app, string name, [FromBody] ChangeCategoryDto request)
{
await CommandBus.PublishAsync(request.ToCommand());
return NoContent();
}
/// <summary> /// <summary>
/// Update the scripts of a schema. /// Update the scripts of a schema.
/// </summary> /// </summary>

2
src/Squidex/app/shared/services/schemas.fields.spec.ts

@ -343,7 +343,7 @@ describe('StringField', () => {
}); });
function createSchema(properties: SchemaPropertiesDto, index = 1, fields: FieldDto[]) { function createSchema(properties: SchemaPropertiesDto, index = 1, fields: FieldDto[]) {
return new SchemaDetailsDto('id' + index, 'schema' + index, properties, true, null!, null!, null!, null!, null!, fields); return new SchemaDetailsDto('id' + index, 'schema' + index, '', properties, true, null!, null!, null!, null!, null!, fields);
} }
function createField(properties: FieldPropertiesDto, index = 1, partitioning = 'languages') { function createField(properties: FieldPropertiesDto, index = 1, partitioning = 'languages') {

29
src/Squidex/app/shared/services/schemas.service.spec.ts

@ -21,6 +21,7 @@ import {
SchemaPropertiesDto, SchemaPropertiesDto,
SchemasService, SchemasService,
UpdateFieldDto, UpdateFieldDto,
UpdateSchemaCategoryDto,
UpdateSchemaDto, UpdateSchemaDto,
UpdateSchemaScriptsDto, UpdateSchemaScriptsDto,
Version Version
@ -70,6 +71,7 @@ describe('SchemasService', () => {
{ {
id: 'id1', id: 'id1',
name: 'name1', name: 'name1',
category: 'category1',
properties: { properties: {
label: 'label1', label: 'label1',
hints: 'hints1' hints: 'hints1'
@ -85,6 +87,7 @@ describe('SchemasService', () => {
{ {
id: 'id2', id: 'id2',
name: 'name2', name: 'name2',
category: 'category2',
properties: { properties: {
label: 'label2', label: 'label2',
hints: 'hints2' hints: 'hints2'
@ -100,11 +103,11 @@ describe('SchemasService', () => {
]); ]);
expect(schemas).toEqual([ expect(schemas).toEqual([
new SchemaDto('id1', 'name1', new SchemaPropertiesDto('label1', 'hints1'), true, 'Created1', 'LastModifiedBy1', new SchemaDto('id1', 'name1', 'category1', new SchemaPropertiesDto('label1', 'hints1'), true, 'Created1', 'LastModifiedBy1',
DateTime.parseISO_UTC('2016-12-12T10:10'), DateTime.parseISO_UTC('2016-12-12T10:10'),
DateTime.parseISO_UTC('2017-12-12T10:10'), DateTime.parseISO_UTC('2017-12-12T10:10'),
new Version('11')), new Version('11')),
new SchemaDto('id2', 'name2', new SchemaPropertiesDto('label2', 'hints2'), true, 'Created2', 'LastModifiedBy2', new SchemaDto('id2', 'name2', 'category2', new SchemaPropertiesDto('label2', 'hints2'), true, 'Created2', 'LastModifiedBy2',
DateTime.parseISO_UTC('2016-10-12T10:10'), DateTime.parseISO_UTC('2016-10-12T10:10'),
DateTime.parseISO_UTC('2017-10-12T10:10'), DateTime.parseISO_UTC('2017-10-12T10:10'),
new Version('22')) new Version('22'))
@ -128,6 +131,7 @@ describe('SchemasService', () => {
req.flush({ req.flush({
id: 'id1', id: 'id1',
name: 'name1', name: 'name1',
category: 'category1',
isPublished: true, isPublished: true,
created: '2016-12-12T10:10', created: '2016-12-12T10:10',
createdBy: 'Created1', createdBy: 'Created1',
@ -250,7 +254,7 @@ describe('SchemasService', () => {
}); });
expect(schema).toEqual( expect(schema).toEqual(
new SchemaDetailsDto('id1', 'name1', new SchemaPropertiesDto('label1', 'hints1'), true, 'Created1', 'LastModifiedBy1', new SchemaDetailsDto('id1', 'name1', 'category1', new SchemaPropertiesDto('label1', 'hints1'), true, 'Created1', 'LastModifiedBy1',
DateTime.parseISO_UTC('2016-12-12T10:10'), DateTime.parseISO_UTC('2016-12-12T10:10'),
DateTime.parseISO_UTC('2017-12-12T10:10'), DateTime.parseISO_UTC('2017-12-12T10:10'),
new Version('2'), new Version('2'),
@ -297,7 +301,7 @@ describe('SchemasService', () => {
}); });
expect(schema).toEqual( expect(schema).toEqual(
new SchemaDetailsDto('1', dto.name, new SchemaPropertiesDto(), false, user, user, now, now, new Version('2'), [])); new SchemaDetailsDto('1', dto.name, '', new SchemaPropertiesDto(), false, user, user, now, now, new Version('2'), []));
})); }));
it('should make post request to add field', it('should make post request to add field',
@ -342,7 +346,7 @@ describe('SchemasService', () => {
const dto = new UpdateSchemaScriptsDto(); const dto = new UpdateSchemaScriptsDto();
schemasService.putSchemaScripts('my-app', 'my-schema', dto, version).subscribe(); schemasService.putScripts('my-app', 'my-schema', dto, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/scripts'); const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/scripts');
@ -352,6 +356,21 @@ describe('SchemasService', () => {
req.flush({}); req.flush({});
})); }));
it('should make put request to update category',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
const dto = new UpdateSchemaCategoryDto();
schemasService.putCategory('my-app', 'my-schema', dto, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/category');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({});
}));
it('should make put request to update field', it('should make put request to update field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => { inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {

33
src/Squidex/app/shared/services/schemas.service.ts

@ -104,6 +104,7 @@ export class SchemaDto {
constructor( constructor(
public readonly id: string, public readonly id: string,
public readonly name: string, public readonly name: string,
public readonly category: string,
public readonly properties: SchemaPropertiesDto, public readonly properties: SchemaPropertiesDto,
public readonly isPublished: boolean, public readonly isPublished: boolean,
public readonly createdBy: string, public readonly createdBy: string,
@ -118,7 +119,7 @@ export class SchemaDto {
export class SchemaDetailsDto extends SchemaDto { export class SchemaDetailsDto extends SchemaDto {
public readonly listFields: FieldDto[]; public readonly listFields: FieldDto[];
constructor(id: string, name: string, properties: SchemaPropertiesDto, isPublished: boolean, createdBy: string, lastModifiedBy: string, created: DateTime, lastModified: DateTime, version: Version, constructor(id: string, name: string, category: string, properties: SchemaPropertiesDto, isPublished: boolean, createdBy: string, lastModifiedBy: string, created: DateTime, lastModified: DateTime, version: Version,
public readonly fields: FieldDto[], public readonly fields: FieldDto[],
public readonly scriptQuery?: string, public readonly scriptQuery?: string,
public readonly scriptCreate?: string, public readonly scriptCreate?: string,
@ -126,7 +127,7 @@ export class SchemaDetailsDto extends SchemaDto {
public readonly scriptDelete?: string, public readonly scriptDelete?: string,
public readonly scriptChange?: string public readonly scriptChange?: string
) { ) {
super(id, name, properties, isPublished, createdBy, lastModifiedBy, created, lastModified, version); super(id, name, category, properties, isPublished, createdBy, lastModifiedBy, created, lastModified, version);
this.listFields = this.fields.filter(x => x.properties.isListField); this.listFields = this.fields.filter(x => x.properties.isListField);
@ -600,6 +601,13 @@ export class UpdateSchemaDto {
} }
} }
export class UpdateSchemaCategoryDto {
constructor(
public readonly category?: string
) {
}
}
export class AddFieldDto { export class AddFieldDto {
constructor( constructor(
public readonly name: string, public readonly name: string,
@ -659,7 +667,9 @@ export class SchemasService {
return new SchemaDto( return new SchemaDto(
item.id, item.id,
item.name, properties, item.name,
item.category,
properties,
item.isPublished, item.isPublished,
item.createdBy, item.createdBy,
item.lastModifiedBy, item.lastModifiedBy,
@ -698,7 +708,9 @@ export class SchemasService {
return new SchemaDetailsDto( return new SchemaDetailsDto(
body.id, body.id,
body.name, properties, body.name,
body.category,
properties,
body.isPublished, body.isPublished,
body.createdBy, body.createdBy,
body.lastModifiedBy, body.lastModifiedBy,
@ -727,6 +739,7 @@ export class SchemasService {
return new SchemaDetailsDto( return new SchemaDetailsDto(
body.id, body.id,
dto.name, dto.name,
'',
dto.properties || new SchemaPropertiesDto(), dto.properties || new SchemaPropertiesDto(),
false, false,
user, user,
@ -781,7 +794,7 @@ export class SchemasService {
.pretifyError('Failed to delete schema. Please reload.'); .pretifyError('Failed to delete schema. Please reload.');
} }
public putSchemaScripts(appName: string, schemaName: string, dto: UpdateSchemaScriptsDto, version: Version): Observable<Versioned<any>> { public putScripts(appName: string, schemaName: string, dto: UpdateSchemaScriptsDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/scripts`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/scripts`);
return HTTP.putVersioned(this.http, url, dto, version) return HTTP.putVersioned(this.http, url, dto, version)
@ -831,6 +844,16 @@ export class SchemasService {
.pretifyError('Failed to unpublish schema. Please reload.'); .pretifyError('Failed to unpublish schema. Please reload.');
} }
public putCategory(appName: string, schemaName: string, dto: UpdateSchemaCategoryDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/category`);
return HTTP.putVersioned(this.http, url, dto, version)
.do(() => {
this.analytics.trackEvent('Schema', 'CategoryChanged', appName);
})
.pretifyError('Failed to change category. Please reload.');
}
public putField(appName: string, schemaName: string, fieldId: number, dto: UpdateFieldDto, version: Version): Observable<Versioned<any>> { public putField(appName: string, schemaName: string, fieldId: number, dto: UpdateFieldDto, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}`);

42
src/Squidex/app/shared/state/schemas.state.spec.ts

@ -23,6 +23,7 @@ import {
SchemaDto, SchemaDto,
SchemasService, SchemasService,
UpdateFieldDto, UpdateFieldDto,
UpdateSchemaCategoryDto,
UpdateSchemaDto, UpdateSchemaDto,
UpdateSchemaScriptsDto, UpdateSchemaScriptsDto,
Version, Version,
@ -39,15 +40,15 @@ describe('SchemasState', () => {
const newVersion = new Version('2'); const newVersion = new Version('2');
const oldSchemas = [ const oldSchemas = [
new SchemaDto('id1', 'name1', {}, false, creator, creator, creation, creation, version), new SchemaDto('id1', 'name1', 'category1', {}, false, creator, creator, creation, creation, version),
new SchemaDto('id2', 'name2', {}, true , creator, creator, creation, creation, version) new SchemaDto('id2', 'name2', 'category2', {}, true , creator, creator, creation, creation, version)
]; ];
const field1 = new FieldDto(1, '1', false, false, false, 'l', createProperties('String')); const field1 = new FieldDto(1, '1', false, false, false, 'l', createProperties('String'));
const field2 = new FieldDto(2, '2', true, true, true, 'l', createProperties('Number')); const field2 = new FieldDto(2, '2', true, true, true, 'l', createProperties('Number'));
const schema = const schema =
new SchemaDetailsDto('id2', 'name2', {}, true, new SchemaDetailsDto('id2', 'name2', 'category2', {}, true,
creator, creator, creator, creator,
creation, creation, creation, creation,
version, version,
@ -170,12 +171,26 @@ describe('SchemasState', () => {
expectToBeModified(schema_1); expectToBeModified(schema_1);
}); });
it('should change category and update user info when category changed', () => {
const category = 'my-new-category';
schemasService.setup(x => x.putCategory(app, oldSchemas[0].name, It.is<UpdateSchemaCategoryDto>(i => i.category === category), version))
.returns(() => Observable.of(new Versioned<any>(newVersion, {})));
schemasState.changeCategory(oldSchemas[0], category, modified).subscribe();
const schema_1 = schemasState.snapshot.schemas.at(0);
expect(schema_1.category).toEqual(category);
expectToBeModified(schema_1);
});
describe('with selection', () => { describe('with selection', () => {
beforeEach(() => { beforeEach(() => {
schemasState.select(schema.name).subscribe(); schemasState.select(schema.name).subscribe();
}); });
it('should unmark published and update user info when published selected schema', () => { it('should nmark published and update user info when published selected schema', () => {
schemasService.setup(x => x.publishSchema(app, schema.name, version)) schemasService.setup(x => x.publishSchema(app, schema.name, version))
.returns(() => Observable.of(new Versioned<any>(newVersion, {}))); .returns(() => Observable.of(new Versioned<any>(newVersion, {})));
@ -187,6 +202,21 @@ describe('SchemasState', () => {
expectToBeModified(schema_1); expectToBeModified(schema_1);
}); });
it('should change category and update user info when category of selected schema changed', () => {
const category = 'my-new-category';
schemasService.setup(x => x.putCategory(app, oldSchemas[0].name, It.is<UpdateSchemaCategoryDto>(i => i.category === category), version))
.returns(() => Observable.of(new Versioned<any>(newVersion, {})));
schemasState.changeCategory(oldSchemas[0], category, modified).subscribe();
const schema_1 = schemasState.snapshot.schemas.at(0);
expect(schema_1.category).toEqual(category);
expectToBeModified(schema_1);
});
it('should update properties and update user info when updated', () => { it('should update properties and update user info when updated', () => {
const request = new UpdateSchemaDto('name2_label', 'name2_hints'); const request = new UpdateSchemaDto('name2_label', 'name2_hints');
@ -205,7 +235,7 @@ describe('SchemasState', () => {
it('should update script properties and update user info when scripts configured', () => { it('should update script properties and update user info when scripts configured', () => {
const request = new UpdateSchemaScriptsDto('query', 'create', 'update', 'delete', 'change'); const request = new UpdateSchemaScriptsDto('query', 'create', 'update', 'delete', 'change');
schemasService.setup(x => x.putSchemaScripts(app, schema.name, It.isAny(), version)) schemasService.setup(x => x.putScripts(app, schema.name, It.isAny(), version))
.returns(() => Observable.of(new Versioned<any>(newVersion, {}))); .returns(() => Observable.of(new Versioned<any>(newVersion, {})));
schemasState.configureScripts(schema, request, modified).subscribe(); schemasState.configureScripts(schema, request, modified).subscribe();
@ -223,7 +253,7 @@ describe('SchemasState', () => {
it('should add schema to snapshot when created', () => { it('should add schema to snapshot when created', () => {
const request = new CreateSchemaDto('newName'); const request = new CreateSchemaDto('newName');
const result = new SchemaDetailsDto('id4', 'newName', {}, false, modifier, modifier, modified, modified, version, []); const result = new SchemaDetailsDto('id4', 'newName', '', {}, false, modifier, modifier, modified, modified, version, []);
schemasService.setup(x => x.postSchema(app, request, modifier, modified)) schemasService.setup(x => x.postSchema(app, request, modifier, modified))
.returns(() => Observable.of(result)); .returns(() => Observable.of(result));

49
src/Squidex/app/shared/state/schemas.state.ts

@ -36,6 +36,7 @@ import {
SchemaPropertiesDto, SchemaPropertiesDto,
SchemasService, SchemasService,
UpdateFieldDto, UpdateFieldDto,
UpdateSchemaCategoryDto,
UpdateSchemaDto, UpdateSchemaDto,
UpdateSchemaScriptsDto UpdateSchemaScriptsDto
} from './../services/schemas.service'; } from './../services/schemas.service';
@ -272,6 +273,14 @@ export class SchemasState extends State<Snapshot> {
.notify(this.dialogs); .notify(this.dialogs);
} }
public changeCategory(schema: SchemaDto, name: string, now?: DateTime): Observable<any> {
return this.schemasService.putCategory(this.appName, schema.name, new UpdateSchemaCategoryDto(name), schema.version)
.do(dto => {
this.replaceSchema(changeCategory(schema, name, this.user, dto.version, now));
})
.notify(this.dialogs);
}
public enableField(schema: SchemaDetailsDto, field: FieldDto, now?: DateTime): Observable<any> { public enableField(schema: SchemaDetailsDto, field: FieldDto, now?: DateTime): Observable<any> {
return this.schemasService.enableField(this.appName, schema.name, field.fieldId, schema.version) return this.schemasService.enableField(this.appName, schema.name, field.fieldId, schema.version)
.do(dto => { .do(dto => {
@ -337,7 +346,7 @@ export class SchemasState extends State<Snapshot> {
} }
public configureScripts(schema: SchemaDetailsDto, request: UpdateSchemaScriptsDto, now?: DateTime): Observable<any> { public configureScripts(schema: SchemaDetailsDto, request: UpdateSchemaScriptsDto, now?: DateTime): Observable<any> {
return this.schemasService.putSchemaScripts(this.appName, schema.name, request, schema.version) return this.schemasService.putScripts(this.appName, schema.name, request, schema.version)
.do(dto => { .do(dto => {
this.replaceSchema(configureScripts(schema, request, this.user, dto.version, now)); this.replaceSchema(configureScripts(schema, request, this.user, dto.version, now));
}) })
@ -375,6 +384,7 @@ const setPublished = (schema: SchemaDto | SchemaDetailsDto, publish: boolean, us
return new SchemaDetailsDto( return new SchemaDetailsDto(
schema.id, schema.id,
schema.name, schema.name,
schema.category,
schema.properties, schema.properties,
publish, publish,
schema.createdBy, user, schema.createdBy, user,
@ -390,6 +400,7 @@ const setPublished = (schema: SchemaDto | SchemaDetailsDto, publish: boolean, us
return new SchemaDto( return new SchemaDto(
schema.id, schema.id,
schema.name, schema.name,
schema.category,
schema.properties, schema.properties,
publish, publish,
schema.createdBy, user, schema.createdBy, user,
@ -398,10 +409,41 @@ const setPublished = (schema: SchemaDto | SchemaDetailsDto, publish: boolean, us
} }
}; };
const changeCategory = (schema: SchemaDto | SchemaDetailsDto, category: string, user: string, version: Version, now?: DateTime) => {
if (Types.is(schema, SchemaDetailsDto)) {
return new SchemaDetailsDto(
schema.id,
schema.name,
category,
schema.properties,
schema.isPublished,
schema.createdBy, user,
schema.created, now || DateTime.now(),
version,
schema.fields,
schema.scriptQuery,
schema.scriptCreate,
schema.scriptUpdate,
schema.scriptDelete,
schema.scriptChange);
} else {
return new SchemaDto(
schema.id,
schema.name,
category,
schema.properties,
schema.isPublished,
schema.createdBy, user,
schema.created, now || DateTime.now(),
version);
}
};
const configureScripts = (schema: SchemaDetailsDto, scripts: UpdateSchemaScriptsDto, user: string, version: Version, now?: DateTime) => const configureScripts = (schema: SchemaDetailsDto, scripts: UpdateSchemaScriptsDto, user: string, version: Version, now?: DateTime) =>
new SchemaDetailsDto( new SchemaDetailsDto(
schema.id, schema.id,
schema.name, schema.name,
schema.category,
schema.properties, schema.properties,
schema.isPublished, schema.isPublished,
schema.createdBy, user, schema.createdBy, user,
@ -418,6 +460,7 @@ const updateProperties = (schema: SchemaDetailsDto, properties: SchemaProperties
new SchemaDetailsDto( new SchemaDetailsDto(
schema.id, schema.id,
schema.name, schema.name,
schema.category,
properties, properties,
schema.isPublished, schema.isPublished,
schema.createdBy, user, schema.createdBy, user,
@ -434,6 +477,7 @@ const addField = (schema: SchemaDetailsDto, field: FieldDto, user: string, versi
new SchemaDetailsDto( new SchemaDetailsDto(
schema.id, schema.id,
schema.name, schema.name,
schema.category,
schema.properties, schema.properties,
schema.isPublished, schema.isPublished,
schema.createdBy, user, schema.createdBy, user,
@ -450,6 +494,7 @@ const updateField = (schema: SchemaDetailsDto, field: FieldDto, user: string, ve
new SchemaDetailsDto( new SchemaDetailsDto(
schema.id, schema.id,
schema.name, schema.name,
schema.category,
schema.properties, schema.properties,
schema.isPublished, schema.isPublished,
schema.createdBy, user, schema.createdBy, user,
@ -466,6 +511,7 @@ const replaceFields = (schema: SchemaDetailsDto, fields: FieldDto[], user: strin
new SchemaDetailsDto( new SchemaDetailsDto(
schema.id, schema.id,
schema.name, schema.name,
schema.category,
schema.properties, schema.properties,
schema.isPublished, schema.isPublished,
schema.createdBy, user, schema.createdBy, user,
@ -482,6 +528,7 @@ const removeField = (schema: SchemaDetailsDto, field: FieldDto, user: string, ve
new SchemaDetailsDto( new SchemaDetailsDto(
schema.id, schema.id,
schema.name, schema.name,
schema.category,
schema.properties, schema.properties,
schema.isPublished, schema.isPublished,
schema.createdBy, user, schema.createdBy, user,

8
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs

@ -186,6 +186,14 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
GuardSchema.CanReorder(schema_0, command); GuardSchema.CanReorder(schema_0, command);
} }
[Fact]
public void CanChangeCategory_should_not_throw_exception()
{
var command = new ChangeCategory();
GuardSchema.CanChangeCategory(schema_0, command);
}
[Fact] [Fact]
public void CanDelete_should_not_throw_exception() public void CanDelete_should_not_throw_exception()
{ {

19
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaGrainTests.cs

@ -211,6 +211,25 @@ namespace Squidex.Domain.Apps.Entities.Schemas
); );
} }
[Fact]
public async Task ChangeCategory_should_create_events_and_update_state()
{
var command = new ChangeCategory { Name = "my-category" };
await ExecuteCreateAsync();
var result = await sut.ExecuteAsync(CreateCommand(command));
result.ShouldBeEquivalent(new EntitySavedResult(1));
Assert.Equal(command.Name, sut.Snapshot.Category);
LastEvents
.ShouldHaveSameEvents(
CreateEvent(new SchemaCategoryChanged { Name = command.Name })
);
}
[Fact] [Fact]
public async Task Delete_should_create_events_and_update_state() public async Task Delete_should_create_events_and_update_state()
{ {

Loading…
Cancel
Save