Kontent.Ai.Management.Helpers 4.0.0-beta.2

This is a prerelease version of Kontent.Ai.Management.Helpers.
There is a newer version of this package available.
See the version list below for details.
dotnet add package Kontent.Ai.Management.Helpers --version 4.0.0-beta.2
NuGet\Install-Package Kontent.Ai.Management.Helpers -Version 4.0.0-beta.2
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Kontent.Ai.Management.Helpers" Version="4.0.0-beta.2" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Kontent.Ai.Management.Helpers --version 4.0.0-beta.2
#r "nuget: Kontent.Ai.Management.Helpers, 4.0.0-beta.2"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Kontent.Ai.Management.Helpers as a Cake Addin
#addin nuget:?package=Kontent.Ai.Management.Helpers&version=4.0.0-beta.2&prerelease

// Install Kontent.Ai.Management.Helpers as a Cake Tool
#tool nuget:?package=Kontent.Ai.Management.Helpers&version=4.0.0-beta.2&prerelease

Kontent.ai Management .NET SDK

Build & Test codecov Stack Overflow Discord

ℹī¸ This repository is currently being migrated from Kentico GitHub organization. Some aspects may still remain unchanged, thank you for your understanding!

Package Version Downloads Compatibility Documentation
Management SDK NuGet NuGet net6.0 📖
Content Item Edit-URL Builder NuGet NuGet net6.0 📖

Summary

The Kontent.ai Management .NET SDK is a client library used for managing content in Kontent.ai. It provides read/write access to your Kontent.ai projects.

You can use the SDK in the form of a NuGet package to migrate existing content into your Kontent.ai project or update your content model.

The Management SDK does not provide any content filtering options and is not optimized for content delivery. If you need to deliver larger amounts of content we recommend using the Delivery SDK instead.

💡 If you want to see all .NET related resources including REST API reference with .NET code samples for every endpoint, check out the "Develop .NET apps" overview page.

Prerequisites

To manage content in a Kontent.ai project via the Management API, you first need to activate the API for the project. See our documentation on how you can activate the Management API.

Using the ManagementClient

The ManagementClient class is the main class of the SDK. Using this class, you can import, update, view and delete content items, language variants, and others in your Kontent.ai projects.

To create an instance of the class, you need to provide:

  • ProjectId: the ID of your Kontent.ai project. This parameter must always be set.
  • SubscriptionId: the ID of your subcription. Set it up if you need to manage users and their permissions.
  • ApiKey: either Management or Subscription API key.
    • Subscription API key can be used for all endpoints but is limited to subscription admins
    • Management API key can be used with project-specific endpoints and is limited to users with the Manage APIs permission.
// Initializes an instance of the ManagementClient client with specified options.
var client = new ManagementClient(new ManagementOptions
{
    ProjectId = "cbbe2d5c-17c6-0128-be26-e997ba7c1619",
    SubscriptionId = "a27b9841-fc99-48a7-a46d-65b2549d6c0"
    ApiKey = "ew0...1eo"
});

Once you create a ManagementClient, you can start managing content in your project by calling methods on the client instance.

Codename vs. ID vs. External ID

The SDK uses an Reference object representation identifying an entity you want to perform the given operation on. There are 3 types of identification you can use to create the identifier:

var codenameIdentifier = Reference.ByCodename("on_roasts");
var idIdentifier = Reference.ById(Guid.Parse("9539c671-d578-4fd3-aa5c-b2d8e486c9b8"));
var externalIdIdentifier = Reference.ByExternalId("Ext-Item-456-Brno");
  • Codenames are generated automatically by Kontent.ai based on the object's name. They can make your code more readable but are not guaranteed to be unique. Use them only when there is no chance of naming conflicts.
    • Unless set while creating a content item, the codename is initially generated from the item's name. When updating an item without specifying its codename, the codename gets autogenerated based on the name's value.
  • (internal) IDs are random GUIDs assigned to objects by Kontent.ai at the moment of import/creation. They are unique and generated by the system for existing objects. This means you cannot use them to refer to content that is not imported yet. This identification is used for all responses from Management API
  • External IDs are string-based custom identifiers defined by you. Use them when importing a batch of cross-referencing content. See Importing linked content for more details.

