Giter Site home page Giter Site logo

kontent-ai / delivery-sdk-net Goto Github PK

View Code? Open in Web Editor NEW
32.0 22.0 41.0 3.3 MB

Kontent.ai Delivery .NET SDK

Home Page: https://www.nuget.org/packages/Kontent.Ai.Delivery

License: MIT License

C# 100.00%
sdk nuget dotnet headless-cms delivery-api hacktoberfest kontent-ai dotnet6 kontent-ai-tool

delivery-sdk-net's Introduction

Kontent.ai Delivery .NET SDK

Build & Test codecov Stack Overflow Discord

Paradigm Package Downloads Compatibility Documentation
Async NuGet NuGet net8.0 ๐Ÿ“– Wiki
Reactive NuGet NuGet net8.0 ๐Ÿ“– Wiki

Summary

The Kontent.ai Delivery .NET SDK is a client library that lets you easily retrieve content from Kontent.ai.

Getting started

Installation via Package Manager Console in Visual Studio:

PM> Install-Package Kontent.Ai.Delivery

Installation via .NET CLI:

> dotnet add <TARGET PROJECT> package Kontent.Ai.Delivery

Usage

To retrieve content from your Kontent.ai environments, you'll be using an implementation of the IDeliveryClient interface. This is the main interface of the SDK. Here's how you can instantiate and use the Delivery client either with DI/IoC or without DI/IoC.

Use dependency injection (ideal for ASP.NET Core web apps)

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
	services.AddDeliveryClient(Configuration);
}

HomeController.cs

public class HomeController
{
	private IDeliveryClient _client;

	public HomeController(IDeliveryClient deliveryClient)
	{
		_client = deliveryClient;
	}
}

In this case, the SDK reads the configuration from the DeliveryOptions section of the Configuration object. There are many ways of providing the configuration to the DeliveryClient as well as many advanced registration scenarios which you can all find in the Wiki.

To see a complete working example, go to one of our sample apps:

To spin up a fully configured blank site quickly, use the:

Usage without IoC/DI containers (ideal for console apps, unit tests...)

You can also set up a DeliveryOptions manually using the DeliveryClientBuilder.

IDeliveryClient _client = DeliveryClientBuilder
    .WithOptions(builder => builder
        .WithEnvironmentId("<YOUR_ENVIRONMENT_ID>")
	.UseProductionApi()
	.Build())
    .Build();

Your first request

Use the .NET code generator to generate POCO models:

public class Article
{
        public string Title { get; set; }
        public string Summary { get; set; }
	public string Body { get; set; }
	public DateTime? PostDate { get; set; }
	public ContentItemSystemAttributes System { get; set; }
}

Call the IDeliveryClient to retrieve data from Kontent:

// Retrieving a single content item
var response = await _client.GetItemAsync<Article>("<article_codename>");
var title = response.Item.Title; // -> "On Roasts"
var lang = response.Item.System.Language; // -> "en-US"

See Working with Strongly Typed Models to learn how to generate models and adjust the logic to your needs.

Further information

For more developer resources, visit:

Get involved

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

delivery-sdk-net's People

Contributors

alesk-kentico avatar arguit avatar aweigold avatar chariths avatar dzmitryk-kontent-ai avatar enngage avatar hejtmii avatar huluvu21 avatar jancerman avatar juraju-kentico avatar kentico-martins2 avatar kgebarowski avatar martin-knapik avatar matus12 avatar mirokentico avatar msenczak avatar ondrejsevcik avatar petrpyszko avatar petrsvihlik avatar petrsvirak avatar pokornyd avatar richardkalinec avatar romano-kontent avatar sevitas avatar simply007 avatar suzii avatar tobiaskamenicky avatar tomasjurasek avatar winklertomas avatar zdenekjurka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

delivery-sdk-net's Issues

In the Pagination when retrieving lists of content items it should give either total available items or total available pages

If I want to implement traditional pagination on my site/app I need to be able to find out how many total items there are available in the result set without paging, but only retrieve the current page's items.

