HotChocolate.Extensions.Tracking.MassTransit
3.0.0-rc.1
dotnet add package HotChocolate.Extensions.Tracking.MassTransit --version 3.0.0-rc.1
NuGet\Install-Package HotChocolate.Extensions.Tracking.MassTransit -Version 3.0.0-rc.1
<PackageReference Include="HotChocolate.Extensions.Tracking.MassTransit" Version="3.0.0-rc.1" />
paket add HotChocolate.Extensions.Tracking.MassTransit --version 3.0.0-rc.1
#r "nuget: HotChocolate.Extensions.Tracking.MassTransit, 3.0.0-rc.1"
// Install HotChocolate.Extensions.Tracking.MassTransit as a Cake Addin #addin nuget:?package=HotChocolate.Extensions.Tracking.MassTransit&version=3.0.0-rc.1&prerelease // Install HotChocolate.Extensions.Tracking.MassTransit as a Cake Tool #tool nuget:?package=HotChocolate.Extensions.Tracking.MassTransit&version=3.0.0-rc.1&prerelease
This repository contains features that can be added to your HotChocolate Server to enhance your HotChocolate experience.
HotChocolate.Extensions.Translations
This package uses HotChocolate directives and middlewares to add translations to your HotChocolate server. Easily extend your fields to transform their code-value (for instance "CH") into a more presentable value for your consumer ("Switzerland"/"Schweiz"/"Suisse"...).
The resource strings containing the translations can be provided from any source you like: external microservices, from memory, resx files...
Install
You will need to include the following package on your HotChocolate Server:
dotnet add package HotChocolate.Extensions.Translation
The next step is to register some directives to your GraphQL Schema:
new ServiceCollection()
.AddGraphQLServer()
.SetSchema<MySchema>()
.AddTranslation(
/* add all translatable types explicitely, except String, which is already added implicitely. */
c => c.AddTranslatableType<Country>()
.AddTranslatableType<MyEnum2>()
);
The last step is to implement and register an IResourcesProvider. This provider will be used to retrieve the string resources in the target language.
services.AddSingleton<IResourcesProvider, MyResourcesProvider>();
The alternative way is to implement IStringLocalizer interfaces. Additionally, we can use resource type marker classes. This approach overrides all logic related to the usage of the IResourcesProvider interface.
namespace Namespace.Namespace1.Namespace2;
[ResourceTypeAlias("SomePath")]
public class CustomResource
{
}
new ServiceCollection()
.AddGraphQLServer()
.SetSchema<MySchema>()
.AddTranslation(
/* add all translatable types explicitely, except String, which is already added implicitely. */
c => c.AddTranslatableType<Country>()
.AddTranslatableType<MyEnum2>()
)
.AddStringLocalizer<CustomLocalizer>(ServiceLifetime.Singleton, typeof(CustomResource))
.AddStringLocalizer<OtherCustomLocalizer>(ServiceLifetime.Scoped, [ typeof(OtherCustomResource) /* additional resources can share the same localizer logic */ ])
.AddStringLocalizer(typeof(OpenGenericLocalizer<>), [ typeof(AnyResource), ......]);
If the resource type is not decorated with the ResourceTypeAlias attribute, the default alias is generated from the namespace and name of the resource class. For the case described above, it would generate the alias "Namespace::Namespace1::Namespace2::CustomResource".
With this we have registered the necessary objects to support translation on our fields. Now we can start adding translation support to our fields.
Translating fields
You can translate field values in several ways, listed below.
Translation to a { key label } type
We can also rewrite our string/enum/other field to make it a { key label }
field.
This can be useful if we also use Array Translations, which use the same typing (see next chapter).
[ResourceTypeAlias("Ref/Aex/Countries"))]
public class CountryResource
{
}
public class AddressType : ObjectType<Query>
{
protected override void Configure(IObjectTypeDescriptor<Query> descriptor)
{
descriptor
.Field(c => c.Country) // The Country property is a Country enum
.Translate("Ref/Aex/Countries");
// OR
// descriptor
// .Field(c => c.Country)
// .Translate(typeof(CountryResource));
}
}
Alternatively it is possible to translate the field via an attribute directly on the property:
[ResourceTypeAlias("Ref/Aex/Countries"))]
public class CountryResource
{
}
public class Address
{
[Translate("Ref/Aex/Countries")]
public Country Country { get; }
// OR
// [Translate(typeof(CountryResource)]
// public Country Country { get; }
}
In both cases, this will generate the following Schema:
type Query {
country: TranslatedResourceOfCountry!
}
type TranslatedResourceOfCountry {
key: Country!
label: String!
}
Querying this field will produce the following results:
{
country {
key // -> "CH",
label // -> "Switzerland" if your Thread language is english
}
}
Translate arrays to { key label } items
We can translate string/enum/other arrays to { key label }
arrays.
public class AddressType : ObjectType<Query>
{
protected override void Configure(IObjectTypeDescriptor<Query> descriptor)
{
descriptor.Field(c => c.Countries)
.TranslateArray<Country>("Ref/Aex/Countries");
}
}
Alternatively it is possible to translate the field via an attribute directly on the property:
public class Address
{
[TranslateArray<Country>("Ref/Aex/Countries")]
public Country[] Countries { get; }
}
This will generate the following Schema:
type Query {
countries: [TranslatedResourceOfCountry!]!
}
type TranslatedResourceOfCountry {
key: Country!
label: String!
}
Querying this field will produce the following results:
{
countries {
key
label
}
}
{
countries: [
{
key: "FR",
label: "France"
},
{
key: "CH",
label: "Switzerland"
}
}
FAQ
What language is used by default by this extension?
The language of the thread, which you can for instance initialize via the ASP.NET Core Localization Middleware.
What is the performance impact of translations?
This will depend in large part on your implementation of IResourcesProvider
. This provider is registered in the service container and can therefore use any registered service of your applicaton.
In our samples, we usually build our resource strings into our assemblies so that we can retrieve them very quickly from the memory.
If you are retrieving your resource strings from another microservice, you could consider injecting Greendonut DataLoaders or IMemoryCache to reduce calls to the external microservice.
HotChocolate.Extensions.Tracking
This project enables tracking via middlewares on HotChocolate Query and mutation fields. In order to save the tracking items, a custom repository needs to be injected. As for now, the only provided repository is the MassTransitRepository, which essentially sends the tracking item to an azure ServiceBus topic.
Note: In order to not slow down the resolver pipelines of Tracked HotChocolate Fields, the context data of the tracked Field is passed into a System.Threading.Channels.Channel and then persisted asynchonously. As a consequence, some tracking items could be lost if the server were to shut down unexpectedly.
Setting up the depencies and schema
Register the tracking pipeline in the GraphQL Schema.
Schema schema = await services
.AddGraphQLServer()
.AddTrackingPipeline(builder => builder
.AddExporter<NotifyOnFirstEntryExporter>()
.AddSupportedType<MyTrace>()
.AddSupportedType<MyOtherTrace>());
Basic tracking by Tags
The basic field invocation tracking produces a message with the following data:
- a tag
- a timestamp
You can add tracking on a field in the following way:
fieldDescriptor.Field("myField").Track(tag:"myFieldWasCalled");
Alternatively, you can also add tracking on the field via an attribute on the field/resolver:
public class Query
{
[Track("FooInvoked")]
public string Foo => "bar";
}
Custom tracking
If you want to save custom data fields, you have the possibility to write your own TrackingEntryFactory.
public sealed class MyTrackingEntryFactory : ITrackingEntryFactory
{
public ITrackingEntry CreateTrackingEntry(
IHttpContextAccessor httpContextAccessor,
IResolverContext context)
{
// Get data from httpContextAccessor or the resolver context and create and return a custom ITrackingEntry from it.
}
You can then add custom tracking to your fields:
fieldDescriptor.Field("myField").Track(new MyTrackingEntryFactory());
Or alternatively via an attribute:
public class Query
{
[Track<MyTrackingEntryFactory>]
public string Foo => "bar";
}
Community
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community. For more information, see the Swiss Life OSS Code of Conduct.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net7.0 is compatible. 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 is compatible. 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. |
-
net7.0
- HotChocolate.Extensions.Tracking (>= 3.0.0-rc.1)
- MassTransit.Azure.ServiceBus.Core (>= 8.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 7.0.0)
-
net8.0
- HotChocolate.Extensions.Tracking (>= 3.0.0-rc.1)
- MassTransit.Azure.ServiceBus.Core (>= 8.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.0)
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 |
---|---|---|
3.0.0-rc.1 | 47 | 10/17/2024 |
3.0.0-preview.7 | 43 | 10/10/2024 |
3.0.0-preview.5 | 47 | 10/9/2024 |
3.0.0-preview.4 | 41 | 10/9/2024 |
3.0.0-preview.3 | 43 | 10/9/2024 |
3.0.0-preview.1 | 41 | 10/9/2024 |
2.3.0-preview.6 | 61 | 7/25/2024 |
2.3.0-preview.5 | 62 | 7/18/2024 |
2.3.0-preview.4 | 60 | 7/2/2024 |
2.3.0-preview.3 | 57 | 7/1/2024 |
2.3.0-preview.2 | 55 | 6/28/2024 |
2.3.0-preview.1 | 60 | 6/27/2024 |
2.2.0 | 7,388 | 4/4/2023 |
2.1.0 | 211 | 4/3/2023 |
2.0.0 | 3,112 | 2/10/2023 |
1.0.0 | 304 | 2/9/2023 |
1.0.0-preview.4 | 3,632 | 12/2/2022 |
1.0.0-preview.3 | 316 | 11/30/2022 |
1.0.0-preview.2 | 103 | 11/29/2022 |