The set of identification types varies based on the entity. The SDK does not check whether, for example, webhooks allows only ID for identification. This is being handled by the API itself. To check what identification types are allowed for a given entity, see the API documentation.

User identifier

The SDK also suports endpoints that require either user ID or email. UserIdentifier object represents identification of an user. See following example for more detail.

UserIdentifier identifier = UserIdentifier.ById("usr_0vKjTCH2TkO687K3y3bKNS");
UserIdentifier identifier = UserIdentifier.ByEmail("user@email.com");

Handling Kontent.ai errors

You can catch Kontent.ai errors (more in error section in Management API reference)) by using try-catch block and catching Kontent.Ai.Management.Exceptions.ManagementException.

try
{
    var response = await client.UpsertLanguageVariantAsync(identifier, elements);
}
catch (ManagementException ex)
{
    Console.WriteLine(ex.StatusCode);
    Console.WriteLine(ex.Message);
}

Working with language variants

The ManagementClient supports working with strongly-typed models. You can generate strongly-typed models from your content types using the Kontent.ai model generator utility and then be able to retrieve the data in a strongly typed form.

// Retrieve strongly-typed content item
var itemIdentifier = Reference.ById(Guid.Parse("9539c671-d578-4fd3-aa5c-b2d8e486c9b8"));
var languageIdentifier = Reference.ByCodename("en-US");
var identifier = new LanguageVariantIdentifier(itemIdentifier, languageIdentifier);

var response = await client.GetLanguageVariantAsync<ArticleModel>(identifier);

response.Elements.Title = new TextElement() { Value = "On Roasts - changed" };
response.Elements.PostDate = new DateTimeElement() { Value = new DateTime(2018, 7, 4) };

var responseVariant = await client.UpsertLanguageVariantAsync(identifier, response.Elements);

Or you can construct instance of strongly type model without necessity to retrieve the data from Kontent. You just provide the elements you want to change. If the property is not initialized (is null) the SDK won't include to the payload.

// Defines the content elements to update
var stronglyTypedElements = new ArticleModel
{
    Title = new TextElement() { Value = "On Roasts - changed" },
    PostDate = new DateTimeElement() { Value = new DateTime(2018, 7, 4) },
};

// Specifies the content item and the language variant
var itemIdentifier = Reference.ByCodename("on_roasts");
var languageIdentifier = Reference.ByCodename("en-US");
var identifier = new LanguageVariantIdentifier(itemIdentifier, languageIdentifier);

// Upserts a language variant of a content item
var response = await client.UpsertLanguageVariantAsync(identifier, stronglyTypedElements);