The Pagination object seems to be the right place for this but it doesn't seem to work like this, instead only returning the number of items currently retrieved which is the single page worth.

Unit tests vs. integration tests?

Do we want/need to expand the testing to unit tests? The current tests look like integration tests to me as they rely on at least two different projects (which could get modified) and requires the deliver endpoint to be accessible and available.

Let the SDK bypass the CDN for particular requests

Motivation

Currently, the DeliveryClient can either be configured to utilize or circumvent the CDN of Kentico Kontent, as a whole. One of the pitfalls of this is when an app clears its cached Kentico Kontent data and needs to fetch the most-up-to-date content in the very subsequent Delivery request.

Design guidelines

Allow the SDK to sense whether it should use the X-KC-Wait-For-Loading-New-Content header for each particular request.

Based on the signal, the SDK should either use the header or not.

The logic that decides whether to send the signal, it should be outside the SDK (eg. in our boilerplate project).

If no signal is sent explicitly, the SDK should not use the header (ie. not bypass the CDN).

Spike: Context-aware caching

Expected outcome

Originally, the CachedDeliveryClient had stored data in memory cache for a fixed period of time. As the boilerplate now purges cache entries via web hook push notifications, the timeout defined in settings is no longer important. The CacheTimeoutSeconds appsettings.json value is therefore set to a generous value of 24 hours. Still, the situation is not ideal. The app may appear in a memory pressure condition where the MemoryCache logic may start an uncontrolled cache eviction process.

Find ways of setting the level of importance of various sorts of data in cache. This value should then be used by the framework code to prioritize the cache entries for the purpose of eviction.

Also, identify factors that most often affect the levels of importance. If such factors stem from the state of the app and could be computed, design an implementation (in a high-level fashion).

Reference

Pulling strongly typed content item without it's modular content throws an exception.

2017-03-17 16_19_18-kenticocloud delivery - microsoft visual studio administrator

Problem is most likely on this line https://github.com/Kentico/delivery-sdk-net/blob/master/KenticoCloud.Delivery/CodeFirst/CodeFirstModelProvider.cs#L116

Failing test

[Test]
public void FailingTest()
{
	var client = new DeliveryClient("975bf280-fd91-488c-994c-2f04416e5ee3");
	client.CodeFirstModelProvider.TypeProvider = new CustomTypeProvider();

	// Returns on_roasts content item with related_articles modular element to two other articles.
	// on_roasts
	// |- coffee_processing_techniques
	// |- origins_of_arabica_bourbon
	//   |- on_roasts
	var onRoastsItem = Task.Run(() => client.GetItemAsync<Article>("on_roasts", new DepthParameter(1))).Result.Item;

	Assert.AreEqual(2, onRoastsItem.RelatedArticles.Count());
	Assert.AreEqual(0, ((Article)onRoastsItem.RelatedArticles.First()).RelatedArticles.Count());
	Assert.AreEqual(0, ((Article)onRoastsItem.RelatedArticles.ElementAt(1)).RelatedArticles.Count());
}

public class CustomTypeProvider : ICodeFirstTypeProvider
{
	public Type GetType(string contentType)
	{
		switch (contentType)
		{
			case "article":
				return typeof(Article);
			default:
				return null;
		}
	}
}

public partial class Article
{
	public string Title { get; set; }
	public IEnumerable<object> RelatedArticles { get; set; }
}

Add support for secured access to published (production) content

In coming weeks, the Delivery API will start supporting authenticated access.

In the first version of this feature, content will be secured using an API key. The developer will be able to turn the authenticated access on and off for the whole project. There will be one authentication key for the project, not multiple ones (for various user roles, for example).

Implement support for submitting the key in the form of the "Authorization" request header. The key should be sent via the Bearer scheme (with a "Bearer " prefix).

We'll provide more information once they become available. https://kenticocloud.com/roadmap#secured-delivery-api

Add language variant filtering support to the LanguageParameter

Motivation

Currently, the LanguageParameter does not support filtering based on language variants.

