Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Content and media type CRUD controllers and services #14665

Merged
merged 69 commits into from
Aug 17, 2023

Conversation

kjac
Copy link
Contributor

@kjac kjac commented Aug 11, 2023

Prerequisites

  • I have added steps to test this contribution in the description below

Description

This adds content and media type CRUD capability to the management API. This includes support for inheritance and compositions, which are not supported by the current (intermediate) content type CRUD controllers.

The PR introduces the ContentTypeEditingService and MediaTypeEditingService, which are the content type and media type equivalents to the ContentEditingService and MediaEditingService, which were introduced earlier for content and media editing.

Among many things, the PR includes a refactor of ContentTypeSort, which has been long overdue. The refactor is necessary to complete this PR without piling on additional technical debt. A fair few changes in this PR relates exclusively to the refactor, thus strictly not directly to the CRUD features being introduced.

Testing this PR

It's no small feat to test this PR, as there are so many pitfalls in the content type space.

The PR includes 120+ new unit tests to test a myriad of service level scenarios. In all likelihood, not all edge cases are covered. Likewise, it is highly unlikely that a systematic test of the API will uncover all potential API issues - the permutations are simply too many. This is not to say that the API shouldn't be tested 😄 but the true test of the API will be when the new backoffice UI starts consuming it. We have to accept that this PR is not flawless and will need amending down the road.

To start testing, reach out to @kjac for Postman collections and test scenarios. Here follows some payloads that can be used initially to test a few creation scenarios.

Create composition base.
{
    "allowedTemplateIds": [],
    "defaultTemplateId": null,
    "cleanup": {
        "preventCleanup": false,
        "keepAllVersionsNewerThanDays": null,
        "keepLatestVersionPerDayForDays": null
    },
    "id": "015bc281-7410-40e2-81b5-b8f7c963bd61",
    "alias": "testCompositionOne",
    "name": "Test Composition One",
    "description": null,
    "icon": "icon-bug color-green",
    "allowedAsRoot": false,
    "variesByCulture": false,
    "variesBySegment": false,
    "isElement": false,
    "properties": [{
            "id": "e524c976-480d-4eb2-9000-85b621f8aa4e",
            "containerId": "d9c77176-73ea-4123-a9cc-5ac44cbd148b",
            "sortOrder": 0,
            "alias": "composedTitle",
            "name": "Composed Title",
            "description": null,
            "dataTypeId": "0cc0eba1-9960-42c9-bf9b-60e150b429ae",
            "variesByCulture": false,
            "variesBySegment": false,
            "validation": {
                "mandatory": false,
                "mandatoryMessage": null,
                "regEx": null,
                "regExMessage": null
            },
            "appearance": {
                "labelOnTop": false
            }
        }
    ],
    "containers": [{
            "id": "d9c77176-73ea-4123-a9cc-5ac44cbd148b",
            "parentId": "2b5d17a2-35b6-476a-8696-1dcab49b3515",
            "name": "Composed Group",
            "type": "Group",
            "sortOrder": 0
        }, {
            "id": "2b5d17a2-35b6-476a-8696-1dcab49b3515",
            "parentId": null,
            "name": "Composed Tab",
            "type": "Tab",
            "sortOrder": 0
        }
    ],
    "allowedContentTypes": [],
    "compositions": []
}
Create content type composed by the composition base, having its own tab and group.
{
    "allowedTemplateIds": [],
    "defaultTemplateId": null,
    "cleanup": {
        "preventCleanup": true,
        "keepAllVersionsNewerThanDays": 11,
        "keepLatestVersionPerDayForDays": 22
    },
    "id": "34283b3e-b2f4-4de6-b6ac-88bdcc1758bc",
    "alias": "testActualOne",
    "name": "Test Actual One",
    "description": null,
    "icon": "icon-anchor color-green",
    "allowedAsRoot": true,
    "variesByCulture": false,
    "variesBySegment": false,
    "isElement": false,
    "properties": [{
            "id": "2161e42c-19e8-43e2-b832-d55d9ce6ad24",
            "containerId": "2cf5dd8c-9e73-43d8-91a0-ef412a75d7d5",
            "sortOrder": 0,
            "alias": "actualTitle",
            "name": "Actual Title",
            "description": null,
            "dataTypeId": "0cc0eba1-9960-42c9-bf9b-60e150b429ae",
            "variesByCulture": false,
            "variesBySegment": false,
            "validation": {
                "mandatory": true,
                "mandatoryMessage": "Fill this out!",
                "regEx": null,
                "regExMessage": null
            },
            "appearance": {
                "labelOnTop": false
            }
        }
    ],
    "containers": [{
            "id": "2cf5dd8c-9e73-43d8-91a0-ef412a75d7d5",
            "parentId": "1b7c8ce1-6991-4433-a290-93d0761c1967",
            "name": "Actual Group",
            "type": "Group",
            "sortOrder": 0
        }, {
            "id": "1b7c8ce1-6991-4433-a290-93d0761c1967",
            "parentId": null,
            "name": "Actual Tab",
            "type": "Tab",
            "sortOrder": 0
        }
    ],
    "allowedContentTypes": [],
    "compositions": [{
		"id": "015bc281-7410-40e2-81b5-b8f7c963bd61",
		"compositionType": "Composition"
	}]
}
Create content type composed by the composition base, adding its own property to the composition tab and group. Also adds the content type above as allowed child type.
{
    "allowedTemplateIds": [],
    "defaultTemplateId": null,
    "cleanup": {
        "preventCleanup": false,
        "keepAllVersionsNewerThanDays": null,
        "keepLatestVersionPerDayForDays": null
    },
    "id": "192408a1-23cb-43b9-a710-7514ce74f4c1",
    "alias": "testActualTwo",
    "name": "Test Actual Two",
    "description": null,
    "icon": "icon-article color-green",
    "allowedAsRoot": true,
    "variesByCulture": false,
    "variesBySegment": false,
    "isElement": false,
    "properties": [{
            "id": "4176df07-0ca7-4524-942b-68745f1aabfc",
            "containerId": "292c27dc-c236-4ae2-91e7-f93182bc3737",
            "sortOrder": 0,
            "alias": "actualTitle",
            "name": "Actual Title",
            "description": null,
            "dataTypeId": "0cc0eba1-9960-42c9-bf9b-60e150b429ae",
            "variesByCulture": false,
            "variesBySegment": false,
            "validation": {
                "mandatory": true,
                "mandatoryMessage": "Fill this out!",
                "regEx": null,
                "regExMessage": null
            },
            "appearance": {
                "labelOnTop": false
            }
        }
    ],
    "containers": [{
            "id": "292c27dc-c236-4ae2-91e7-f93182bc3737",
            "parentId": "8b2cf0a9-0916-49b1-9223-9cc1bcfa50a2",
            "name": "Composed Group",
            "type": "Group",
            "sortOrder": 0
		}, {
            "id": "8b2cf0a9-0916-49b1-9223-9cc1bcfa50a2",
            "parentId": null,
            "name": "Composed Tab",
            "type": "Tab",
            "sortOrder": 0
        }
	],
    "allowedContentTypes": [{
			"id": "34283b3e-b2f4-4de6-b6ac-88bdcc1758bc",
			"sortOrder": 0
		}
	],
    "compositions": [{
			"id": "015bc281-7410-40e2-81b5-b8f7c963bd61",
			"compositionType": "Composition"
		}
	]
}
Create a media type.
{
    "id": "22237548-aba0-4534-94d8-46420468e5a5",
    "alias": "apiArticle",
    "name": "Article from API",
    "description": null,
    "icon": "icon-article color-green",
    "allowedAsRoot": true,
    "variesByCulture": false,
    "variesBySegment": false,
    "isElement": false,
    "properties": [{
            "id": "2bba2025-ba6a-49ea-a7b2-92829ab2da43",
            "containerId": "67a7fb4e-21db-400a-a7a2-ecd58aa951be",
            "sortOrder": 0,
            "alias": "umbracoFile",
            "name": "Article",
            "description": null,
            "dataTypeId": "bc1e266c-dac4-4164-bf08-8a1ec6a7143d",
            "variesByCulture": false,
            "variesBySegment": false,
            "validation": {
                "mandatory": true,
                "mandatoryMessage": null,
                "regEx": null,
                "regExMessage": null
            },
            "appearance": {
                "labelOnTop": false
            }
        }, {
            "id": "e6f94a61-d52c-4d4f-859b-7b9201f0c603",
            "containerId": "67a7fb4e-21db-400a-a7a2-ecd58aa951be",
            "sortOrder": 1,
            "alias": "umbracoExtension",
            "name": "Type",
            "description": null,
            "dataTypeId": "f0bc4bfb-b499-40d6-ba86-058885a5178c",
            "variesByCulture": false,
            "variesBySegment": false,
            "validation": {
                "mandatory": false,
                "mandatoryMessage": null,
                "regEx": null,
                "regExMessage": null
            },
            "appearance": {
                "labelOnTop": false
            }
        }, {
            "id": "5174163f-44d5-4ff2-8de3-1099bc347229",
            "containerId": "67a7fb4e-21db-400a-a7a2-ecd58aa951be",
            "sortOrder": 2,
            "alias": "umbracoBytes",
            "name": "Size",
            "description": "in bytes",
            "dataTypeId": "930861bf-e262-4ead-a704-f99453565708",
            "variesByCulture": false,
            "variesBySegment": false,
            "validation": {
                "mandatory": false,
                "mandatoryMessage": null,
                "regEx": null,
                "regExMessage": null
            },
            "appearance": {
                "labelOnTop": false
            }
        }
    ],
    "containers": [{
            "id": "67a7fb4e-21db-400a-a7a2-ecd58aa951be",
            "parentId": null,
            "name": "Article",
            "type": "Group",
            "sortOrder": 1
        }
    ],
    "allowedContentTypes": [],
    "compositions": []
}

