Skip to main content

SavedView component

Overview

SavedView Component is a component that allows configuring the state of the user interface. For example, a view is a table view containing a list of visible and invisible columns, column widths, column ordering, format, etc.

Install

The component consists of the following NuGet packages:

PackageDescription
ASOL.SavedViewComponentCore package containing application logic
ASOL.SavedViewComponent.AbstractionPackage containing interfaces and abstractions
ASOL.SavedViewComponent.DomainPackage containing domain models and business logic
ASOL.SavedViewComponent.AspNetCoreASP.NET Core integration package providing endpoints and controllers
ASOL.SavedViewComponent.Persistence.MongoDBMongoDB persistence provider
ASOL.SavedViewComponent.Persistence.EFCoreEntity Framework Core persistence provider for PostgreSQL
ASOL.SavedViewComponent.ConnectorHTTP Client connector package
ASOL.SavedViewComponent.ContractsPackage containing data transfer objects and service contracts

Usage of third-party libraries

The component uses the following third-party packages:

PackageVersionLicenseDescription
Microsoft.EntityFrameworkCore8.0.0MITEntity Framework Core ORM
MongoDB.Driver2.23.1ApacheMongoDB driver for .NET

Changelog

Component registration

CoCo implementation

Package: ASOL.SavedViewComponent

To properly register the component, the following configuration values must be set:

  • AuthorizationServiceOptions:ApplicationCode
  • SsoAuth:ClientId

MongoDb

Package: ASOL.SavedViewComponent.Persistence.MongoDB

using ASOL.SavedViewComponent.Extensions;
using ASOL.SavedViewComponent.Persistence.MongoDB.Extensions;

builder.Services.AddSavedViewComponent(builder.Configuration, configurator =>
{
configurator
.UseSavedViewComplexComponent()
.UseMongoDb();
});

EF Core

Package: ASOL.SavedViewComponent.Persistence.EFCore

using ASOL.SavedViewComponent.Extensions;
using ASOL.SavedViewComponent.Persistence.EFCore.Extensions;

builder.Services.AddSavedViewComponent(builder.Configuration, configurator =>
{
configurator
.UseSavedViewComplexComponent()
.UseEfCore<DatabaseContext>();
});

Apply saved view component configuration to your database context:

using ASOL.Core.Persistence.EFCore;
using ASOL.SavedViewComponent.Persistence.EFCore.Extensions;
using Microsoft.EntityFrameworkCore;

public class DatabaseContext(
DbContextOptions<DatabaseContext> options,
IMasterDbContextConfigurationProvider configurationProvider,
IServiceProvider serviceProvider)
: MasterDbContext(
options,
configurationProvider,
serviceProvider)
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// ...
modelBuilder.ApplySavedViewComponentConfiguration();
// ...
base.OnModelCreating(modelBuilder);
}
}

Then Add Migration and Update Database.

Endpoints

By adding package ASOL.SavedViewComponent.AspNetCore the endpoints below will be automatically registered.