var response = await DeliveryClient.GetItemsAsync<Article>(new LanguageParameter("en-US"));

It would be nice if it was possible to ignore language fallbacks as described in the documentation:
image

This means that the LanguageParameter needs to be enriched with filtering based on system.language on top of the currently present language.

Design guidelines

Let's create two new constructors:

// This one will cause the system.language to be set instead of language
public LanguageParameter(string language, bool ignoreFallbacks = false)

// This one will set both language and system.language
public LanguageParameter(string language, string languageVariant = null)

The GetQueryStringParameter() method will need to be adjusted to return one or two query parameters in a single string.

  • Don't forget to add unit tests

CI / CD improvements

  • Set up Appveyor for build
  • Enable tests in AppVeyor (wating for #4)
  • Set up branch protection for pull requests
  • Set up automatic artifacts generation
  • Set up version incrementation for artifacts
  • Set up automatic artifacts publishing from master to NuGet

Depth=0 vs Depth=1 & modular content

While requesting our blog post content items with depth=0 and selecting also top-level modular content author i get this response:
depth-bug
You can see, that the "author" modular content is present in the json response. However, when i use
item.GetModularContent("author"), null is returned.
When setting depth=1 , the same exact json response is returned; however, item.GetModularContent("author") returns expected values.
I am really confused, is this really the intended behavior? I thought the depth parameter should limit the amount of data I'm gonna receive (also, i can easily avoid the SDK and get my author data from the response directly).

Spike: Support interfaces as generic parameters of collections

Motivation

In strongly typed models, this is supported:

IEnumerable<MyType> CollectionOfMyTypes { get; set; }

and this is not:

IEnumerable<IMyType> CollectionOfMyInterfaces { get; set; }

This applies both to externally defined types and types defined within the SDK (Asset, TaxonomyTerm).

We should give developers the freedom to unwind from specific classes and work with Kontent data through interfaces. For example, defining shadow properties wouldn't be necessary anymore.

Expected spike outcome

Propose a way of defining translations between interfaces and concrete types.

These translations would be used in CodeFirstModelProvider wherever Activator.CreateInstance(...); is called.

There would be default translations for Asset, TaxonomyTerm and other types defined in the SDK to maintain backwards compatibility.

As for how we could allow the CodeFirstModelProvider to translate interfaces to objects, maybe we can leverage the good old IoC pattern. A brief spike revealed that 3 out of 4 popular IoC containers for .NET Core use the IServiceCollection abstraction to work with the services and their implementations.

Namely:

  • Lamar's ServiceRegistry class implements IServiceCollection.
  • AutoFac works directly with ServiceCollection, which is a default implementation of IServiceCollection.
  • And of course, the default Microsoft.Extensions.DependencyInjection work with IServiceCollections.

The obvious way how the CodeFirstModelProvider would obtain the IServiceCollection would be through a DeliveryClient constructor (in ASP.NET Core apps, in Startup.cs).

Reported by: @kenticomartinh and @JanLenoch

Spike: Simplify IDeliveryClient and get rid of its internal dependencies/references

Motivation

  • IDeliveryClient contains members such as IContentLinkUrlResolver, ICodeFirstModelProvider, or IInlineContentItemsProcessor which shouldn't be part of the contract at all. Currently, they're there just because of internal referencing of the DeliveryClient in the underlying code.
  • The wrong code can be easily identified:
    • All references to this in the DeliveryClient (and references to IDeliveryClient in the underlying code
      • such as _contentLinkResolver = new ContentLinkResolver(_client.ContentLinkUrlResolver); in the ContentItem.
      • or _client.CodeFirstModelProvider.GetContentItemModel<T>(source, _response["modular_content"])) in DeliveryItemListingResponse<T>

Expected spike outcome

We should come up with a way to get rid of these dependencies and simplify the IDeliveryClient contract.

Things to consider:

  • we could utilize DI, however we'd like to avoid using any particular DI framework
    • we don't want to push consumers of the library to use a specific DI framework
    • we might want to introduce some simple DI of our own making, crafted just to fit our needs
      • there would be a default implementation of the container with a possibility to replace the logic
      • it should also be possible to inject a DI framework of user's choice to take care of the instantiation completely
        • is there some kind of generic DI interface that we could expose for replacement?
    • we should keep in mind that the user can create multiple instances of the DeliveryClient in a single application. So a simple singleton might not be the right thing for us.
  • another approach would be to refactor the logic in a way that no DI is needed.
    • in a way that all the complex stuff happens outside of the models/response objects and the models/responses are just dummy container objects

CodeFirstModelProvider shouldn't set null for collections

It should be consistent with ContentItem.

With ContentItem.GetModularContent("related_articles") I get an empty collection if there is no modular content. With custom Strongly Typed model, I get a null value.

It forces me to use null check in my MVC views

foreach(var article in Model.GetModularContent("related_articles"))
{ ... }

// vs.

if (Model.RelatedArticles != null) 
{
    foreach(var article in Model.RelatedArticles)) { ... }
}