You can also use anonymous dynamic objects to work with language variants. For upsert operations, you need to provide element identification - element.id/element.codename (optionally load element's ID or codename from generated content model):

var itemIdentifier = Reference.ById(Guid.Parse("9539c671-d578-4fd3-aa5c-b2d8e486c9b8"));
var languageIdentifier = Reference.ByCodename("en-US");
var identifier = new LanguageVariantIdentifier(itemIdentifier, languageIdentifier);

// Elements to update
var elements = new dynamic[]
{
    new
    {
        element = new
        {
            // You can use `Reference.ById` if you don't have the model
            id = typeof(ArticleModel).GetProperty(nameof(ArticleModel.Title)).GetKontentElementId()
        },
        value = "On Roasts - changed",
    },
    new
    {
        element = new
        {
            // You can use `Reference.ById` if you don't have the model
            id = typeof(ArticleModel).GetProperty(nameof(ArticleModel.PostDate)).GetKontentElementId()
        },
        value = new DateTime(2018, 7, 4),
    }
};

var upsertModel = new LanguageVariantUpsertModel() { Elements = elements };

// Upserts a language variant of a content item
var response = await client.UpsertLanguageVariantAsync(identifier, upsertModel);

You can also build your dynamic object representations of the elements from strongly typed elements models with ElementBuilder. That is recommended approach when you don't need to work with strongly typed models because it ensures you provided the element identification - element.id/element.codename.

var itemIdentifier = Reference.ById(Guid.Parse("9539c671-d578-4fd3-aa5c-b2d8e486c9b8"));
var languageIdentifier = Reference.ByCodename("en-US");
var identifier = new LanguageVariantIdentifier(itemIdentifier, languageIdentifier);

// Elements to update
var elements = ElementBuilder.GetElementsAsDynamic(new BaseElement[]
{
    new TextElement()
    {
        // You can use `Reference.ById` if you don't have the model
        Element = Reference.ById(typeof(ArticleModel).GetProperty(nameof(ArticleModel.Title)).GetKontentElementId()),
        Value = "On Roasts - changed"
    },
    new DateTimeElement()
    {
        // You can use `Reference.ById` if you don't have the model
        Element = Reference.ById(typeof(ArticleModel).GetProperty(nameof(ArticleModel.PostDate)).GetKontentElementId()),
        Value = new DateTime(2018, 7, 4)
    },
});

var upsertModel = new LanguageVariantUpsertModel() { Elements = elements };

// Upserts a language variant of a content item
var response = await client.UpsertLanguageVariantAsync(identifier, upsertModel);

Working with assets

The Kontent.ai model generator utility currently does not support generating a strongly-typed model from your asset type. But you can construct an instance of a strongly-typed model yourself. You just need to provide the elements you want to change.

var stream = new MemoryStream(Encoding.UTF8.GetBytes("Hello world from CM API .NET SDK"));
var fileName = "Hello.txt";
var contentType = "text/plain";

// Returns a reference that you can later use to create an asset
var fileResult = await client.UploadFileAsync(new FileContentSource(stream, fileName, contentType));

// Defines the content elements to create
var stronglyTypedTaxonomyElements = new AssetMetadataModel
{
    TaxonomyCategories = new TaxonomyElement()
    {
        Value = new[] { "hello", "SDK" }.Select(Reference.ByCodename)
    },
};

// Defines the asset to create
var asset = new AssetCreateModel<AssetMetadataModel>
{
    FileReference = fileResult,
    Elements = stronglyTypedTaxonomyElements
};

// Creates an asset
var response = await client.CreateAssetAsync(asset);

You can also build your dynamic object representations of the elements from strongly typed elements models with ElementBuilder. That is recommended approach when you don't need to work with strongly typed models because it ensures you provided the element identification - element.id/element.codename.

 // Elements to update
var taxonomyElements = ElementBuilder.GetElementsAsDynamic(
    new TaxonomyElement
    {
        Element = Reference.ByCodename("taxonomy-categories"),
        Value = new[]
        {
            Reference.ByCodename("hello"),
            Reference.ByCodename("SDK"),
        }
    });

// Defines the asset to update
var asset = new AssetUpsertModel
{
    Elements = taxonomyElements
};

var assetReference = Reference.ById(Guid.Parse("6d1c8ee9-76bc-474f-b09f-8a54a98f06ea"));

// Updates asset metadata
var response = await client.UpsertAssetAsync(assetReference, asset);

You can also use anonymous dynamic objects to work with assets same as with the language variants.

Quick start

Retrieving content items

Responses from Kontent.ai API are paginated. To retrieve all of content items, you need to go page by page. Here's how:

var items = new List<ContentItemModel>();

var response = await _client.ListContentItemsAsync();

while (true)
{
    items.AddRange(response);

    if (!response.HasNextPage())
    {
        break;
    }

    response = await response.GetNextPage();
}

If you need all content items you can use GetAllAsync:

var response = await _client.ListContentItemsAsync().GetAllAsync();

Importing content items

Importing content items is a 2 step process, using 2 separate methods:

  1. Creating an empty content item which serves as a wrapper for your content.
  2. Adding content inside a language variant of the content item.

Each content item can consist of several localized variants. The content itself is always part of a specific language variant, even if your project only uses one language. See our tutorial on Importing to Kontent.ai for a more detailed explanation.

1. Creating a content item
// Creates an instance of the Management client
var client = new ManagementClient(options);

var item = new ContentItemCreateModel
{
    Codename = "on_roasts",
    Name = "On Roasts",
    Type = Reference.ByCodename("article")
};

var responseItem = await client.CreateContentItemAsync(item);

Kontent.ai will generate an internal ID for the (new and empty) content item and include it in the response. If you do not specify a codename, it will be generated based on name. In the next step, we will add the actual (localized) content.

2. Adding language variants

To add localized content, you have to specify:

  • The content item you are importing into.
  • The language variant of the content item.
  • The language variant elements you want to add or update. Omitted elements will remain unchanged.
var componentId = "04bc8d32-97ab-431a-abaa-83102fc4c198";
var contentTypeCodename = "article";
var relatedArticle1Guid = Guid.Parse("b4e7bfaa-593c-4ae4-a231-5136b10757b8");
var relatedArticle2Guid = Guid.Parse("6d1c8ee9-76bc-474f-b09f-8a54a98f06ea");
var taxonomyTermGuid1 = Guid.Parse("5c060bf3-ed38-4c77-acfa-9868e6e2b5dd");
var taxonomyTermGuid2 = Guid.Parse("5c060bf3-ed38-4c77-acfa-9868e6e2b5dd");

// Defines the content elements to update
var stronglyTypedElements = new ArticleModel
{
    Title = new TextElement() { Value = "On Roasts" },
    PostDate = new DateTimeElement() { Value = new DateTime(2017, 7, 4) },
    BodyCopy = new RichTextElement
    {
        Value = $"<p>Rich Text</p><object type=\"application/kenticocloud\" data-type=\"component\" data-id=\"{componentId}\"></object>",
        Components = new ComponentModel[]
        {
            new ComponentModel
            {
                Id = Guid.Parse(componentId),
                Type = Reference.ByCodename(contentTypeCodename),
                Elements = new dynamic[]
                {
                    new
                    {
                        element = new
                        {
                            id = typeof(ArticleModel).GetProperty(nameof(ArticleModel.Title)).GetKontentElementId()
                        },
                        value = "Article component title",
                    }
                }
            }
        }
    },
    RelatedArticles = new LinkedItemsElement
    {
        Value = new[] { relatedArticle1Guid, relatedArticle2Guid }.Select(Reference.ById)
    },
    Personas = new TaxonomyElement
    {
        Value = new[] { taxonomyTermGuid1, taxonomyTermGuid2 }.Select(Reference.ById)
    },
    UrlPattern = new UrlSlugElement { Value = "on-roasts", Mode = "custom" },
};

// Specifies the content item and the language variant
var itemIdentifier = Reference.ByCodename("on_roasts");
var languageIdentifier = Reference.ByCodename("en-US");
var identifier = new LanguageVariantIdentifier(itemIdentifier, languageIdentifier);

// Upserts a language variant of your content item
var response = await client.UpsertLanguageVariantAsync<ArticleModel>(identifier, stronglyTypedElements);

Helper Methods

Methods for building links to content items and their elements in Kontent. Available as a separate NuGet package.

var options = new ManagementHelpersOptions
{
    ProjectId = "bb6882a0-3088-405c-a6ac-4a0da46810b0",
};

string itemId = "8ceeb2d8-9676-48ae-887d-47ccb0f54a79";
string languageCodename = "en-US";

var linkBuilder = new EditLinkBuilder(options);
var result = linkBuilder.BuildEditItemUrl(languageCodename, itemId);

// Result is "https://app.kontent.ai/goto/edit-item/project/bb6882a0-3088-405c-a6ac-4a0da46810b0/
// variant-codename/en-US/item/8ceeb2d8-9676-48ae-887d-47ccb0f54a79"
var options = new ManagementHelpersOptions
{
    ProjectId = "bb6882a0-3088-405c-a6ac-4a0da46810b0",
};

string itemId = "8ceeb2d8-9676-48ae-887d-47ccb0f54a79";
string languageCodename = "en-US";
var elementIdentifier = new ElementIdentifier(itemId, "single-Element-Codename");

var linkBuilder = new EditLinkBuilder(options);
var result = linkBuilder.BuildEditItemUrl(languageCodename, elementIdentifier);

// Result is "https://app.kontent.ai/goto/edit-item/project/bb6882a0-3088-405c-a6ac-4a0da46810b0/
// variant-codename/en-US/item/8ceeb2d8-9676-48ae-887d-47ccb0f54a79/element/single-Element-Codename"
var options = new ManagementHelpersOptions
{
    ProjectId = "bb6882a0-3088-405c-a6ac-4a0da46810b0",
};

string languageCodename = "en-US";
var elements = new ElementIdentifier[]
{
    new ElementIdentifier("76c06b74-bae9-4732-b629-1a59395e893d", "some-Element-Codename-1"),
    new ElementIdentifier("326c63aa-ae71-40b7-a6a8-56455b0b9751", "some-Element-Codename-2"),
    new ElementIdentifier("ffcd0436-8274-40ee-aaae-86fee1966fce", "some-Element-Codename-3"),
    new ElementIdentifier("d31d27cf-ddf6-4040-ab67-2f70edc0d46b", "some-Element-Codename-4"),
};

var linkBuilder = new EditLinkBuilder(options);
var result = linkBuilder.BuildEditItemUrl(languageCodename, elements);

// Result is "https://app.kontent.ai/goto/edit-item/"
//    project/bb6882a0-3088-405c-a6ac-4a0da46810b0/variant-codename/en-US/
//    item/76c06b74-bae9-4732-b629-1a59395e893d/element/some-Element-Codename-1/
//    item/326c63aa-ae71-40b7-a6a8-56455b0b9751/element/some-Element-Codename-2/
//    item/ffcd0436-8274-40ee-aaae-86fee1966fce/element/some-Element-Codename-3/
//    item/d31d27cf-ddf6-4040-ab67-2f70edc0d46b/element/some-Element-Codename-4"

This repository is configured to generate SourceLink tag in the Nuget package that allows to debug this repository source code when it is referenced as a Nuget package. Source code is downloaded directly from github to the Visual Studio.

  1. Open a solution with a project referencing the Kontent.Ai.Management Nuget package.

  2. Open Tools → Options → Debugging → General.

    • Clear Enable Just My Code.
    • Select Enable Source Link Support.
    • (Optional) Clear Require source files to exactly match the original version.
  3. Build your solution.

  4. Add a symbol server https://symbols.nuget.org/download/symbols

    • Add a symbol server in VS
  5. Run a debugging session and try to step into the Kontent.Ai.Management code.

  6. Allow Visual Studio to download the source code from GitHub.

  • SourceLink confirmation dialog

Now you are able to debug the source code of our library without needing to download the source code manually!

Further information

For more developer resources, visit the overview of .NET tools and API references at Kontent.ai Learn.

Building the sources

Prerequisites:

Required: .NET Core SDK.

Optional:

Tests

Tests can run against Live endpoint or mocked filesystem. TestUtils.TestRunType specifies target environment for tests. Commit always with TestRunType.MockFromFileSystem.

The following section is meant to be used by maintainers and people with access to the live endpoint project.

For updating mocked data use TestUtils.TestRunType.LiveEndPoint_SaveToFileSystem. When using TestRunType.MockFromFileSystem, at the build time, data from Data directory are being used as a mocked data.

Creating a new release

Feedback & Contributing

Check out the contributing page to see the best places to file issues, start discussions, and begin contributing.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
6.1.0 56 5/13/2024
6.0.0 125 4/8/2024
5.2.0 118 2/27/2024
5.1.0 267 2/5/2024
5.0.0 213 1/15/2024
4.7.0 1,265 9/13/2023
4.6.0 1,796 6/22/2023
4.5.0 959 4/6/2023
4.4.0 1,574 2/2/2023
4.3.0 8,013 1/5/2023
4.2.1-beta 130 11/25/2022
4.2.0 1,668 9/15/2022
4.1.0 70,783 8/18/2022
4.0.0 393 8/3/2022
4.0.0-beta.5 102 8/1/2022
4.0.0-beta.4 99 7/21/2022
4.0.0-beta.3 99 7/21/2022
4.0.0-beta.2 100 7/21/2022
4.0.0-beta.1 94 7/20/2022