HTTPPathDescriptionRoute params
GET/api/v1/SavedView/target/{targetId}Get aggregated saved viewstargetId
POST/api/v1/SavedView/target/{targetId}/personalCreate a personal saved viewtargetId
PATCH/api/v1/SavedView/target/{targetId}/personal/{id}/labelUpdate label of a personal saved viewtargetId, id
PATCH/api/v1/SavedView/target/{targetId}/personal/{id}/valueUpdate value of a personal saved viewtargetId, id
PUT/api/v1/SavedView/target/{targetId}/personal/{id}Update label and value of a personal saved viewtargetId, id
GET/api/v1/SavedView/target/{targetId}/personal/{id}Get a personal saved view by idtargetId, id
DELETE/api/v1/SavedView/target/{targetId}/personal/{id}Delete a personal saved viewtargetId, id
POST/api/v1/SavedView/target/{targetId}/personal/{id}/shareShare a personal saved view (creates shared view)targetId, id
PUT/api/v1/SavedView/target/{targetId}/personal/layoutCreate or replace the personal saved views layouttargetId
PATCH/api/v1/SavedView/target/{targetId}/personal/layout/defaultviewUpdate default saved view in the personal layouttargetId
PATCH/api/v1/SavedView/target/{targetId}/personal/layout/sortingUpdate sorting of saved views in the personal layouttargetId
GET/api/v1/SavedView/target/{targetId}/personal/layoutGet the personal saved views layouttargetId
DELETE/api/v1/SavedView/target/{targetId}/personal/layoutDelete the personal saved views layouttargetId
POST/api/v1/SavedView/target/{targetId}/sharedCreate a shared saved viewtargetId
PUT/api/v1/SavedView/target/{targetId}/shared/{id}Create or replace a shared saved viewtargetId, id
PATCH/api/v1/SavedView/target/{targetId}/shared/{id}/labelUpdate label of a shared saved viewtargetId, id
PATCH/api/v1/SavedView/target/{targetId}/shared/{id}/valueUpdate value of a shared saved viewtargetId, id
GET/api/v1/SavedView/target/{targetId}/shared/{id}Get a shared saved view by idtargetId, id
DELETE/api/v1/SavedView/target/{targetId}/shared/{id}Delete a shared saved viewtargetId, id

Authorization

If the package ASOL.SavedViewComponent.AspNetCore is added to the project authorization configuration is mandatory using the UseSavedViewComplexComponentAuthorization method and implementing the SavedViewAuthorizationImporter class.

using ASOL.SavedViewComponent.Extensions;
using ASOL.SavedViewComponent.AspNetCore.Extensions;

builder.Services.AddSavedViewComponent(builder.Configuration, configurator =>
{
configurator
.UseSavedViewComplexComponent()
.UseSavedViewComplexComponentAuthorization<SavedViewAuthorizationImporter>()
// ...
});

Example of authorization importer:

using ASOL.SavedViewComponent.Authorization;

public class SavedViewAuthorizationImporter : ISavedViewAuthorizationImporter
{
public void ConfigureAuthorizationImport(ISavedViewAuthorizationImporterConfigurator configurator)
{
configurator
.AllowAccessToSavedViews("ASOL-SavedViewComponentTest-AP-.User")
.AllowSharingSavedViews("ASOL-SavedViewComponentTest-AP-.User");

// configurator.AllowSharingSavedViewsForSpecificTargetIds("ASOL-SavedViewComponentTest-AP-.User",
// "1696f874-9b60-43e0-a038-95a80524d864");
}
}
  • AllowAccessToSavedViews role that can access saved views
  • AllowSharingSavedViews role or user that can share saved views
  • AllowSharingSavedViewsForSpecificTargetIds role that can share saved views for specific target ids

Note: A regular user sees only those shared saved views that are for the given tenant context. A user with sharing rights sees all shared saved views in all tenants to which they have access to.

Connector

Package: ASOL.SavedViewComponent.Connector

using ASOL.SavedViewComponent.Connector.Extensions;

builder.Services.AddSavedViewComponentConnector(builder.Configuration);

Import/Seed Application (Global) Saved Views

For importing/seeding application (global) saved views, you need to implement the IGlobalSavedViewsImporter interface.
Use methods UpsertGlobalSavedView, SetDefaultGlobalSavedView and DeleteGlobalSavedView to import global saved views for your application.

using ASOL.SavedViewComponent.Importers;
using ASOL.SavedViewComponent.Extensions;