IMO it's safer to return an empty collection. Developers won't see so many exceptions on their websites ๐Ÿ™‚

Allow excluding properties from (de)serialization

This comes in handy in situations such as:

public IEnumerable<object> X { get; set; } // element name x exists in kentico cloud
public IEnumerable<Product> Y { get { return X.Cast<Product>(); } set { X = value; } } // element name y does not exist in KC

where one property would overwrite the other. (Typical when extending models via partial classes.)
Warning: order of properties in a code file is significant!

Support for ReactiveX programming

Are there any plans to support ReactiveX and their Rx.NET extension?

I feel like this could be a nice addition to the SDK as it would make working with (especially multiple) asynchronous operations much easier. Especially it would help with cancelling, chaining, retrying and error handling of the http requests that are made to Kentico Cloud.

The highest benefit would receive UI heavy applications (i.e. desktop apps), but it would be useful for web as well.

Support of 'Modular Content in Rich Text Elements'

With the introduction of that feature, the SDK should scan for tags inside of Rich text elements to build more structured objects using the runtime typing functionality. That way, the included Modular content items would get recognized and rendered by frameworks like MVC.

Improve pagination in the DeliveryItemListingResponse

As part of the DeliveryItemListingResponse object, the Pagination.NextPageUrl property provides a reference to getting the next page data.

The GetItemsAsync method should have an overload to be able to get the next page data, either using the NextPageUrl, or something more appropriate.

Set up code coverage

Generating coverage:

Examples for .NET Standard libraries:

Generic filters

Current situation:
When I want to use e.g. GreaterThanFilter with DateTime I have to know how to format the DateTime so that the API accepts it.

Proposed solution:
We should solve this in a generic way. The filters should accept <T> as a value and handle the formatting internally based on the type.

  • public abstract class Filter (the base class for filters) should be switched into a generic class
  • it should have an extra constructor accepting <T> and storing it in a public property
  • getter of public string Value should be modified to take the type into consideration
    • for DateTime it should format it to the ISO 8601 format (accepted by the delivery API)
    • for the rest it should call just ToString()
  • adjust filters inheriting from Filter correspondingly (where it makes sense)
  • add unit tests
  • adjust the readme if necessary
  • all changes should preserve backward compatibility

Suggestions for any better approaches appreciated!

Use IDeliveryClient wherever possible

Places like internal DeliveryItemResponse(JToken response, DeliveryClient client) should use IDeliveryClient.

The concrete implementation - DeliveryClient - shouldn't be used in the SDK at all.

Response objects should contain data for easier debugging

It's usual that the response objects contain data for easier debugging.

In our case, we should include:

  • API URL (URL constructed by DeliveryEndpointUrlBuilder)
  • Raw data (JSON string)
    to make debugging easier.

The remaining questions is - should we make it private and fill it only #if DEBUG or should it be available all the times via public properties?

Get rid of build-time conditions

