From 7ff37a6758680ae74fe8aa0adf29a962f172b967 Mon Sep 17 00:00:00 2001 From: Jakub Bednar Date: Wed, 13 Apr 2022 09:08:04 +0200 Subject: [PATCH 1/6] chore: prepare tests for takeLast --- Client.Linq.Test/InfluxDBQueryVisitorTest.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Client.Linq.Test/InfluxDBQueryVisitorTest.cs b/Client.Linq.Test/InfluxDBQueryVisitorTest.cs index 94f2dca12..e4da064e2 100644 --- a/Client.Linq.Test/InfluxDBQueryVisitorTest.cs +++ b/Client.Linq.Test/InfluxDBQueryVisitorTest.cs @@ -98,6 +98,18 @@ public void ResultOperatorTake() Assert.AreEqual(expected, visitor.BuildFluxQuery()); } + + [Test] + public void ResultOperatorTakeLast() + { + var query = from s in InfluxDBQueryable.Queryable("my-bucket", "my-org", _queryApi) + select s; + var visitor = BuildQueryVisitor(query, MakeExpression(query, q => q.TakeLast(10))); + + const string expected = FluxStart + " " + "|> tail(n: p3)"; + + Assert.AreEqual(expected, visitor.BuildFluxQuery()); + } [Test] public void ResultOperatorSkip() From d48750f2ec5b08e2a4458ebe09eb8c9f271c0629 Mon Sep 17 00:00:00 2001 From: Jakub Bednar Date: Wed, 13 Apr 2022 13:49:00 +0200 Subject: [PATCH 2/6] feat(linq): add support for TakeLast expression --- Client.Linq.Test/InfluxDBQueryVisitorTest.cs | 14 +-- Client.Linq/InfluxDBQueryable.cs | 14 ++- .../NodeTypes/InfluxDBNodeTypeProvider.cs | 27 ++++++ .../Internal/NodeTypes/TakeLastNodeType.cs | 89 +++++++++++++++++++ Client.Linq/Internal/QueryAggregator.cs | 26 +++--- Client.Linq/Internal/QueryVisitor.cs | 10 ++- 6 files changed, 156 insertions(+), 24 deletions(-) create mode 100644 Client.Linq/Internal/NodeTypes/InfluxDBNodeTypeProvider.cs create mode 100644 Client.Linq/Internal/NodeTypes/TakeLastNodeType.cs diff --git a/Client.Linq.Test/InfluxDBQueryVisitorTest.cs b/Client.Linq.Test/InfluxDBQueryVisitorTest.cs index e4da064e2..e7b7f6017 100644 --- a/Client.Linq.Test/InfluxDBQueryVisitorTest.cs +++ b/Client.Linq.Test/InfluxDBQueryVisitorTest.cs @@ -15,7 +15,6 @@ using NUnit.Framework; using Remotion.Linq; using Remotion.Linq.Parsing.ExpressionVisitors; -using Remotion.Linq.Parsing.Structure; using Expression = System.Linq.Expressions.Expression; namespace Client.Linq.Test @@ -104,10 +103,15 @@ public void ResultOperatorTakeLast() { var query = from s in InfluxDBQueryable.Queryable("my-bucket", "my-org", _queryApi) select s; + var visitor = BuildQueryVisitor(query, MakeExpression(query, q => q.TakeLast(10))); - const string expected = FluxStart + " " + "|> tail(n: p3)"; + var expected = FluxStart + " " + "|> tail(n: p3)"; + Assert.AreEqual(expected, visitor.BuildFluxQuery()); + + visitor = BuildQueryVisitor(query, MakeExpression(query, q => q.TakeLast(10).Skip(5))); + expected = FluxStart + " " + "|> tail(n: p3, offset: p4)"; Assert.AreEqual(expected, visitor.BuildFluxQuery()); } @@ -118,9 +122,7 @@ public void ResultOperatorSkip() select s; var visitor = BuildQueryVisitor(query, MakeExpression(query, q => q.Skip(5))); - const string expected = FluxStart; - - Assert.AreEqual(expected, visitor.BuildFluxQuery()); + Assert.AreEqual(FluxStart, visitor.BuildFluxQuery()); } [Test] @@ -1109,7 +1111,7 @@ public void FilterByTimeAndTagWithAnds() private InfluxDBQueryVisitor BuildQueryVisitor(IQueryable queryable, Expression expression = null) { var queryExecutor = (InfluxDBQueryExecutor)((DefaultQueryProvider)queryable.Provider).Executor; - var queryModel = QueryParser.CreateDefault().GetParsedQuery(expression ?? queryable.Expression); + var queryModel = InfluxDBQueryable.CreateQueryParser().GetParsedQuery(expression ?? queryable.Expression); return queryExecutor.QueryVisitor(queryModel); } diff --git a/Client.Linq/InfluxDBQueryable.cs b/Client.Linq/InfluxDBQueryable.cs index 4c277308e..456ecd801 100644 --- a/Client.Linq/InfluxDBQueryable.cs +++ b/Client.Linq/InfluxDBQueryable.cs @@ -1,12 +1,15 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; using System.Threading; +using InfluxDB.Client.Api.Domain; using InfluxDB.Client.Core; using InfluxDB.Client.Linq.Internal; +using InfluxDB.Client.Linq.Internal.NodeTypes; using Remotion.Linq; using Remotion.Linq.Parsing.Structure; +using Remotion.Linq.Parsing.Structure.NodeTypeProviders; +using Expression = System.Linq.Expressions.Expression; namespace InfluxDB.Client.Linq { @@ -175,7 +178,7 @@ public InfluxDBQueryable(IQueryProvider provider, Expression expression) : base( /// Create a object that will be used for Querying. /// /// Query that will be used to Querying - public Api.Domain.Query ToDebugQuery() + public Query ToDebugQuery() { var provider = Provider as DefaultQueryProvider; var executor = provider?.Executor as InfluxDBQueryExecutor; @@ -213,9 +216,12 @@ private static IQueryExecutor CreateExecutor(string bucket, string org, QueryApi queryableOptimizerSettings ?? new QueryableOptimizerSettings()); } - private static QueryParser CreateQueryParser() + internal static QueryParser CreateQueryParser() { - return QueryParser.CreateDefault(); + var queryParser = QueryParser.CreateDefault(); + var compoundNodeTypeProvider = queryParser.NodeTypeProvider as CompoundNodeTypeProvider; + compoundNodeTypeProvider?.InnerProviders.Add(new InfluxDBNodeTypeProvider()); + return queryParser; } public IAsyncEnumerable GetAsyncEnumerator(CancellationToken cancellationToken = default) diff --git a/Client.Linq/Internal/NodeTypes/InfluxDBNodeTypeProvider.cs b/Client.Linq/Internal/NodeTypes/InfluxDBNodeTypeProvider.cs new file mode 100644 index 000000000..56cfefb4c --- /dev/null +++ b/Client.Linq/Internal/NodeTypes/InfluxDBNodeTypeProvider.cs @@ -0,0 +1,27 @@ +using System; +using System.Reflection; +using Remotion.Linq.Parsing.Structure; +using Remotion.Linq.Parsing.Structure.NodeTypeProviders; + +namespace InfluxDB.Client.Linq.Internal.NodeTypes +{ + internal class InfluxDBNodeTypeProvider : INodeTypeProvider + { + private readonly MethodInfoBasedNodeTypeRegistry _methodInfoRegistry = new MethodInfoBasedNodeTypeRegistry(); + + internal InfluxDBNodeTypeProvider() + { + _methodInfoRegistry.Register(TakeLastExpressionNode.GetSupportedMethods, typeof(TakeLastExpressionNode)); + } + + public bool IsRegistered(MethodInfo method) + { + return _methodInfoRegistry.IsRegistered(method); + } + + public Type GetNodeType(MethodInfo method) + { + return _methodInfoRegistry.GetNodeType(method); + } + } +} \ No newline at end of file diff --git a/Client.Linq/Internal/NodeTypes/TakeLastNodeType.cs b/Client.Linq/Internal/NodeTypes/TakeLastNodeType.cs new file mode 100644 index 000000000..047279251 --- /dev/null +++ b/Client.Linq/Internal/NodeTypes/TakeLastNodeType.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using InfluxDB.Client.Core; +using Remotion.Linq.Clauses; +using Remotion.Linq.Clauses.ResultOperators; +using Remotion.Linq.Clauses.StreamedData; +using Remotion.Linq.Parsing.Structure.IntermediateModel; + +namespace InfluxDB.Client.Linq.Internal.NodeTypes +{ + internal class TakeLastExpressionNode : ResultOperatorExpressionNodeBase + { + private readonly Expression _count; + + internal static readonly IEnumerable GetSupportedMethods = + new ReadOnlyCollection(typeof(Enumerable).GetRuntimeMethods() + .Concat(typeof(Queryable).GetRuntimeMethods()).ToList()) + .Where(mi => mi.Name == "TakeLast"); + + public TakeLastExpressionNode(MethodCallExpressionParseInfo parseInfo, Expression count) + : base(parseInfo, null, null) + { + _count = count; + } + + public override Expression Resolve( + ParameterExpression inputParameter, + Expression expressionToBeResolved, + ClauseGenerationContext clauseGenerationContext) + { + Arguments.CheckNotNull(inputParameter, nameof(inputParameter)); + Arguments.CheckNotNull(expressionToBeResolved, nameof(expressionToBeResolved)); + return Source.Resolve(inputParameter, expressionToBeResolved, clauseGenerationContext); + } + + protected override ResultOperatorBase CreateResultOperator(ClauseGenerationContext clauseGenerationContext) + { + return new TakeLastResultOperator(_count); + } + } + + internal class TakeLastResultOperator : SequenceTypePreservingResultOperatorBase + { + private Expression _count; + + internal TakeLastResultOperator(Expression count) + { + Arguments.CheckNotNull(count, nameof(count)); + Count = count; + } + + public Expression Count + { + get => _count; + private set + { + Arguments.CheckNotNull(value, nameof(value)); + _count = ReferenceEquals(value.Type, typeof(int)) + ? value + : throw new ArgumentException(string.Format( + "The value expression returns '{0}', an expression returning 'System.Int32' was expected.", + new object[] + { + value.Type + }), nameof(value)); + } + } + + public override ResultOperatorBase Clone(CloneContext cloneContext) => + new TakeResultOperator(Count); + + public override StreamedSequence ExecuteInMemory(StreamedSequence input) => new StreamedSequence( + input.GetTypedSequence().Take(GetConstantCount()).AsQueryable(), + GetOutputDataInfo(input.DataInfo)); + + public override void TransformExpressions(Func transformation) + { + Arguments.CheckNotNull(transformation, nameof(transformation)); + Count = transformation(Count); + } + + public override string ToString() => $"TakeLast({Count})"; + private int GetConstantCount() => GetConstantValueFromExpression("count", Count); + } +} \ No newline at end of file diff --git a/Client.Linq/Internal/QueryAggregator.cs b/Client.Linq/Internal/QueryAggregator.cs index 5658b05f8..0d486c325 100644 --- a/Client.Linq/Internal/QueryAggregator.cs +++ b/Client.Linq/Internal/QueryAggregator.cs @@ -49,6 +49,7 @@ internal enum RangeExpressionType internal class LimitOffsetAssignment { + internal string FluxFunction; internal string N; internal string Offset; } @@ -60,7 +61,7 @@ internal class QueryAggregator private RangeExpressionType _rangeStartExpression; private string _rangeStopAssignment; private RangeExpressionType _rangeStopExpression; - private readonly List _limitNOffsetAssignments; + private readonly List _limitTailNOffsetAssignments; private ResultFunction _resultFunction; private readonly List _filterByTags; private readonly List _filterByFields; @@ -70,7 +71,7 @@ internal class QueryAggregator internal QueryAggregator() { _resultFunction = ResultFunction.None; - _limitNOffsetAssignments = new List(); + _limitTailNOffsetAssignments = new List(); _filterByTags = new List(); _filterByFields = new List(); _orders = new List<(string, string, bool, string)>(); @@ -100,27 +101,28 @@ internal void AddAggregateWindow(string everyVariable, string periodVariable, st } - internal void AddLimitN(string limitNAssignment) + internal void AddLimitTailN(string limitNAssignment, string fluxFunction) { - if (_limitNOffsetAssignments.Count > 0 && _limitNOffsetAssignments.Last().N == null) + if (_limitTailNOffsetAssignments.Count > 0 && _limitTailNOffsetAssignments.Last().N == null) { - _limitNOffsetAssignments.Last().N = limitNAssignment; + _limitTailNOffsetAssignments.Last().FluxFunction = fluxFunction; + _limitTailNOffsetAssignments.Last().N = limitNAssignment; } else { - _limitNOffsetAssignments.Add(new LimitOffsetAssignment { N = limitNAssignment }); + _limitTailNOffsetAssignments.Add(new LimitOffsetAssignment { FluxFunction = fluxFunction, N = limitNAssignment }); } } - internal void AddLimitOffset(string limitOffsetAssignment) + internal void AddLimitTailOffset(string limitOffsetAssignment) { - if (_limitNOffsetAssignments.Count > 0) + if (_limitTailNOffsetAssignments.Count > 0) { - _limitNOffsetAssignments.Last().Offset = limitOffsetAssignment; + _limitTailNOffsetAssignments.Last().Offset = limitOffsetAssignment; } else { - _limitNOffsetAssignments.Add(new LimitOffsetAssignment { Offset = limitOffsetAssignment }); + _limitTailNOffsetAssignments.Add(new LimitOffsetAssignment { Offset = limitOffsetAssignment }); } } @@ -193,10 +195,10 @@ internal string BuildFluxQuery(QueryableOptimizerSettings settings) } // https://docs.influxdata.com/flux/v0.x/stdlib/universe/limit/ - foreach (var limitNOffsetAssignment in _limitNOffsetAssignments) + foreach (var limitNOffsetAssignment in _limitTailNOffsetAssignments) if (limitNOffsetAssignment.N != null) { - parts.Add(BuildOperator("limit", + parts.Add(BuildOperator(limitNOffsetAssignment.FluxFunction, "n", limitNOffsetAssignment.N, "offset", limitNOffsetAssignment.Offset)); } diff --git a/Client.Linq/Internal/QueryVisitor.cs b/Client.Linq/Internal/QueryVisitor.cs index 51e42d4d0..09919dcd4 100644 --- a/Client.Linq/Internal/QueryVisitor.cs +++ b/Client.Linq/Internal/QueryVisitor.cs @@ -6,6 +6,7 @@ using System.Text; using InfluxDB.Client.Api.Domain; using InfluxDB.Client.Linq.Internal.Expressions; +using InfluxDB.Client.Linq.Internal.NodeTypes; using Remotion.Linq; using Remotion.Linq.Clauses; using Remotion.Linq.Clauses.ResultOperators; @@ -135,12 +136,17 @@ public override void VisitResultOperator(ResultOperatorBase resultOperator, Quer { case TakeResultOperator takeResultOperator: var takeVariable = GetFluxExpression(takeResultOperator.Count, takeResultOperator); - _context.QueryAggregator.AddLimitN(takeVariable); + _context.QueryAggregator.AddLimitTailN(takeVariable, "limit"); break; + case TakeLastResultOperator takeLastResultOperator: + var takeLastVariable = GetFluxExpression(takeLastResultOperator.Count, takeLastResultOperator); + _context.QueryAggregator.AddLimitTailN(takeLastVariable, "tail"); + break; + case SkipResultOperator skipResultOperator: var skipVariable = GetFluxExpression(skipResultOperator.Count, skipResultOperator); - _context.QueryAggregator.AddLimitOffset(skipVariable); + _context.QueryAggregator.AddLimitTailOffset(skipVariable); break; case AnyResultOperator _: From 444186c54f5d0373ae7788bcbc67d6894beb1a8c Mon Sep 17 00:00:00 2001 From: Jakub Bednar Date: Wed, 13 Apr 2022 13:52:56 +0200 Subject: [PATCH 3/6] docs: update CHANGELOG.md --- CHANGELOG.md | 3 +-- Client.Linq.Test/InfluxDBQueryVisitorTest.cs | 9 ++++--- .../Internal/NodeTypes/TakeLastNodeType.cs | 26 ++++++++++++++----- Client.Linq/Internal/QueryAggregator.cs | 3 ++- Client.Linq/Internal/QueryVisitor.cs | 2 +- 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee770fdfc..30f7eac19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,10 @@ ### Features 1. [#304](https://github.com/influxdata/influxdb-client-csharp/pull/304): Add `InvocableScriptsApi` to create, update, list, delete and invoke scripts by seamless way + [#308](https://github.com/influxdata/influxdb-client-csharp/pull/308): Add support for `TakeLast` expression [LINQ] ### Bug Fixes 1. [#305](https://github.com/influxdata/influxdb-client-csharp/pull/305): Authentication Cookies follow redirects - -### Bug Fixes 1. [#309](https://github.com/influxdata/influxdb-client-csharp/pull/309): Query expression for joins of binary operators [LINQ] ## 4.0.0 [2022-03-18] diff --git a/Client.Linq.Test/InfluxDBQueryVisitorTest.cs b/Client.Linq.Test/InfluxDBQueryVisitorTest.cs index e7b7f6017..0d01d2cac 100644 --- a/Client.Linq.Test/InfluxDBQueryVisitorTest.cs +++ b/Client.Linq.Test/InfluxDBQueryVisitorTest.cs @@ -97,18 +97,18 @@ public void ResultOperatorTake() Assert.AreEqual(expected, visitor.BuildFluxQuery()); } - + [Test] public void ResultOperatorTakeLast() { var query = from s in InfluxDBQueryable.Queryable("my-bucket", "my-org", _queryApi) select s; - + var visitor = BuildQueryVisitor(query, MakeExpression(query, q => q.TakeLast(10))); var expected = FluxStart + " " + "|> tail(n: p3)"; Assert.AreEqual(expected, visitor.BuildFluxQuery()); - + visitor = BuildQueryVisitor(query, MakeExpression(query, q => q.TakeLast(10).Skip(5))); expected = FluxStart + " " + "|> tail(n: p3, offset: p4)"; @@ -1111,7 +1111,8 @@ public void FilterByTimeAndTagWithAnds() private InfluxDBQueryVisitor BuildQueryVisitor(IQueryable queryable, Expression expression = null) { var queryExecutor = (InfluxDBQueryExecutor)((DefaultQueryProvider)queryable.Provider).Executor; - var queryModel = InfluxDBQueryable.CreateQueryParser().GetParsedQuery(expression ?? queryable.Expression); + var queryModel = InfluxDBQueryable.CreateQueryParser() + .GetParsedQuery(expression ?? queryable.Expression); return queryExecutor.QueryVisitor(queryModel); } diff --git a/Client.Linq/Internal/NodeTypes/TakeLastNodeType.cs b/Client.Linq/Internal/NodeTypes/TakeLastNodeType.cs index 047279251..5ab4e6e23 100644 --- a/Client.Linq/Internal/NodeTypes/TakeLastNodeType.cs +++ b/Client.Linq/Internal/NodeTypes/TakeLastNodeType.cs @@ -70,12 +70,17 @@ private set } } - public override ResultOperatorBase Clone(CloneContext cloneContext) => - new TakeResultOperator(Count); + public override ResultOperatorBase Clone(CloneContext cloneContext) + { + return new TakeResultOperator(Count); + } - public override StreamedSequence ExecuteInMemory(StreamedSequence input) => new StreamedSequence( - input.GetTypedSequence().Take(GetConstantCount()).AsQueryable(), - GetOutputDataInfo(input.DataInfo)); + public override StreamedSequence ExecuteInMemory(StreamedSequence input) + { + return new StreamedSequence( + input.GetTypedSequence().Take(GetConstantCount()).AsQueryable(), + GetOutputDataInfo(input.DataInfo)); + } public override void TransformExpressions(Func transformation) { @@ -83,7 +88,14 @@ public override void TransformExpressions(Func transform Count = transformation(Count); } - public override string ToString() => $"TakeLast({Count})"; - private int GetConstantCount() => GetConstantValueFromExpression("count", Count); + public override string ToString() + { + return $"TakeLast({Count})"; + } + + private int GetConstantCount() + { + return GetConstantValueFromExpression("count", Count); + } } } \ No newline at end of file diff --git a/Client.Linq/Internal/QueryAggregator.cs b/Client.Linq/Internal/QueryAggregator.cs index 0d486c325..02acd5ff0 100644 --- a/Client.Linq/Internal/QueryAggregator.cs +++ b/Client.Linq/Internal/QueryAggregator.cs @@ -110,7 +110,8 @@ internal void AddLimitTailN(string limitNAssignment, string fluxFunction) } else { - _limitTailNOffsetAssignments.Add(new LimitOffsetAssignment { FluxFunction = fluxFunction, N = limitNAssignment }); + _limitTailNOffsetAssignments.Add(new LimitOffsetAssignment + { FluxFunction = fluxFunction, N = limitNAssignment }); } } diff --git a/Client.Linq/Internal/QueryVisitor.cs b/Client.Linq/Internal/QueryVisitor.cs index 09919dcd4..375c9f09c 100644 --- a/Client.Linq/Internal/QueryVisitor.cs +++ b/Client.Linq/Internal/QueryVisitor.cs @@ -143,7 +143,7 @@ public override void VisitResultOperator(ResultOperatorBase resultOperator, Quer var takeLastVariable = GetFluxExpression(takeLastResultOperator.Count, takeLastResultOperator); _context.QueryAggregator.AddLimitTailN(takeLastVariable, "tail"); break; - + case SkipResultOperator skipResultOperator: var skipVariable = GetFluxExpression(skipResultOperator.Count, skipResultOperator); _context.QueryAggregator.AddLimitTailOffset(skipVariable); From afe2017dada67e730a8bf72d5dd5b8fa2c99ffec Mon Sep 17 00:00:00 2001 From: Jakub Bednar Date: Wed, 13 Apr 2022 13:55:11 +0200 Subject: [PATCH 4/6] fix: code style --- Client.Linq/InfluxDBQueryable.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Client.Linq/InfluxDBQueryable.cs b/Client.Linq/InfluxDBQueryable.cs index 456ecd801..bccaf15ae 100644 --- a/Client.Linq/InfluxDBQueryable.cs +++ b/Client.Linq/InfluxDBQueryable.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using InfluxDB.Client.Api.Domain; using InfluxDB.Client.Core; using InfluxDB.Client.Linq.Internal; using InfluxDB.Client.Linq.Internal.NodeTypes; @@ -178,7 +177,7 @@ public InfluxDBQueryable(IQueryProvider provider, Expression expression) : base( /// Create a object that will be used for Querying. /// /// Query that will be used to Querying - public Query ToDebugQuery() + public Api.Domain.Query ToDebugQuery() { var provider = Provider as DefaultQueryProvider; var executor = provider?.Executor as InfluxDBQueryExecutor; From dffa72f5eab1da3688cfe957242c2f1f6249d076 Mon Sep 17 00:00:00 2001 From: Jakub Bednar Date: Wed, 13 Apr 2022 14:02:54 +0200 Subject: [PATCH 5/6] docs: add TakeLast operator --- Client.Linq/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Client.Linq/README.md b/Client.Linq/README.md index 3f989455e..96609083f 100644 --- a/Client.Linq/README.md +++ b/Client.Linq/README.md @@ -32,6 +32,7 @@ This section contains links to the client library documentation. - [Or](#or) - [Any](#any) - [Take](#take) + - [TakeLast](#takelast) - [Skip](#skip) - [OrderBy](#orderby) - [Count](#count) @@ -850,6 +851,24 @@ from(bucket: "my-bucket") |> limit(n: 10) ``` +### TakeLast + +```c# +var query = (from s in InfluxDBQueryable.Queryable("my-bucket", "my-org", queryApi) + select s) + .TakeLast(10); +``` + +Flux Query: + +```flux +from(bucket: "my-bucket") + |> range(start: 0) + |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value") + |> drop(columns: ["_start", "_stop", "_measurement"]) + |> tail(n: 10) +``` + ### Skip ```c# From 8862eb404ffecf5229889a7844e2a62c732d0988 Mon Sep 17 00:00:00 2001 From: Jakub Bednar Date: Wed, 13 Apr 2022 20:23:35 +0200 Subject: [PATCH 6/6] docs: update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30f7eac19..edddfa362 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Features 1. [#304](https://github.com/influxdata/influxdb-client-csharp/pull/304): Add `InvocableScriptsApi` to create, update, list, delete and invoke scripts by seamless way - [#308](https://github.com/influxdata/influxdb-client-csharp/pull/308): Add support for `TakeLast` expression [LINQ] +1. [#308](https://github.com/influxdata/influxdb-client-csharp/pull/308): Add support for `TakeLast` expression [LINQ] ### Bug Fixes 1. [#305](https://github.com/influxdata/influxdb-client-csharp/pull/305): Authentication Cookies follow redirects