Sharing Levels integration
These are the steps to integrate the Sharing Levels
clients into our services.
Once we finish with the integration all the domain entities being persisted will
be intercepted applying the sharings algorithm and the corresponding Sharing
Level will be persisted within these entities.
If you want to know more about Sharings
, take a look at:
Backend-for-frontend
Note
This step can be skipped if you are working in AdminCenter
,
since its already implemented.
Add the following reference to your BFF application project:
XML |
---|
| <ProjectReference Include="$(ServicesPath)SharingLevels\ITsynch.Suite.SharingLevels.BffClientModule\ITsynch.Suite.SharingLevels.BffClientModule.csproj" />
|
And add the dependency in the application module.
C# |
---|
| using ITsynch.Suite.SharingLevels;
public override void SetupModule(IModuleBuilder builder)
{
base.SetupModule(builder);
builder.DependsOn<SharingLevelsBffClientModule>();
}
|
This module propagates the Sharing Levels
header that we are receiving from
the UI to backend services accessed via GraphQL federations.
Backend service
Add the following reference to your application project:
XML |
---|
| <ProjectReference Include="$(ServicesPath)SharingLevels/ITsynch.Suite.SharingLevels.ClientModule/ITsynch.Suite.SharingLevels.ClientModule.csproj" />
|
Add the dependency in the application module and configure the SharingMode of
all the domain entities.
C# |
---|
| public override void SetupModule(IModuleBuilder builder)
{
base.SetupModule(builder);
builder.DependsOn<SharingLevelsClientModule, SharingLevelsClientModuleOptions>(
opts =>
{
opts.SetDbContext<EntityDbContext>();
opts.ConfigureEntity<Entity>(SharingMode.ByEntity);
opts.ConfigureEntity<AnotherEntity>(SharingMode.ByEntity);
});
}
|
Important
This sharing entity configuration only works if the entity is described.
Otherwise, it will throw an exception.
There are some cases where our entities are tightly related and it makes sense
that they would follow the same sharing configuration. A common use case are
aggregate roots, where the root entity would be our main entity being
configured, and it should be "followed" by its children.
The client module provides a specific API to accomplish this.
C# |
---|
| public override void SetupModule(IModuleBuilder builder)
{
base.SetupModule(builder);
builder.DependsOn<SharingLevelsClientModule, SharingLevelsClientModuleOptions>(
opts =>
{
opts.SetDbContext<EntityDbContext>();
opts.ConfigureEntity<Entity, RelatedEntity>(SharingMode.ByEntity);
});
}
|
Both entities will share the same configuration, but it is the main entity who
"guides" it's followers (you can add more than one entity following a principal
one).
Now, moving to the Domain library we need to add the reference of
Abstractions
:
XML |
---|
| <ProjectReference Include="$(ServicesPath)SharingLevels/ITsynch.Suite.SharingLevels.Abstractions/ITsynch.Suite.SharingLevels.Abstractions.csproj" />
|
And implement the IShareableEntity
interface to our domain entity:
C# |
---|
| using ITsynch.Suite.SharingLevels;
namespace ITsynch.Suite.Entity.Domain
{
public class Entity : IShareableEntity
{
/// <inheritdoc />
public Guid SharingLevelId { get; set; }
}
}
|
Also, add the same property to the following:
GraphQL Entity
C# |
---|
| namespace ITsynch.Suite.Entity.Application.GraphQL
{
public record Entity
{
public Guid SharingLevelId { get; set; }
}
}
|
GraphQL Create and Update Entity
C# |
---|
| namespace ITsynch.Suite.Entity.Application.GraphQL
{
public record CreateEntity
{
public Guid SharingLevelId { get; set; }
}
}
|
CreateOrUpdateEntity Message
C# |
---|
| namespace ITsynch.Suite.Entity.Application
{
public record CreateOrUpdateEntity
{
public Guid SharingLevel { get; set; }
}
}
|
Remember to add the mapping of these new properties to the corresponding
Automapper profile.
Update your consumer/saga:
C# |
---|
| var entityData = context.Message;
entity.SetSharingLevel(entityData.SharingLevel);
|
Note
Remember to add new Migrations.
Lastly, we need to add the SharingLevels.AspNetModule
. The place where we add
it will depend on where you are exposing the mutations, for example : if you
have mutations exposed in AdminCenter, you should add the dependency there and
the other case is when you are exposing the mutations in the backend service
itself (e.g: Discussions, FileStorage, etc.) then add it there.
First, add the reference:
XML |
---|
| <ProjectReference Include="$(ServicesPath)SharingLevels\ITsynch.Suite.SharingLevels.AspNetModule\ITsynch.Suite.SharingLevels.AspNetModule.csproj" />
|
And finally add the module dependency:
C# |
---|
| using ITsynch.Suite.SharingLevels;
public override void SetupModule(IModuleBuilder builder)
{
base.SetupModule(builder);
builder.DependsOn<SharingLevelsAspNetModule>();
}
|
This module will hydrate the ISuiteContext
with the current SharingLevel
.
UI Integration
The steps to follow to able to use sharing levels in the UI and hydrate each
mutation with the current sharing level are as follows:
First of all, you need to add suiteSharingLevelsConfiguration in you application
module
TypeScript |
---|
| const AdminCenterConfiguration: SuiteApplicationConfiguration = {
...,
suiteSharingLevelsConfiguration: {
enabled: true
}
};
|
Then, you will need to call supportSharingLevel method from the customizeForm
method in you creation/edition form page effect. This method will hide the field
'SharingLevelId' in the form, but it will populate that field with the current
sharing level.
TypeScript |
---|
| this.customizeForm((b) =>
b
.supportSharingLevel()
...
);
|
And that's it, with these minor changes in the UI, it will send the current
sharing level on the mutation and receive it on the backend, saving that field
to the database.
Testing
First, make sure you have the sharing levels test project dependency added to
your test project:
XML |
---|
| <ProjectReference Include="$(ServicesPath)SharingLevels\ITsynch.Suite.SharingLevels.ClientModule.Testing\ITsynch.Suite.SharingLevels.ClientModule.Testing.csproj" />
|
If your entity is shareable, in testing runtime it will try to apply sharings as
well, so to mock these sharings (otherwise the sharings logic will fail) you
need to mock Sharing Levels, relate them to your entity and configure the mock
objects:
C# |
---|
| private SharingLevelsStoreMock SharingLevelsStoreMock { get; }
private Guid CurrentSharingLevelId => this.SourceLevel.CorrelationId;
private SharingLevel SourceLevel { get; }
private SharingLevel TargetLevel { get; }
public EntityIntegrationTests(ITestOutputHelper helper)
: base(helper)
{
this.SourceLevel = new SharingLevel
{
CorrelationId = NewId.NextGuid(),
DisplayName = "Source level",
ParentId = null
};
this.TargetLevel = new SharingLevel
{
CorrelationId = NewId.NextGuid(),
DisplayName = "Target level",
ParentId = null
};
this.SharingLevelsStoreMock = SharingLevelsStoreMock.Create(entitySharingConfig =>
{
entitySharingConfig.ConfigureSharingForEntity<Entity>(sharingConfig =>
{
sharingConfig.AddSharing(this.SourceLevel, this.TargetLevel);
});
});
}
protected override void WireSuiteApplication(IHostBuilder hostBuilder)
{
base.WireSuiteApplication(hostBuilder);
var suiteContext = new Mock<ISuiteContext>();
suiteContext.Setup(d => d.Get(It.IsAny<string>())).Returns(this.CurrentSharingLevelId.ToString());
hostBuilder.ConfigureContainer<IServiceCollection>(services =>
{
services.Replace(ServiceDescriptor.Scoped(_ => suiteContext.Object));
services.Replace(new ServiceDescriptor(
typeof(ISharingLevelsStore),
this.SharingLevelsStoreMock.Object));
});
}
|
And if your entity has Entity Management implemented (the entity is described)
you need to add the following configuration:
C# |
---|
| public override async Task InitializeAsync()
{
await base.InitializeAsync();
this.SharingLevelsStoreMock.UseEntityManagement(this.ServiceProvider);
}
|
Disabling SharingLevels BusObserver
When you depend on the SharingLevelsClientModule
a BusObserver will be added
to DI, this can cause some issues when running tests, in order to deactivate it
you can do one of the following.
If your test has a custom Suite TestModule, you can simply add the following
dependency:
C# |
---|
| public override void SetupModule(IModuleBuilder builder)
{
base.SetupModule(builder);
builder.DependsOn<SharingLevelsTestingClientModule>();
}
|
If that's not the case, you need to override the following method:
C# |
---|
| protected override void ConfigureBootstrapServices(
HostBuilderContext context,
IServiceCollection bootstrapServices)
{
base.ConfigureBootstrapServices(context, bootstrapServices);
bootstrapServices.UseSharingLevelsTestingModule();
}
|
Now, the Sharing Levels client bus observer will not run.