public class GlobalSavedViewsImporter : IGlobalSavedViewsImporter
{
public async Task ImportGlobalSavedViewsAsync(IGlobalSavedViewImporterConfigurator configurator, CancellationToken ct)
{
configurator
.UpsertGlobalSavedView(
"1696f874-9b60-43e0-a038-95a80524d864", // targetId
"9fa42dd4-0c77-4377-8819-1b3a094a72c9", // id
"imported global 1", // label
new
{
Column = "Column1",
Sort = 1,
Filter = "Filter1",
ExtendFilter = new []{"Filter1", "Filter2"},
ExtendFilter2 = new List<dynamic>{new {Filter1 = "Filter1", Filter2 = "Filter2"}}
}.ToExpando()
)
.UpsertGlobalSavedView(
"1696f874-9b60-43e0-a038-95a80524d864", // targetId
"417f5d5a-4de4-4123-b3d2-582b0334f324", // id
"imported global 2", // label
new { Column = "Column1", Sort = 2, Filter = new { Town = "Praha" }, Country = "Czech Republic" }.ToExpando()
)
.SetDefaultGlobalSavedView("1696f874-9b60-43e0-a038-95a80524d864", "417f5d5a-4de4-4123-b3d2-582b0334f324")
.DeleteGlobalSavedView("1696f874-9b60-43e0-a038-95a80524d864", "3a888ab4-b929-45a8-8218-b3225b4b613d");

await configurator.Import(ct);
}
}

Then you need to register your importer:

MongoDb

using ASOL.SavedViewComponent.Extensions;
using ASOL.SavedViewComponent.Persistence.MongoDB.Extensions;

builder.Services.AddSavedViewComponent(builder.Configuration, configurator =>
{
configurator
.UseSavedViewComplexComponent()
.UseGlobalSavedViewsImporter<GlobalSavedViewsImporter>()
.UseMongoDb();
});

EF Core

using ASOL.SavedViewComponent.Extensions;
using ASOL.SavedViewComponent.Persistence.EFCore.Extensions;

builder.Services.AddSavedViewComponent(builder.Configuration, configurator =>
{
configurator
.UseSavedViewComplexComponent()
.UseGlobalSavedViewsImporter<GlobalSavedViewsImporter>()
.UseEfCore<DatabaseContext>();
});

Apply saved view component configuration to your database context:

using ASOL.Core.Persistence.EFCore;
using ASOL.SavedViewComponent.Persistence.EFCore.Extensions;
using Microsoft.EntityFrameworkCore;

public class DatabaseContext(
DbContextOptions<DatabaseContext> options,
IMasterDbContextConfigurationProvider configurationProvider,
IServiceProvider serviceProvider)
: MasterDbContext(
options,
configurationProvider,
serviceProvider)
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// ...
modelBuilder.ApplySavedViewComponentConfiguration();
// ...
base.OnModelCreating(modelBuilder);
}
}

Then Add Migration and Update Database.

CoCoS implementation

Packages:

  • ASOL.SavedViewComponent
  • ASOL.SavedViewComponent.AspNetCore

MongoDb

Package: ASOL.SavedViewComponent.Persistence.MongoDB

using ASOL.SavedViewComponent.Extensions;
using ASOL.SavedViewComponent.AspNetCore.Extensions;
using ASOL.SavedViewComponent.Persistence.MongoDB.Extensions;

builder.Services.AddSavedViewComponent(builder.Configuration, configurator =>
{
configurator
.UseSavedViewComplexComponentService()
.UseMongoDb();
});

EF Core

Package: ASOL.SavedViewComponent.Persistence.EFCore

using ASOL.SavedViewComponent.Extensions;
using ASOL.SavedViewComponent.AspNetCore.Extensions;
using ASOL.SavedViewComponent.Persistence.EFCore.Extensions;

builder.Services.AddSavedViewComponent(builder.Configuration, configurator =>
{
configurator
.UseSavedViewComplexComponentService()
.UseEfCore<DatabaseContext>();
});

Authorization

Authorization configuration is mandatory using the UseSavedViewComplexComponentAuthorization method and implementing the SavedViewAuthorizationImporter class.

using ASOL.SavedViewComponent.Extensions;
using ASOL.SavedViewComponent.AspNetCore.Extensions;

builder.Services.AddSavedViewComponent(builder.Configuration, configurator =>
{
configurator
.UseSavedViewComplexComponentService()
.UseSavedViewComplexComponentServiceAuthorization<SavedViewAuthorizationImporter>()
// ...
});

Example of authorization importer:

using ASOL.SavedViewComponent.Authorization;

public class SavedViewAuthorizationImporter : ISavedViewAuthorizationImporter
{
public void ConfigureAuthorizationImport(ISavedViewAuthorizationImporterConfigurator configurator)
{
configurator
.AllowAccessToSavedViews("ASOL-SavedViewComponentTest-AP-.User")
.AllowSharingSavedViews("ASOL-SavedViewComponentTest-AP-.User");

// configurator.AllowSharingSavedViewsForSpecificTargetIds("ASOL-SavedViewComponentTest-AP-.User",
// "1696f874-9b60-43e0-a038-95a80524d864");
}
}
  • AllowAccessToSavedViews role that can access saved views
  • AllowSharingSavedViews role or user that can share saved views
  • AllowSharingSavedViewsForSpecificTargetIds role that can share saved views for specific target ids

Note: A regular user sees only those shared saved views that are for the given tenant context. A user with sharing rights sees all shared saved views in all tenants to which they have access to.

Example of authorization importer:

using ASOL.SavedViewComponent.Authorization;

public class SavedViewAuthorizationImporter : ISavedViewAuthorizationImporter
{
public void ConfigureAuthorizationImport(ISavedViewAuthorizationImporterConfigurator configurator)
{
configurator
.AllowAccessToSavedViews("ASOL-SavedViewComponentTest-AP-.User")
.AllowSharingSavedViews("ASOL-SavedViewComponentTest-AP-.User");

// configurator.AllowSharingSavedViewsForSpecificTargetIds("ASOL-SavedViewComponentTest-AP-.User",
// "1696f874-9b60-43e0-a038-95a80524d864");
}
}

Endpoints

The endpoints listed here will be automatically registered.

HTTPPathDescriptionRoute params
GET/api/v1/SavedView/application/{applicationCode}/target/{targetId}Get aggregated saved viewstargetId
POST/api/v1/SavedView/application/{applicationCode}/target/{targetId}/personalCreate a personal saved viewtargetId
PATCH/api/v1/SavedView/application/{applicationCode}/target/{targetId}/personal/{id}/labelUpdate label of a personal saved viewtargetId, id
PATCH/api/v1/SavedView/application/{applicationCode}/target/{targetId}/personal/{id}/valueUpdate value of a personal saved viewtargetId, id
PUT/api/v1/SavedView/application/{applicationCode}/target/{targetId}/personal/{id}Create or replace a personal saved viewtargetId, id
GET/api/v1/SavedView/application/{applicationCode}/target/{targetId}/personal/{id}Get a personal saved view by idtargetId, id
DELETE/api/v1/SavedView/application/{applicationCode}/target/{targetId}/personal/{id}Delete a personal saved viewtargetId, id
POST/api/v1/SavedView/application/{applicationCode}/target/{targetId}/personal/{id}/shareShare a personal saved view (creates shared view)targetId, id
PUT/api/v1/SavedView/application/{applicationCode}/target/{targetId}/personal/layoutCreate or replace the personal saved views layouttargetId
PATCH/api/v1/SavedView/application/{applicationCode}/target/{targetId}/personal/layout/defaultviewUpdate default saved view in the personal layouttargetId
PATCH/api/v1/SavedView/application/{applicationCode}/target/{targetId}/personal/layout/sortingUpdate sorting of saved views in the personal layouttargetId
GET/api/v1/SavedView/application/{applicationCode}/target/{targetId}/personal/layoutGet the personal saved views layouttargetId
DELETE/api/v1/SavedView/application/{applicationCode}/target/{targetId}/personal/layoutDelete the personal saved views layouttargetId
POST/api/v1/SavedView/application/{applicationCode}/target/{targetId}/sharedCreate a shared saved viewtargetId
PUT/api/v1/SavedView/application/{applicationCode}/target/{targetId}/shared/{id}Create or replace a shared saved viewtargetId, id
PATCH/api/v1/SavedView/application/{applicationCode}/target/{targetId}/shared/{id}/labelUpdate label of a shared saved viewtargetId, id
PATCH/api/v1/SavedView/application/{applicationCode}/target/{targetId}/shared/{id}/valueUpdate value of a shared saved viewtargetId, id
GET/api/v1/SavedView/application/{applicationCode}/target/{targetId}/shared/{id}Get a shared saved view by idtargetId, id
DELETE/api/v1/SavedView/application/{applicationCode}/target/{targetId}/shared/{id}Delete a shared saved viewtargetId, id