Convert this:

 #if (DEBUG && NET45)
        private string PRODUCTION_ENDPOINT = ConfigurationManager.AppSettings["ProductionEndpoint"] ?? "https://deliver.kenticocloud.com/{0}";
        private string PREVIEW_ENDPOINT = ConfigurationManager.AppSettings["PreviewEndpoint"] ?? "https://preview-deliver.kenticocloud.com/{0}";
        #else
        private const string PRODUCTION_ENDPOINT = "https://deliver.kenticocloud.com/{0}";
        private const string PREVIEW_ENDPOINT = "https://preview-deliver.kenticocloud.com/{0}";
        #endif

in DeliveryEndpointUrlBuilder to use ASP.NET Core Configuration.

Custom class that can read appSettings from web.config needs to be implemented.

IQueryable<T> LINQ Provider

Current situation:

var parameters = new IQueryParameter[]
            {
                new AllFilter("elements.personas", "barista", "coffee", "blogger"),
                new AnyFilter("elements.personas", "barista", "coffee", "blogger"),
                new ContainsFilter("system.sitemap_locations", "cafes"),
                new EqualsFilter("elements.product_name", "Hario V60"),
                new GreaterThanFilter("elements.price", "1000"),
                new GreaterThanOrEqualFilter("elements.price", "50"),
                new InFilter("system.type", "cafe", "coffee"),
                new LessThanFilter("elements.price", "10"),
                new LessThanOrEqualFilter("elements.price", "4"),
                new RangeFilter("elements.country", "Guatemala", "Nicaragua"),
                new DepthParameter(2),
                new ElementsParameter("price", "product_name"),
                new LimitParameter(10),
                new OrderParameter("elements.price", SortOrder.Descending),
                new SkipParameter(2)
            };
client.GetItemsAsync(parameters)

Ideal situation:

var items = client.GetItems<Article>().Where(a=>a.Name.Contains("xyz").Skip(2)....;

Resources:

Let the DeliveryClient be initialized by passing a configuration section

We should let the DeliveryClient (and its subclasses) read from a configuration object . The consumer would have the option of passing a configuration object and not having to provide projectid and preview api key explicitly through the constructor.

This story should lead to a better support of DI scenarios, better separation of concerns (e.g. DeliveryEndpointUrlBuilder should not know anything about where its settings come from) and ability to control settings on an application level.

Suggested changes:

  • introduce a new class DeliveryOptions with 4 properties (ProductionEndpoint, PreviewEndpoint, ProjectId, PreviewApiKey) - the first two will have their default values set (similarly to MyOptions)

  • DeliveryEndpointUrlBuilder

    • will have a new constructor accepting IOptions<DeliveryOptions> options
    • will internally use the IOptions<DeliveryOptions> object
    • the old constructors should construct the IOptions<DeliveryOptions> object as well
    • DeliveryClient will use the new constructor with IOptions<DeliveryOptions> object to construct the DeliveryEndpointUrlBuilder
    • the constructors should be chained, if possible (:this(...))
    • it will no longer be possible to override ProductionEndpoint and PreviewEndpoint when using the old constructors (shouldn't be a breaking change for anyone)...(it will still be possible by using the new constructor)
  • DeliveryClient

    • will have a new constructor accepting IOptions<DeliveryOptions> options
    • will internally use the IOptions<DeliveryOptions> object
    • the old constructors should construct the IOptions<DeliveryOptions> object
  • the NET45ConfigurationProvider (#57)

    • will be converted to a class that returns an IOptions<DeliveryOptions> object ( IOptions<DeliveryOptions> a = new OptionsManager<DeliveryOptions>(new List<IConfigureOptions<DeliveryOptions>>()); return a;)
    • this is a breaking change for Kentico who'll have to call this method prior instantiating the DeliveryClient using the new constructor (only in NET45)
  • It may be possible to avoid the IOptions wrapper.

  • Follow up story: #59
    Resources: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration

Description not initialized

If I do this:

        var item= (await client.GetItemAsync("codename")).Item;

        var model = item.CastTo<Article>();

the Article.TeaserImage[0].Description is not initialized.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.