-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#738 #1990 Route Metadata as custom properties #1843
Merged
ggnaegi
merged 32 commits into
ThreeMammals:release/24.0
from
vantm:feat/route-metadata
May 21, 2024
Merged
Changes from 30 commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
8cf8305
feat(configuration): adding route metadata
vantm d5eae05
feat(configuration): update docs
vantm 2bd8d75
feat(configuration): replace Dictionary<> by IDictionary<>, code clea…
vantm b5087d8
feat(configuration): replace Dictionary<> by IDictionary<>
vantm f132867
feat(configuration): replace Dictionary<> by IDictionary<>
vantm f84327a
feat(configuration): update the data type of FileDynamicRoute Metadata
vantm 3a94444
formatting
raman-m 2b07078
feat(configuration): fix integration tests
vantm 35ec151
feat !1843 add extension methods for DownstreamRoute to get metadata
vantm 97df824
feat !1843 add extension methods for DownstreamRoute
vantm 775e1ad
feat !1843 update docs
vantm 1c95421
feat !1843 update docs
vantm d03bdbc
feat !1843 cleanup split string logic
vantm 474eef0
SA1505: An opening brace should not be followed by a blank line
raman-m 48a17a3
IDE1006: Naming rule violation: These words must begin with upper cas…
raman-m 7055457
Fix compile errors after rebasing
raman-m dfcc351
Fix unit tests + AAA pattern
raman-m 8f21752
First Version, providing a generic extension method GetMetadata<T> wi…
ggnaegi 768a305
Adding ConvertToNumericType method to be able to use the NumberStyles…
ggnaegi 7544f60
adding first acceptance tests
ggnaegi 7f2f575
The tests are now passing again...
ggnaegi a3a2725
adding latest test cases. That should be enough (includes global conf…
ggnaegi aebf5b2
Update metadata.rst
ggnaegi c9724e2
adding the xml docs for IMetadataCreator and MetadataCreator
ggnaegi 0d37962
renaming MetadataCreator to DefaultMetadataCreator
ggnaegi 3c624fa
number tests for .net 6 too
ggnaegi 0279fe3
Moving Metadata specific downstream route extensions to the Metadata …
ggnaegi a4db704
cleanup
ggnaegi 8bd1dbc
applying some of the requested changes
ggnaegi 987c288
Final code review by @raman-m
raman-m 40b404f
Add traits
raman-m 5f2beb6
Fix docs build error
raman-m File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ggnaegi marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
Metadata | ||
======== | ||
|
||
Configuration | ||
------------- | ||
|
||
Ocelot provides various features such as routing, authentication, caching, load | ||
balancing, and more. | ||
However, some users may encounter situations where Ocelot does not meet their | ||
specific needs or they want to customize its behavior. | ||
In such cases, Ocelot allows users to add metadata to the route configuration. | ||
This property can store any arbitrary data that users can access in middlewares | ||
or delegating handlers. | ||
|
||
By using the metadata, users can implement their own logic and extend the | ||
functionality of Ocelot e.g. | ||
|
||
.. code-block:: json | ||
|
||
{ | ||
"Routes": [ | ||
{ | ||
"UpstreamHttpMethod": [ "GET" ], | ||
"UpstreamPathTemplate": "/posts/{postId}", | ||
"DownstreamPathTemplate": "/api/posts/{postId}", | ||
"DownstreamHostAndPorts": [ | ||
{ "Host": "localhost", "Port": 80 } | ||
], | ||
"Metadata": { | ||
"id": "FindPost", | ||
"tags": "tag1, tag2, area1, area2, func1", | ||
"plugin1.enabled": "true", | ||
"plugin1.values": "[1, 2, 3, 4, 5]", | ||
"plugin1.param": "value2", | ||
"plugin1.param2": "123", | ||
"plugin2/param1": "overwritten-value", | ||
"plugin2/param2": "{\"name\":\"John Doe\",\"age\":30,\"city\":\"New York\",\"is_student\":false,\"hobbies\":[\"reading\",\"hiking\",\"cooking\"]}" | ||
ggnaegi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
], | ||
"GlobalConfiguration": { | ||
"Metadata": { | ||
"instance_name": "machine-1", | ||
"plugin2/param1": "default-value" | ||
} | ||
} | ||
} | ||
|
||
Now, the route metadata can be accessed through the ``DownstreamRoute`` object: | ||
|
||
.. code-block:: csharp | ||
|
||
public class MyMiddleware | ||
ggnaegi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
public Task Invoke(HttpContext context, Func<Task> next) | ||
{ | ||
var route = context.Items.DownstreamRoute(); | ||
var enabled = route.GetMetadata<bool>("plugin1.enabled"); | ||
var values = route.GetMetadata<string[]>("plugin1.values"); | ||
var param1 = route.GetMetadata<string>("plugin1.param", "system-default-value"); | ||
var param2 = route.GetMetadata<int>("plugin1.param2"); | ||
|
||
// working on the plugin1's function | ||
|
||
return next?.Invoke(); | ||
} | ||
} | ||
|
||
Extension Methods | ||
----------------- | ||
|
||
Ocelot provides one DowstreamRoute extension method to help you retrieve your metadata values effortlessly. | ||
With the exception of the types string, bool, bool?, string[] and numeric, all strings passed as parameters are treated as json strings and an attempt is made to convert them into objects of generic type T. | ||
If the value is null, then, if not explicitely specified, the default for the chosen target type is returned. | ||
|
||
.. list-table:: | ||
:widths: 20 40 40 | ||
raman-m marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
* - Method | ||
- Description | ||
- Notes | ||
* - ``GetMetadata<string>`` | ||
- The metadata value is returned as string without further parsing | ||
* - ``GetMetadata<string[]>`` | ||
- The metadata value is splitted by a given separator (default ``,``) and | ||
returned as a string array. | ||
- Several parameters can be set in the global configuration, such as Separators (default = ``[","]``), StringSplitOptions (default ``None``) and TrimChars, the characters that should be trimmed (default = ``[' ']``). | ||
* - ``GetMetadata<Any known numeric type>`` | ||
- The metadata value is parsed to a number. | ||
- Some parameters can be set in the global configuration, such as NumberStyle (default ``Any``) and CurrentCulture (default ``CultureInfo.CurrentCulture``) | ||
* - ``GetMetadata<T>`` | ||
- The metadata value is converted to the given generic type. The value is treated as a json string and the json serializer tries to deserialize the string to the target type. | ||
- A JsonSerializerOptions object can be passed as method parameter, Web is used as default. | ||
* - ``GetMetadata<bool>`` | ||
- Check if the metadata value is a truthy value, otherwise return false. | ||
- The truthy values are: ``true``, ``yes``, ``ok``, ``on``, ``enable``, ``enabled`` | ||
* - ``GetMetadata<bool?>`` | ||
- Check if the metadata value is a truthy value (return true), or falsy value (return false), otherwise return null. | ||
- The known truthy values are: ``true``, ``yes``, ``ok``, ``on``, ``enable``, ``enabled``, ``1``, the known falsy values are: ``false``, ``no``, ``off``, ``disable``, ``disabled``, ``0`` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
src/Ocelot/Configuration/Builder/MetadataOptionsBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
using System.Globalization; | ||
|
||
namespace Ocelot.Configuration.Builder; | ||
|
||
public class MetadataOptionsBuilder | ||
ggnaegi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
private string[] _separators; | ||
private char[] _trimChars; | ||
private StringSplitOptions _stringSplitOption; | ||
private NumberStyles _numberStyle; | ||
private CultureInfo _currentCulture; | ||
private IDictionary<string, string> _metadata; | ||
|
||
public MetadataOptionsBuilder WithSeparators(string[] separators) | ||
{ | ||
_separators = separators; | ||
return this; | ||
} | ||
|
||
public MetadataOptionsBuilder WithTrimChars(char[] trimChars) | ||
{ | ||
_trimChars = trimChars; | ||
return this; | ||
} | ||
|
||
public MetadataOptionsBuilder WithStringSplitOption(string stringSplitOption) | ||
{ | ||
_stringSplitOption = Enum.Parse<StringSplitOptions>(stringSplitOption); | ||
return this; | ||
} | ||
|
||
public MetadataOptionsBuilder WithNumberStyle(string numberStyle) | ||
{ | ||
_numberStyle = Enum.Parse<NumberStyles>(numberStyle); | ||
return this; | ||
} | ||
|
||
public MetadataOptionsBuilder WithCurrentCulture(string currentCulture) | ||
{ | ||
_currentCulture = CultureInfo.GetCultureInfo(currentCulture); | ||
return this; | ||
} | ||
|
||
public MetadataOptionsBuilder WithMetadata(IDictionary<string, string> metadata) | ||
{ | ||
_metadata = metadata; | ||
return this; | ||
} | ||
|
||
public MetadataOptions Build() | ||
{ | ||
return new MetadataOptions(_separators, _trimChars, _stringSplitOption, _numberStyle, _currentCulture, _metadata); | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
src/Ocelot/Configuration/Creator/DefaultMetadataCreator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
using Ocelot.Configuration.Builder; | ||
using Ocelot.Configuration.File; | ||
|
||
namespace Ocelot.Configuration.Creator; | ||
ggnaegi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// <summary> | ||
/// This class implements the <see cref="IMetadataCreator"/> interface. | ||
/// </summary> | ||
public class DefaultMetadataCreator : IMetadataCreator | ||
{ | ||
public MetadataOptions Create(IDictionary<string, string> metadata, FileGlobalConfiguration globalConfiguration) | ||
{ | ||
// metadata from the route could be null when no metadata is defined | ||
metadata ??= new Dictionary<string, string>(); | ||
|
||
// metadata from the global configuration is never null | ||
var options = globalConfiguration.MetadataOptions; | ||
var mergedMetadata = new Dictionary<string, string>(options.Metadata); | ||
|
||
foreach (var (key, value) in metadata) | ||
{ | ||
mergedMetadata[key] = value; | ||
} | ||
|
||
return new MetadataOptionsBuilder() | ||
.WithMetadata(mergedMetadata) | ||
.WithSeparators(options.Separators) | ||
.WithTrimChars(options.TrimChars) | ||
.WithStringSplitOption(options.StringSplitOption) | ||
.WithNumberStyle(options.NumberStyle) | ||
.WithCurrentCulture(options.CurrentCulture) | ||
.Build(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using Ocelot.Configuration.File; | ||
|
||
namespace Ocelot.Configuration.Creator; | ||
ggnaegi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// <summary> | ||
/// This interface describes the creation of metadata options. | ||
/// </summary> | ||
public interface IMetadataCreator | ||
ggnaegi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
MetadataOptions Create(IDictionary<string, string> metadata, FileGlobalConfiguration globalConfiguration); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We must have a short subsection in configuration.rst doc and redirect to the webpage with complete reference as metadata.rst chapter.