Connector

Package: ASOL.SavedViewComponent.Connector

To properly register the component, the following configuration values must be set:

  • AuthorizationServiceOptions:ApplicationCode
using ASOL.SavedViewComponent.Connector.Extensions;

builder.Services.AddSavedViewComponentConnector(builder.Configuration, configurator =>
{
configurator
.UseSavedViewComplexComponentConnector();
});

Import/Seed Application (Global) Saved Views

Package: ASOL.SavedViewComponent.Connector

For importing/seeding application (global) saved views, you need to implement the IGlobalSavedViewsImporter interface.
Use methods UpsertGlobalSavedView, SetDefaultGlobalSavedView and DeleteGlobalSavedView to import global saved views for your application.

using ASOL.SavedViewComponent.Importers;
using ASOL.SavedViewComponent.Extensions;

public class GlobalSavedViewsImporter : IGlobalSavedViewsImporter
{
public async Task ImportGlobalSavedViewsAsync(IGlobalSavedViewImporterConfigurator configurator, CancellationToken ct)
{
configurator
.UpsertGlobalSavedView(
"1696f874-9b60-43e0-a038-95a80524d864", // targeId
"9fa42dd4-0c77-4377-8819-1b3a094a72c9", // id
"imported global 1", // label
new
{
Column = "Column1",
Sort = 1,
Filter = "Filter1",
ExtendFilter = new[] { "Filter1", "Filter2" },
ExtendFilter2 = new List<dynamic> { new { Filter1 = "Filter1", Filter2 = "Filter2" } }
}.ToExpando()
)
.UpsertGlobalSavedView(
"1696f874-9b60-43e0-a038-95a80524d864", // targetId
"417f5d5a-4de4-4123-b3d2-582b0334f324", // id
"imported global 2", // label
new { Column = "Column1", Sort = 2, Filter = new { Town = "Praha" }, Country = "Czech Republic" }.ToExpando()
)
.SetDefaultGlobalSavedView("1696f874-9b60-43e0-a038-95a80524d864", "417f5d5a-4de4-4123-b3d2-582b0334f324")
.DeleteGlobalSavedView("1696f874-9b60-43e0-a038-95a80524d864", "3a888ab4-b929-45a8-8218-b3225b4b613d");

await configurator.Import(ct);
}
}

The next step is implementation of a hosted service. Example:

using System.Diagnostics;
using ASOL.Core.Identity;
using ASOL.Core.Identity.Contexts;
using ASOL.Core.Identity.Options;
using ASOL.SavedViewComponent.Importers;
using Microsoft.Extensions.Options;

public class GlobalSavedViewConnectorService(IRuntimeContextScope runtimeContextScope, IOptions<SsoAuthOptions> ssoAuthOptions) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
ArgumentNullException.ThrowIfNull(ssoAuthOptions);

var claimsPrincipal = ClaimsPrincipalBuilder
.CreateClientPrincipal(ssoAuthOptions.Value.ClientId!)
.AddAuthenticateType()
.AddLocale(LocalizationContext.DefaultLanguageCode)
.Build();

runtimeContextScope.LocalImpersonate(claimsPrincipal, ActivityTraceId.CreateRandom().ToString());
var globalSavedViewImporter = runtimeContextScope.ScopeProvider.GetRequiredService<IGlobalSavedViewsImporter>();
var globalSavedViewImporterConfigurator = runtimeContextScope.ScopeProvider.GetRequiredService<IGlobalSavedViewImporterConfigurator>();
await globalSavedViewImporter.ImportGlobalSavedViewsAsync(globalSavedViewImporterConfigurator, stoppingToken);
}
}

Finally, you need to register your importer and hosted service:

using ASOL.SavedViewComponent.Connector.Extensions;

builder.Services.AddSavedViewComponentConnector(builder.Configuration, configurator =>
{
configurator
.UseSavedViewComplexComponentConnector()
.UseGlobalSavedViewsImporter<GlobalSavedViewsImporter, GlobalSavedViewConnectorService>();
});

Complete list of endpoints

[BASE] is a route prefix depending on the type of the component registration

HTTPPathControllerActionDescriptionVisibility
GET[BASE]/SavedViewControllerGetGet aggregated saved viewsPublic
POST[BASE]/personalSavedViewControllerCreatePersonalSavedViewCreate a personal saved viewPublic
PATCH[BASE]/personal/{id}/labelSavedViewControllerUpdatePersonalSavedViewLabelUpdate label of a personal saved viewPublic
PATCH[BASE]/personal/{id}/valueSavedViewControllerUpdatePersonalSavedViewValueUpdate value of a personal saved viewPublic
PUT[BASE]/personal/{id}SavedViewControllerUpdatePersonalSavedViewCreate or replace a personal saved viewPublic
GET[BASE]/personal/{id}SavedViewControllerGetPersonalSavedViewByIdGet a personal saved view by idPublic
DELETE[BASE]/personal/{id}SavedViewControllerDeletePersonalSavedViewDelete a personal saved viewPublic
PUT[BASE]/personal/layoutSavedViewControllerCreatePersonalSavedViewsLayoutCreate or replace the personal saved views layoutPublic
PATCH[BASE]/personal/layout/defaultviewSavedViewControllerUpdatePersonalSavedViewsLayoutDefaultViewUpdate default saved view in the personal layoutPublic
PATCH[BASE]/personal/layout/sortingSavedViewControllerUpdatePersonalSavedViewsLayoutSortingUpdate sorting of saved views in the personal layoutPublic
GET[BASE]/personal/layoutSavedViewControllerGetPersonalSavedViewsLayoutGet the personal saved views layoutPublic
DELETE[BASE]/personal/layoutSavedViewControllerDeletePersonalSavedViewsLayoutDelete the personal saved views layoutPublic
POST[BASE]/globalSavedViewControllerCreateGlobalSavedViewCreate a global saved viewHidden
PATCH[BASE]/global/{id}/defaultSavedViewControllerSetGlobalSavedViewAsDefaultSet a global saved view as defaultHidden
GET[BASE]/global/{id}SavedViewControllerGetGlobalSavedViewByIdGet a global saved view by idHidden
PATCH[BASE]/global/{id}/labelSavedViewControllerUpdateGlobalSavedViewLabelUpdate label of a global saved viewHidden
PATCH[BASE]/global/{id}/valueSavedViewControllerUpdateGlobalSavedViewValueUpdate value of a global saved viewHidden
DELETE[BASE]/global/{id}SavedViewControllerDeleteGlobalSavedViewDelete a global saved viewHidden

Deleted saved views have a retention period of 31 days. After this period, they are permanently removed from the database.

Useful examples of requests

Here are some useful examples of http requests.

POST http://localhost:5123/api/v1/SavedView/application/ASOL-SavedViewComponentTest-AP-/target/1696f874-9b60-43e0-a038-95a80524d864/personal
Content-Type: application/json

{
"label": "label-1",
"value": {
"column1": "test",
"sort": "column1",
"allowValues": [
"a",
"b"
],
"filters": [
{
"f1": 1,
"f3": 5
}
],
"value": {
"column1": "test",
"sort": "column1"
}
}
}
PUT http://localhost:5123/api/v1/SavedView/application/ASOL-SavedViewComponentTest-AP-/target/1696f874-9b60-43e0-a038-95a80524d864/personal/layout
Content-Type: application/json; charset=utf-8

{
"defaultSavedViewId": "9011b5df-e917-4d9b-96d7-124975074def",
"sortedSavedViewIds": [
"9011b5df-e917-4d9b-96d7-124975074def",
"417f5d5a-4de4-4123-b3d2-582b0334f324"
]
}