Still need to figure out some kinks
# Conflicts:
#	src/Umbraco.Core/Services/OperationStatus/ContentTypeOperationStatus.cs
Let's try and be consistent
Just to make sure nothing explodes on the round trip
kjac added 21 commits July 26, 2023 14:16
…ler' into v14/feature/content_type_controller

# Conflicts:
#	src/Umbraco.Core/Services/ContentTypeEditing/DocumentTypeEditingService.cs
#	src/Umbraco.Core/Services/ContentTypeEditing/IDocumentTypeEditingService.cs
# Conflicts:
#	src/Umbraco.Cms.Api.Management/Controllers/DocumentType/DocumentTypeControllerBase.cs
# Conflicts:
#	src/Umbraco.Cms.Api.Management/Controllers/DocumentType/DeleteDocumentTypeController.cs
#	src/Umbraco.Cms.Api.Management/Controllers/DocumentType/DocumentTypeControllerBase.cs
#	src/Umbraco.Cms.Api.Management/Controllers/MediaType/MediaTypeControllerBase.cs
Copy link
Contributor

@nikolajlauridsen nikolajlauridsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good, only found a couple of things 😄

@nikolajlauridsen
Copy link
Contributor

Looks great to me, and from what I've tested it all seems to work great 👍

@nikolajlauridsen nikolajlauridsen merged commit 0096add into v14/dev Aug 17, 2023
12 of 13 checks passed
@nikolajlauridsen nikolajlauridsen deleted the v14/feature/content_type_controller branch August 17, 2023 10:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants