diff --git a/.github/workflows/run_linux_container_tests.yml b/.github/workflows/run_linux_container_tests.yml index d711ed9e61..cd4de53d51 100644 --- a/.github/workflows/run_linux_container_tests.yml +++ b/.github/workflows/run_linux_container_tests.yml @@ -76,6 +76,6 @@ jobs: INTEGRATION_TEST_SECRETS: ${{ secrets.TEST_SECRETS }} run: | echo $INTEGRATION_TEST_SECRETS | dotnet user-secrets set --project ${{ env.integration_tests_shared_project }} - + - name: Build & Run Linux Container Integration Tests run: dotnet test ./tests/Agent/IntegrationTests/ContainerIntegrationTests/ContainerIntegrationTests.csproj --framework net8.0 diff --git a/src/Agent/NewRelic/Agent/Core/Api/TransactionBridgeApi.cs b/src/Agent/NewRelic/Agent/Core/Api/TransactionBridgeApi.cs index 78d8e8cb4b..915ff60bb6 100644 --- a/src/Agent/NewRelic/Agent/Core/Api/TransactionBridgeApi.cs +++ b/src/Agent/NewRelic/Agent/Core/Api/TransactionBridgeApi.cs @@ -226,7 +226,7 @@ public ISegment StartDatastoreSegment(string vendor, string model, string operat var method = new Method(typeof(object), "StartDatastoreSegment", string.Empty); var methodCall = new MethodCall(method, null, null, false); var parsedSqlStatement = new ParsedSqlStatement(DatastoreVendor.Other, model, operation); - var connectionInfo = new ConnectionInfo(vendor.ToLower(), host, portPathOrID, databaseName); + var connectionInfo = new ConnectionInfo(host, portPathOrID, databaseName); return _transaction.StartDatastoreSegment( methodCall: methodCall, parsedSqlStatement: parsedSqlStatement, diff --git a/src/Agent/NewRelic/Agent/Core/Segments/DatastoreSegmentData.cs b/src/Agent/NewRelic/Agent/Core/Segments/DatastoreSegmentData.cs index 8a445aca87..a1afd2162e 100644 --- a/src/Agent/NewRelic/Agent/Core/Segments/DatastoreSegmentData.cs +++ b/src/Agent/NewRelic/Agent/Core/Segments/DatastoreSegmentData.cs @@ -31,7 +31,6 @@ public class DatastoreSegmentData : AbstractSegmentData, IDatastoreSegmentData public DatastoreVendor DatastoreVendorName => _parsedSqlStatement.DatastoreVendor; public string Model => _parsedSqlStatement.Model; public string CommandText { get; set; } - public string Vendor => _connectionInfo.Vendor; public string Host => _connectionInfo.Host; public int? Port => _connectionInfo.Port; public string PathOrId => _connectionInfo.PathOrId; @@ -222,7 +221,7 @@ public override void SetSpanTypeSpecificAttributes(SpanAttributeValueCollection AttribDefs.DbCollection.TrySetValue(attribVals, _parsedSqlStatement.Model); } - AttribDefs.DbSystem.TrySetValue(attribVals, Vendor); + AttribDefs.DbSystem.TrySetValue(attribVals, DatastoreVendorName.ToKnownName()); AttribDefs.DbInstance.TrySetValue(attribVals, DatabaseName); AttribDefs.DbOperation.TrySetValue(attribVals, Operation); AttribDefs.PeerAddress.TrySetValue(attribVals, $"{Host}:{PortPathOrId}"); diff --git a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/IbmDb2ConnectionStringParser.cs b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/IbmDb2ConnectionStringParser.cs index 8cf3314aaf..9bca8cc810 100644 --- a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/IbmDb2ConnectionStringParser.cs +++ b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/IbmDb2ConnectionStringParser.cs @@ -27,7 +27,7 @@ public ConnectionInfo GetConnectionInfo(string utilizationHostName) var portPathOrId = ParsePortPathOrId(); var databaseName = ConnectionStringParserHelper.GetKeyValuePair(_connectionStringBuilder, _databaseNameKeys)?.Value; - return new ConnectionInfo(DatastoreVendor.IBMDB2.ToKnownName(), host, portPathOrId, databaseName); + return new ConnectionInfo(host, portPathOrId, databaseName); } private string ParseHost() diff --git a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/MsSqlConnectionStringParser.cs b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/MsSqlConnectionStringParser.cs index 953acdce3d..0dd99fb99c 100644 --- a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/MsSqlConnectionStringParser.cs +++ b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/MsSqlConnectionStringParser.cs @@ -27,7 +27,7 @@ public ConnectionInfo GetConnectionInfo(string utilizationHostName) var portPathOrId = ParsePortPathOrId(); var databaseName = ConnectionStringParserHelper.GetKeyValuePair(_connectionStringBuilder, _databaseNameKeys)?.Value; var instanceName = ParseInstanceName(); - return new ConnectionInfo(DatastoreVendor.MySQL.ToKnownName(), host, portPathOrId, databaseName, instanceName); + return new ConnectionInfo(host, portPathOrId, databaseName, instanceName); } private string ParseHost() diff --git a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/MySqlConnectionStringParser.cs b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/MySqlConnectionStringParser.cs index 9eac69b940..39079ea679 100644 --- a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/MySqlConnectionStringParser.cs +++ b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/MySqlConnectionStringParser.cs @@ -35,7 +35,7 @@ public ConnectionInfo GetConnectionInfo(string utilizationHostName) var port = ConnectionStringParserHelper.GetKeyValuePair(_connectionStringBuilder, _portKeys)?.Value; if (port == null && host != null) { - return new ConnectionInfo(DatastoreVendor.MySQL.ToKnownName(), host, "default", databaseName); + return new ConnectionInfo(host, "default", databaseName); } else { @@ -44,7 +44,7 @@ public ConnectionInfo GetConnectionInfo(string utilizationHostName) { portNum = -1; } - return new ConnectionInfo(DatastoreVendor.MySQL.ToKnownName(), host, portNum, databaseName); + return new ConnectionInfo(host, portNum, databaseName); } diff --git a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/OracleConnectionStringParser.cs b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/OracleConnectionStringParser.cs index 7d85b66da7..76294180a0 100644 --- a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/OracleConnectionStringParser.cs +++ b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/OracleConnectionStringParser.cs @@ -29,14 +29,14 @@ public ConnectionInfo GetConnectionInfo(string utilizationHostName) var portStr = ParsePortString(); if (string.IsNullOrEmpty(portStr)) { - return new ConnectionInfo(DatastoreVendor.Oracle.ToKnownName(), host, "default", null); + return new ConnectionInfo(host, "default", null); } int port; if (!int.TryParse(portStr, out port)) { port = -1; } - return new ConnectionInfo(DatastoreVendor.Oracle.ToKnownName(), host, port, null); + return new ConnectionInfo(host, port, null); } private string ParseHost() diff --git a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/PostgresConnectionStringParser.cs b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/PostgresConnectionStringParser.cs index e50094fde5..f9029981e1 100644 --- a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/PostgresConnectionStringParser.cs +++ b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/PostgresConnectionStringParser.cs @@ -35,7 +35,7 @@ public ConnectionInfo GetConnectionInfo(string utilizationHostName) portNum = -1; } - return new ConnectionInfo(DatastoreVendor.Postgres.ToKnownName(), host, portNum, databaseName); + return new ConnectionInfo(host, portNum, databaseName); } } } diff --git a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/StackExchangeRedisConnectionStringParser.cs b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/StackExchangeRedisConnectionStringParser.cs index bb79178666..cdae23860d 100644 --- a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/StackExchangeRedisConnectionStringParser.cs +++ b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/ConnectionString/StackExchangeRedisConnectionStringParser.cs @@ -44,7 +44,7 @@ public ConnectionInfo GetConnectionInfo(string utilizationHostName) { portNum = -1; } - return new ConnectionInfo(DatastoreVendor.Redis.ToKnownName(), ConnectionStringParserHelper.NormalizeHostname(hostPortPair[0], utilizationHostName), portNum, null); + return new ConnectionInfo(ConnectionStringParserHelper.NormalizeHostname(hostPortPair[0], utilizationHostName), portNum, null); } return new ConnectionInfo(null, null, null, null); diff --git a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/IConnectionInfo.cs b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/IConnectionInfo.cs index 7b1a4bbc3e..27062a25df 100644 --- a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/IConnectionInfo.cs +++ b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/IConnectionInfo.cs @@ -5,9 +5,8 @@ namespace NewRelic.Agent.Extensions.Parsing { public class ConnectionInfo { - public ConnectionInfo(string vendor, string host, int port, string databaseName, string instanceName = null) + public ConnectionInfo(string host, int port, string databaseName, string instanceName = null) { - Vendor = vendor; Host = ValueOrUnknown(host); if (port >= 0) { @@ -18,9 +17,8 @@ public ConnectionInfo(string vendor, string host, int port, string databaseName, InstanceName = instanceName; } - public ConnectionInfo(string vendor, string host, string pathOrId, string databaseName, string instanceName = null) + public ConnectionInfo(string host, string pathOrId, string databaseName, string instanceName = null) { - Vendor = vendor; Host = ValueOrUnknown(host); Port = null; PathOrId = ValueOrUnknown(pathOrId); @@ -33,7 +31,6 @@ private static string ValueOrUnknown(string value) return string.IsNullOrEmpty(value) ? "unknown" : value; } - public string Vendor { get; private set; } public string Host { get; private set; } public string PortPathOrId { get => (Port != null) ? Port.ToString() : PathOrId; } public int? Port { get; private set; } = null; diff --git a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/StringsHelper.cs b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/StringsHelper.cs index 30d632680e..83e3dff3ec 100644 --- a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/StringsHelper.cs +++ b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Parsing/StringsHelper.cs @@ -119,5 +119,34 @@ public static string RemoveBracketsQuotesParenthesis(string value) return value; } + + public static string ToSnakeCase(this string text) + { + if (text == null) + { + throw new ArgumentNullException(nameof(text)); + } + if (text.Length < 2) + { + return text.ToLowerInvariant(); + } + var sb = new StringBuilder(); + sb.Append(char.ToLowerInvariant(text[0])); + for (int i = 1; i < text.Length; ++i) + { + char c = text[i]; + if (char.IsUpper(c)) + { + sb.Append('_'); + sb.Append(char.ToLowerInvariant(c)); + } + else + { + sb.Append(c); + } + } + return sb.ToString(); + } + } } diff --git a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Providers/Wrapper/Constants.cs b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Providers/Wrapper/Constants.cs index dbdbc5e7e4..116c412f80 100644 --- a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Providers/Wrapper/Constants.cs +++ b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Providers/Wrapper/Constants.cs @@ -80,6 +80,7 @@ public enum DatastoreVendor //SQLite, CosmosDB, Elasticsearch, + DynamoDB, Other } diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/AwsSdkPipelineWrapper.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/AwsSdkPipelineWrapper.cs index 14ede37d8f..33c19c9514 100644 --- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/AwsSdkPipelineWrapper.cs +++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/AwsSdkPipelineWrapper.cs @@ -54,6 +54,10 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins { return SQSRequestHandler.HandleSQSRequest(instrumentedMethodCall, agent, transaction, request, isAsync, executionContext); } + else if (requestType.StartsWith("Amazon.DynamoDBv2")) + { + return DynamoDbRequestHandler.HandleDynamoDbRequest(instrumentedMethodCall, agent, transaction, request, isAsync, executionContext); + } if (!_unsupportedRequestTypes.Contains(requestType)) // log once per unsupported request type { diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/DynamoDbRequestHandler.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/DynamoDbRequestHandler.cs new file mode 100644 index 0000000000..023ee2281e --- /dev/null +++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/DynamoDbRequestHandler.cs @@ -0,0 +1,44 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +using System.Collections.Concurrent; +using System.Threading.Tasks; +using NewRelic.Agent.Api; +using NewRelic.Agent.Extensions.Parsing; +using NewRelic.Agent.Extensions.Providers.Wrapper; + +namespace NewRelic.Providers.Wrapper.AwsSdk +{ + internal static class DynamoDbRequestHandler + { + + private static ConcurrentDictionary _operationNameCache = new ConcurrentDictionary(); + + public static AfterWrappedMethodDelegate HandleDynamoDbRequest(InstrumentedMethodCall instrumentedMethodCall, IAgent agent, ITransaction transaction, dynamic request, bool isAsync, dynamic executionContext) + { + var requestType = request.GetType().Name as string; + + string model; + string operation; + + // PutItemRequest => put_item, + // CreateTableRequest => create_table, etc. + operation = _operationNameCache.GetOrAdd(requestType, GetOperationNameFromRequestType); + + // Even though there is no common interface they all implement, every Request type I checked + // has a TableName property + model = request.TableName; + + var segment = transaction.StartDatastoreSegment(instrumentedMethodCall.MethodCall, new ParsedSqlStatement(DatastoreVendor.DynamoDB, model, operation), isLeaf: true); + return isAsync ? + Delegates.GetAsyncDelegateFor(agent, segment) + : + Delegates.GetDelegateFor(segment); + } + + private static string GetOperationNameFromRequestType(string requestType) + { + return requestType.Replace("Request", string.Empty).ToSnakeCase(); + } + } +} diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/CosmosDb/ExecuteItemQueryAsyncWrapper.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/CosmosDb/ExecuteItemQueryAsyncWrapper.cs index d93ab837ad..57d38b613e 100644 --- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/CosmosDb/ExecuteItemQueryAsyncWrapper.cs +++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/CosmosDb/ExecuteItemQueryAsyncWrapper.cs @@ -1,4 +1,4 @@ -// Copyright 2020 New Relic, Inc. All rights reserved. +// Copyright 2020 New Relic, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 using System; @@ -78,7 +78,7 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins var segment = transaction.StartDatastoreSegment( instrumentedMethodCall.MethodCall, new ParsedSqlStatement(DatastoreVendor.CosmosDB, model, operation), - connectionInfo: endpoint != null ? new ConnectionInfo(DatastoreVendor.CosmosDB.ToKnownName(), endpoint.Host, endpoint.Port, databaseName) : new ConnectionInfo(string.Empty, string.Empty, string.Empty, databaseName), + connectionInfo: endpoint != null ? new ConnectionInfo(endpoint.Host, endpoint.Port, databaseName) : new ConnectionInfo(string.Empty, string.Empty, string.Empty, databaseName), commandText : querySpec != null ? _queryGetter.Invoke(querySpec) : string.Empty, isLeaf: true); diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/CosmosDb/RequestInvokerHandlerWrapper.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/CosmosDb/RequestInvokerHandlerWrapper.cs index 2f081674c1..8ae4c9e4d7 100644 --- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/CosmosDb/RequestInvokerHandlerWrapper.cs +++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/CosmosDb/RequestInvokerHandlerWrapper.cs @@ -1,4 +1,4 @@ -// Copyright 2020 New Relic, Inc. All rights reserved. +// Copyright 2020 New Relic, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 using System; @@ -67,7 +67,7 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins var segment = transaction.StartDatastoreSegment( instrumentedMethodCall.MethodCall, new ParsedSqlStatement(DatastoreVendor.CosmosDB, model, operation), - connectionInfo: endpoint != null ? new ConnectionInfo(DatastoreVendor.CosmosDB.ToKnownName(), endpoint.Host, endpoint.Port, databaseName) : new ConnectionInfo(string.Empty, string.Empty, string.Empty, databaseName), + connectionInfo: endpoint != null ? new ConnectionInfo(endpoint.Host, endpoint.Port, databaseName) : new ConnectionInfo(string.Empty, string.Empty, string.Empty, databaseName), isLeaf: true); return Delegates.GetAsyncDelegateFor(agent, segment); diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Elasticsearch/RequestWrapper.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Elasticsearch/RequestWrapper.cs index f44c1a7fd7..4ab8296e8b 100644 --- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Elasticsearch/RequestWrapper.cs +++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Elasticsearch/RequestWrapper.cs @@ -68,7 +68,7 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins } var transactionExperimental = transaction.GetExperimentalApi(); - var datastoreSegmentData = transactionExperimental.CreateDatastoreSegmentData(new ParsedSqlStatement(DatastoreVendor.Elasticsearch, model, operation), new ConnectionInfo(DatastoreVendor.Elasticsearch.ToKnownName(), string.Empty, string.Empty, string.Empty), string.Empty, null); + var datastoreSegmentData = transactionExperimental.CreateDatastoreSegmentData(new ParsedSqlStatement(DatastoreVendor.Elasticsearch, model, operation), new ConnectionInfo(string.Empty, string.Empty, string.Empty), string.Empty, null); var segment = transactionExperimental.StartSegment(instrumentedMethodCall.MethodCall); segment.GetExperimentalApi().SetSegmentData(datastoreSegmentData).MakeLeaf(); @@ -271,7 +271,7 @@ private static void SetUriOnDatastoreSegment(ISegment segment, Uri uri) { var segmentExperimentalApi = segment.GetExperimentalApi(); var data = segmentExperimentalApi.SegmentData as IDatastoreSegmentData; - data.SetConnectionInfo(new ConnectionInfo(DatastoreVendor.Elasticsearch.ToKnownName(), uri.Host, uri.Port, string.Empty)); + data.SetConnectionInfo(new ConnectionInfo(uri.Host, uri.Port, string.Empty)); segmentExperimentalApi.SetSegmentData(data); } diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Memcached/MemcachedHelpers.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Memcached/MemcachedHelpers.cs index 1411d438f4..2252777742 100644 --- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Memcached/MemcachedHelpers.cs +++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Memcached/MemcachedHelpers.cs @@ -4,7 +4,6 @@ using System; using NewRelic.Agent.Api; using NewRelic.Agent.Extensions.Parsing; -using NewRelic.Agent.Extensions.Providers.Wrapper; using NewRelic.Reflection; namespace NewRelic.Providers.Wrapper.Memcached @@ -27,7 +26,7 @@ public static ConnectionInfo GetConnectionInfo(string key, object target, IAgent { if (_hasGetServerFailed) { - return new ConnectionInfo(DatastoreVendor.Memcached.ToKnownName(), null, -1, null); + return new ConnectionInfo(null, -1, null); } try @@ -67,13 +66,13 @@ public static ConnectionInfo GetConnectionInfo(string key, object target, IAgent _portGetter ??= VisibilityBypasser.Instance.GeneratePropertyAccessor(endpointType, "Port"); int? port = _portGetter(endpoint); - return new ConnectionInfo(DatastoreVendor.Memcached.ToKnownName(), address, port.HasValue ? port.Value : -1, null); + return new ConnectionInfo(address, port.HasValue ? port.Value : -1, null); } catch (Exception exception) { agent.Logger.Warn(exception, "Unable to get Memcached server address/port, likely to due to type differences. Server address/port will not be available."); _hasGetServerFailed = true; - return new ConnectionInfo(DatastoreVendor.Memcached.ToKnownName(), null, -1, null); + return new ConnectionInfo(null, -1, null); } } } diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MongoDb26/MongoDbHelper.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MongoDb26/MongoDbHelper.cs index fda21adc02..d7a24943c1 100644 --- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MongoDb26/MongoDbHelper.cs +++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MongoDb26/MongoDbHelper.cs @@ -128,7 +128,7 @@ public static ConnectionInfo GetConnectionInfoFromCursor(object asyncCursor, obj var databaseName = GetDatabaseNameFromCollectionNamespace(collectionNamespace); - return new ConnectionInfo(DatastoreVendor.MongoDB.ToKnownName(), host, port, databaseName); + return new ConnectionInfo(host, port, databaseName); } public static ConnectionInfo GetConnectionInfoFromDatabase(object database, string utilizationHostName) @@ -146,7 +146,7 @@ public static ConnectionInfo GetConnectionInfoFromDatabase(object database, stri host = ConnectionStringParserHelper.NormalizeHostname(rawHost, utilizationHostName); } - return new ConnectionInfo(DatastoreVendor.MongoDB.ToKnownName(), host, port, databaseName); + return new ConnectionInfo(host, port, databaseName); } private static IList GetServersFromDatabase(object database) diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/ServiceStackRedis/SendCommandWrapper.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/ServiceStackRedis/SendCommandWrapper.cs index c51be5c3b4..6ec86114c9 100644 --- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/ServiceStackRedis/SendCommandWrapper.cs +++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/ServiceStackRedis/SendCommandWrapper.cs @@ -82,7 +82,7 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins portNum = -1; } var databaseName = TryGetPropertyName(PropertyDatabaseName, contextObject); - var connectionInfo = new ConnectionInfo(DatastoreVendor.Redis.ToKnownName(), host, portNum, databaseName); + var connectionInfo = new ConnectionInfo(host, portNum, databaseName); var segment = transaction.StartDatastoreSegment(instrumentedMethodCall.MethodCall, ParsedSqlStatement.FromOperation(DatastoreVendor.Redis, operation), connectionInfo); diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/StackExchangeRedis/Common.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/StackExchangeRedis/Common.cs index b840c9064b..fc9a1ba478 100644 --- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/StackExchangeRedis/Common.cs +++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/StackExchangeRedis/Common.cs @@ -122,7 +122,7 @@ public static ConnectionInfo GetConnectionInfoFromConnectionMultiplexer(MethodCa return null; } - return new ConnectionInfo(DatastoreVendor.Redis.ToKnownName(), host, port, null, null); + return new ConnectionInfo(host, port, null, null); } private static string GetCommandNameFromEnumValue(Enum commandValue) diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/StackExchangeRedis2Plus/SessionCache.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/StackExchangeRedis2Plus/SessionCache.cs index 7e37c1a373..91f8109119 100644 --- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/StackExchangeRedis2Plus/SessionCache.cs +++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/StackExchangeRedis2Plus/SessionCache.cs @@ -121,14 +121,14 @@ private ConnectionInfo GetConnectionInfo(EndPoint endpoint) { var port = dnsEndpoint.Port; var host = ConnectionStringParserHelper.NormalizeHostname(dnsEndpoint.Host, _agent.Configuration.UtilizationHostName); - return new ConnectionInfo(DatastoreVendor.Redis.ToKnownName(), host, port, null, null); + return new ConnectionInfo(host, port, null, null); } if (endpoint is IPEndPoint ipEndpoint) { var port = ipEndpoint.Port; var host = ConnectionStringParserHelper.NormalizeHostname(ipEndpoint.Address.ToString(), _agent.Configuration.UtilizationHostName); - return new ConnectionInfo(DatastoreVendor.Redis.ToKnownName(), host, port, null, null); + return new ConnectionInfo(host, port, null, null); } return null; diff --git a/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExerciser/AwsSdkTestType.cs b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExerciser/AwsSdkTestType.cs deleted file mode 100644 index 847a2a9581..0000000000 --- a/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExerciser/AwsSdkTestType.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2020 New Relic, Inc. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -namespace AwsSdkTestApp.AwsSdkExerciser; - -public enum AwsSdkTestType -{ - SQS, - SNS, - SES, - // etc -} diff --git a/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExercisers/AwsSdkDynamoDBExerciser.cs b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExercisers/AwsSdkDynamoDBExerciser.cs new file mode 100644 index 0000000000..e07fe0aa84 --- /dev/null +++ b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExercisers/AwsSdkDynamoDBExerciser.cs @@ -0,0 +1,250 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +using Amazon.DynamoDBv2.Model; +using Amazon.DynamoDBv2; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using System; +using System.Collections.Generic; +using Amazon.Runtime; +using System.Threading; + +namespace AwsSdkTestApp.AwsSdkExercisers +{ + public class AwsSdkDynamoDBExerciser : IDisposable + { + private readonly AmazonDynamoDBClient _amazonDynamoDBClient; + + public AwsSdkDynamoDBExerciser() + { + _amazonDynamoDBClient = GetDynamoDBClient(); + } + + + private AmazonDynamoDBClient GetDynamoDBClient() + { + + AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig(); + // Set the endpoint URL + clientConfig.ServiceURL = "http://dynamodb:8000"; // port must match what is set in docker compose + clientConfig.AuthenticationRegion = "us-west-2"; + var creds = new BasicAWSCredentials("xxx", "xxx"); + AmazonDynamoDBClient client = new AmazonDynamoDBClient(creds, clientConfig); + + return client; + } + + #region Table Operations + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + public async Task CreateTableAsync(string name) + { + var response = await _amazonDynamoDBClient.CreateTableAsync(new CreateTableRequest + { + TableName = name, + AttributeDefinitions = new List() + { + new AttributeDefinition + { + AttributeName = "title", + AttributeType = ScalarAttributeType.S, + }, + new AttributeDefinition + { + AttributeName = "year", + AttributeType = ScalarAttributeType.N, + }, + }, + KeySchema = new List() + { + new KeySchemaElement + { + AttributeName = "year", + KeyType = KeyType.HASH, + }, + new KeySchemaElement + { + AttributeName = "title", + KeyType = KeyType.RANGE, + }, + }, + ProvisionedThroughput = new ProvisionedThroughput + { + ReadCapacityUnits = 5, + WriteCapacityUnits = 5, + }, + }); + if (response.HttpStatusCode != System.Net.HttpStatusCode.OK) + { + Console.WriteLine($"Got bad http status code: {response.HttpStatusCode}"); + } + + // Wait until the table is ACTIVE and then report success. + Console.Write("Waiting for table to become active..."); + + var request = new DescribeTableRequest + { + TableName = response.TableDescription.TableName, + }; + + TableStatus status; + + int sleepDuration = 2000; + + var startTime = DateTime.Now; + do + { + await Task.Delay(sleepDuration); + + var describeTableResponse = await _amazonDynamoDBClient.DescribeTableAsync(request); + status = describeTableResponse.Table.TableStatus; + + Console.Write("."); + } + while (status != "ACTIVE" && DateTime.Now - startTime < TimeSpan.FromMinutes(2)); + + return status == TableStatus.ACTIVE; + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + public async Task DeleteTableAsync(string tableName) + { + var request = new DeleteTableRequest + { + TableName = tableName + }; + var response = await _amazonDynamoDBClient.DeleteTableAsync(request); + + return response.HttpStatusCode == System.Net.HttpStatusCode.OK; + } + #endregion + + #region CRUD operations + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + public async Task PutItemAsync(string tableName, string title, string year) + { + var item = new Dictionary + { + ["title"] = new AttributeValue { S = title }, + ["year"] = new AttributeValue { N = year }, + }; + + var request = new PutItemRequest + { + TableName = tableName, + Item = item, + }; + + var response = await _amazonDynamoDBClient.PutItemAsync(request); + return response.HttpStatusCode == System.Net.HttpStatusCode.OK; + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + public async Task GetItemAsync(string tableName, string title, string year) + { + var request = new GetItemRequest + { + TableName = tableName, + Key = new Dictionary() + { { "title", new AttributeValue { S = title } }, + { "year", new AttributeValue { N = year } } + } + }; + var response = await _amazonDynamoDBClient.GetItemAsync(request); + + // Check the response. + var result = response.Item; + + Console.WriteLine($"GetItemAsync: response.Item['year'] == {result["year"]}"); + return response.HttpStatusCode == System.Net.HttpStatusCode.OK; + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + public async Task UpdateItemAsync(string tableName, string title, string year) + { + var request = new UpdateItemRequest + { + TableName = tableName, + Key = new Dictionary() + { { "title", new AttributeValue { S = title } }, + { "year", new AttributeValue { N = year } } + }, + ExpressionAttributeNames = new Dictionary + { + {"#NA", "Rating" } + }, + ExpressionAttributeValues = new Dictionary() + { + { ":new", new AttributeValue { N = "5" } } + }, + UpdateExpression = "SET #NA = :new" + }; + var response = await _amazonDynamoDBClient.UpdateItemAsync(request); + + return response.HttpStatusCode == System.Net.HttpStatusCode.OK; + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + public async Task DeleteItemAsync(string tableName, string title, string year) + { + var request = new DeleteItemRequest + { + TableName = tableName, + Key = new Dictionary() + { { "title", new AttributeValue { S = title } }, + { "year", new AttributeValue { N = year } } + }, + }; + var response = await _amazonDynamoDBClient.DeleteItemAsync(request); + + return response.HttpStatusCode == System.Net.HttpStatusCode.OK; + } + #endregion + + #region Query Operations + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + public async Task QueryAsync(string tableName, string title, string year) + { + var request = new QueryRequest + { + TableName = tableName, + KeyConditionExpression = "#title = :title and #year = :year", + ExpressionAttributeNames = new Dictionary() + { + {"#title", "title" }, + {"#year", "year" } + }, + ExpressionAttributeValues = new Dictionary() + { + {":title", new AttributeValue { S = title } }, + {":year" , new AttributeValue { N = year } } + } + }; + var response = await _amazonDynamoDBClient.QueryAsync(request); + + Console.WriteLine($"QueryAsync: number of item returned = {response.Items.Count}"); + return response.HttpStatusCode == System.Net.HttpStatusCode.OK; + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + public async Task ScanAsync(string tableName) + { + var request = new ScanRequest + { + TableName = tableName, + Limit = 10 + + }; + var response = await _amazonDynamoDBClient.ScanAsync(request); + + Console.WriteLine($"ScanAsync: number of item returned = {response.Items.Count}"); + return response.HttpStatusCode == System.Net.HttpStatusCode.OK; + } + #endregion + + public void Dispose() + { + _amazonDynamoDBClient?.Dispose(); + } + } +} diff --git a/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExerciser/AwsSdkExerciser.cs b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExercisers/AwsSdkSQSExerciser.cs similarity index 93% rename from tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExerciser/AwsSdkExerciser.cs rename to tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExercisers/AwsSdkSQSExerciser.cs index e9b626eefc..ffef934382 100644 --- a/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExerciser/AwsSdkExerciser.cs +++ b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExercisers/AwsSdkSQSExerciser.cs @@ -9,26 +9,19 @@ using System.Linq; using System.Collections.Generic; -namespace AwsSdkTestApp.AwsSdkExerciser +namespace AwsSdkTestApp.AwsSdkExercisers { - public class AwsSdkExerciser : IDisposable + public class AwsSdkSQSExerciser : IDisposable { - public AwsSdkExerciser(AwsSdkTestType testType) - { - switch (testType) - { - case AwsSdkTestType.SQS: - _amazonSqsClient = GetSqsClient(); - break; - default: - throw new ArgumentException("Invalid test type"); - } - } - #region SQS - private readonly AmazonSQSClient _amazonSqsClient; private string _sqsQueueUrl = null; + public AwsSdkSQSExerciser() + { + _amazonSqsClient = GetSqsClient(); + } + + private AmazonSQSClient GetSqsClient() { // configure the client to use LocalStack @@ -190,8 +183,6 @@ public void SQS_SetQueueUrl(string messageQueueUrl) _sqsQueueUrl = messageQueueUrl; } - #endregion - public void Dispose() { _amazonSqsClient?.Dispose(); diff --git a/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkTestApp.csproj b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkTestApp.csproj index 78f2f4f55e..0b57c9eb29 100644 --- a/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkTestApp.csproj +++ b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkTestApp.csproj @@ -15,6 +15,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/Controllers/AwsSdkDynamoDBController.cs b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/Controllers/AwsSdkDynamoDBController.cs new file mode 100644 index 0000000000..8ec8329568 --- /dev/null +++ b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/Controllers/AwsSdkDynamoDBController.cs @@ -0,0 +1,122 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using AwsSdkTestApp.AwsSdkExercisers; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace AwsSdkTestApp.Controllers +{ + [ApiController] + [Route("[controller]")] + public class AwsSdkDynamoDBController : ControllerBase + { + private readonly ILogger _logger; + + public AwsSdkDynamoDBController(ILogger logger) + { + _logger = logger; + + _logger.LogInformation("Created AwsSdkDynamoDBController"); + } + + // GET: /AwsSdkDynamoDB/CreateTableAsync?tableName=tableName + [HttpGet("CreateTableAsync")] + public async Task CreateTableAsync([Required] string tableName) + { + _logger.LogInformation("Starting DynamoDB CreateTableAsync {tableName}", tableName); + + using var awsSdkDynamoDBExerciser = new AwsSdkDynamoDBExerciser(); + + await awsSdkDynamoDBExerciser.CreateTableAsync(tableName); + _logger.LogInformation("Finished CreateTableAsync for {tableName}", tableName); + } + + // GET: /AwsSdkDynamoDB/DeleteTableAsync?tableName=tableName + [HttpGet("DeleteTableAsync")] + public async Task DeleteTableAsync([Required] string tableName) + { + _logger.LogInformation("Starting DynamoDB DeleteTableAsync {tableName}", tableName); + + using var awsSdkDynamoDBExerciser = new AwsSdkDynamoDBExerciser(); + + await awsSdkDynamoDBExerciser.DeleteTableAsync(tableName); + _logger.LogInformation("Finished DeleteTableAsync for {tableName}", tableName); + } + + // GET: /AwsSdkDynamoDB/PutItemAsync?tableName=tableName&title=title&year=year + [HttpGet("PutItemAsync")] + public async Task PutItemAsync([Required] string tableName, string title, string year) + { + _logger.LogInformation("Starting DynamoDB PutItemAsync {tableName} {title} {year}", tableName, title, year); + + using var awsSdkDynamoDBExerciser = new AwsSdkDynamoDBExerciser(); + + await awsSdkDynamoDBExerciser.PutItemAsync(tableName, title, year); + _logger.LogInformation("Finished PutItemAsync for {title} {year}", title, year); + } + + // GET: /AwsSdkDynamoDB/GetItemAsync?tableName=tableName&title=title&year=year + [HttpGet("GetItemAsync")] + public async Task GetItemAsync([Required] string tableName, string title, string year) + { + _logger.LogInformation("Starting DynamoDB GetItemAsync {tableName} {title} {year}", tableName, title, year); + + using var awsSdkDynamoDBExerciser = new AwsSdkDynamoDBExerciser(); + + await awsSdkDynamoDBExerciser.GetItemAsync(tableName, title, year); + _logger.LogInformation("Finished GetItemAsync for {title} {year}", title, year); + } + + // GET: /AwsSdkDynamoDB/UpdateItemAsync?tableName=tableName&title=title&year=year + [HttpGet("UpdateItemAsync")] + public async Task UpdateItemAsync([Required] string tableName, string title, string year) + { + _logger.LogInformation("Starting DynamoDB UpdateItemAsync {tableName} {title} {year}", tableName, title, year); + + using var awsSdkDynamoDBExerciser = new AwsSdkDynamoDBExerciser(); + + await awsSdkDynamoDBExerciser.UpdateItemAsync(tableName, title, year); + _logger.LogInformation("Finished UpdateItemAsync for {title} {year}", title, year); + } + + // GET: /AwsSdkDynamoDB/DeleteItemAsync?tableName=tableName&title=title&year=year + [HttpGet("DeleteItemAsync")] + public async Task DeleteItemAsync([Required] string tableName, string title, string year) + { + _logger.LogInformation("Starting DynamoDB DeleteItemAsync {tableName} {title} {year}", tableName, title, year); + + using var awsSdkDynamoDBExerciser = new AwsSdkDynamoDBExerciser(); + + await awsSdkDynamoDBExerciser.DeleteItemAsync(tableName, title, year); + _logger.LogInformation("Finished DeleteItemAsync for {title} {year}", title, year); + } + + // GET: /AwsSdkDynamoDB/QueryAsync?tableName=tableName&title=title&year=year + [HttpGet("QueryAsync")] + public async Task QueryAsync([Required] string tableName, string title, string year) + { + _logger.LogInformation("Starting DynamoDB QueryAsync {tableName} {title} {year}", tableName, title, year); + + using var awsSdkDynamoDBExerciser = new AwsSdkDynamoDBExerciser(); + + await awsSdkDynamoDBExerciser.QueryAsync(tableName, title, year); + _logger.LogInformation("Finished QueryAsync for {title} {year}", title, year); + } + + // GET: /AwsSdkDynamoDB/ScanAsync?tableName=tableName + [HttpGet("ScanAsync")] + public async Task ScanAsync([Required] string tableName) + { + _logger.LogInformation("Starting DynamoDB ScanAsync {tableName}", tableName); + + using var awsSdkDynamoDBExerciser = new AwsSdkDynamoDBExerciser(); + + await awsSdkDynamoDBExerciser.ScanAsync(tableName); + _logger.LogInformation("Finished ScanAsync for {tableName}", tableName); + } + + } +} diff --git a/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/Controllers/AwsSdkController.cs b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/Controllers/AwsSdkSQSController.cs similarity index 70% rename from tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/Controllers/AwsSdkController.cs rename to tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/Controllers/AwsSdkSQSController.cs index 9fab3c712f..fce3d02d2d 100644 --- a/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/Controllers/AwsSdkController.cs +++ b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/Controllers/AwsSdkSQSController.cs @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Amazon.SQS.Model; -using AwsSdkTestApp.AwsSdkExerciser; +using AwsSdkTestApp.AwsSdkExercisers; using AwsSdkTestApp.SQSBackgroundService; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; @@ -15,13 +15,13 @@ namespace AwsSdkTestApp.Controllers { [ApiController] [Route("[controller]")] - public class AwsSdkController : ControllerBase + public class AwsSdkSQSController : ControllerBase { - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly ISQSRequestQueue _requestQueue; private readonly ISQSResponseQueue _responseQueue; - public AwsSdkController(ILogger logger, ISQSRequestQueue requestQueue, ISQSResponseQueue responseQueue) + public AwsSdkSQSController(ILogger logger, ISQSRequestQueue requestQueue, ISQSResponseQueue responseQueue) { _logger = logger; _requestQueue = requestQueue; @@ -36,20 +36,20 @@ public async Task SQS_SendReceivePurgeAsync([Required]string queueName) { _logger.LogInformation("Starting SQS_SendReceivePurge for {Queue}", queueName); - using var awsSdkExerciser = new AwsSdkExerciser.AwsSdkExerciser(AwsSdkTestType.SQS); + using var awsSdkSQSExerciser = new AwsSdkSQSExerciser(); - await awsSdkExerciser.SQS_InitializeAsync(queueName); + await awsSdkSQSExerciser.SQS_InitializeAsync(queueName); - await awsSdkExerciser.SQS_SendMessageAsync("Hello World!"); - await awsSdkExerciser.SQS_ReceiveMessageAsync(); + await awsSdkSQSExerciser.SQS_SendMessageAsync("Hello World!"); + await awsSdkSQSExerciser.SQS_ReceiveMessageAsync(); var messages = new[] { "Hello", "World" }; - await awsSdkExerciser.SQS_SendMessageBatchAsync(messages); - await awsSdkExerciser.SQS_ReceiveMessageAsync(messages.Length); + await awsSdkSQSExerciser.SQS_SendMessageBatchAsync(messages); + await awsSdkSQSExerciser.SQS_ReceiveMessageAsync(messages.Length); - await awsSdkExerciser.SQS_PurgeQueueAsync(); + await awsSdkSQSExerciser.SQS_PurgeQueueAsync(); - await awsSdkExerciser.SQS_TeardownAsync(); + await awsSdkSQSExerciser.SQS_TeardownAsync(); _logger.LogInformation("Finished SQS_SendReceivePurge for {Queue}", queueName); } @@ -64,8 +64,8 @@ public async Task SQS_SendReceivePurgeAsync([Required]string queueName) public async Task SQS_InitializeQueueAsync([Required]string queueName) { _logger.LogInformation("Initializing queue {Queue}", queueName); - using var awsSdkExerciser = new AwsSdkExerciser.AwsSdkExerciser(AwsSdkTestType.SQS); - var queueUrl = await awsSdkExerciser.SQS_InitializeAsync(queueName); + using var awsSdkSQSExerciser = new AwsSdkSQSExerciser(); + var queueUrl = await awsSdkSQSExerciser.SQS_InitializeAsync(queueName); _logger.LogInformation("Queue {Queue} initialized with URL {QueueUrl}", queueName, queueUrl); return queueUrl; } @@ -75,10 +75,10 @@ public async Task SQS_InitializeQueueAsync([Required]string queueName) public async Task SQS_SendMessageToQueueAsync([Required]string message, [Required]string messageQueueUrl) { _logger.LogInformation("Sending message {Message} to {Queue}", message, messageQueueUrl); - using var awsSdkExerciser = new AwsSdkExerciser.AwsSdkExerciser(AwsSdkTestType.SQS); - awsSdkExerciser.SQS_SetQueueUrl(messageQueueUrl); + using var awsSdkSQSExerciser = new AwsSdkSQSExerciser(); + awsSdkSQSExerciser.SQS_SetQueueUrl(messageQueueUrl); - await awsSdkExerciser.SQS_SendMessageAsync(message); + await awsSdkSQSExerciser.SQS_SendMessageAsync(message); _logger.LogInformation("Message {Message} sent to {Queue}", message, messageQueueUrl); } @@ -99,10 +99,10 @@ public async Task> SQS_ReceiveMessageFromQueueAsync([Requir public async Task SQS_DeleteQueueAsync([Required]string messageQueueUrl) { _logger.LogInformation("Deleting queue {Queue}", messageQueueUrl); - using var awsSdkExerciser = new AwsSdkExerciser.AwsSdkExerciser(AwsSdkTestType.SQS); - awsSdkExerciser.SQS_SetQueueUrl(messageQueueUrl); + using var awsSdkSQSExerciser = new AwsSdkSQSExerciser(); + awsSdkSQSExerciser.SQS_SetQueueUrl(messageQueueUrl); - await awsSdkExerciser.SQS_TeardownAsync(); + await awsSdkSQSExerciser.SQS_TeardownAsync(); _logger.LogInformation("Queue {Queue} deleted", messageQueueUrl); } } diff --git a/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/SQSBackgroundService/SQSReceiverService.cs b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/SQSBackgroundService/SQSReceiverService.cs index 5939beccdc..7e0ec60ce8 100644 --- a/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/SQSBackgroundService/SQSReceiverService.cs +++ b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/SQSBackgroundService/SQSReceiverService.cs @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Amazon.SQS.Model; -using AwsSdkTestApp.AwsSdkExerciser; +using AwsSdkTestApp.AwsSdkExercisers; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using NewRelic.Api.Agent; @@ -56,10 +56,10 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) private async Task> ProcessRequestAsync(string queueUrl) { _logger.LogInformation("Received a request to receive a message from {Queue}", queueUrl); - using var awsSdkExerciser = new AwsSdkExerciser.AwsSdkExerciser(AwsSdkTestType.SQS); - awsSdkExerciser.SQS_SetQueueUrl(queueUrl); + using var awsSdkSQSExerciser = new AwsSdkSQSExerciser(); + awsSdkSQSExerciser.SQS_SetQueueUrl(queueUrl); _logger.LogInformation("Receiving a message from {Queue}", queueUrl); - var messages = await awsSdkExerciser.SQS_ReceiveMessageAsync(); + var messages = await awsSdkSQSExerciser.SQS_ReceiveMessageAsync(); _logger.LogInformation("Received a message from {Queue}; queuing a response", queueUrl); await _responseQueue.QueueResponseAsync(messages); _logger.LogInformation("Finished processing request for {Queue}", queueUrl); diff --git a/tests/Agent/IntegrationTests/ContainerApplications/docker-compose-awssdk.yml b/tests/Agent/IntegrationTests/ContainerApplications/docker-compose-awssdk.yml index 06ebdb1a0d..a3873cf8d0 100644 --- a/tests/Agent/IntegrationTests/ContainerApplications/docker-compose-awssdk.yml +++ b/tests/Agent/IntegrationTests/ContainerApplications/docker-compose-awssdk.yml @@ -33,10 +33,18 @@ services: volumes: - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack" - "/var/run/docker.sock:/var/run/docker.sock" + + dynamodb: + command: "-jar DynamoDBLocal.jar -inMemory" + image: "amazon/dynamodb-local:latest" + expose: # ports are only available internal to the service, not external so there's no chance for conflicts + - "8000" + working_dir: /home/dynamodblocal awssdktestapp: depends_on: - localstack + - dynamodb container_name: ${CONTAINER_NAME} image: ${CONTAINER_NAME} platform: ${PLATFORM} diff --git a/tests/Agent/IntegrationTests/ContainerIntegrationTests.sln b/tests/Agent/IntegrationTests/ContainerIntegrationTests.sln index 6125d5c40b..4bfa52e5a2 100644 --- a/tests/Agent/IntegrationTests/ContainerIntegrationTests.sln +++ b/tests/Agent/IntegrationTests/ContainerIntegrationTests.sln @@ -35,7 +35,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NewRelic.Testing.Assertions EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AwsSdkTestApp", "ContainerApplications\AwsSdkTestApp\AwsSdkTestApp.csproj", "{70731828-AFC8-4262-9076-3FB39E224D10}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MemcachedTestApp", "ContainerApplications\MemcachedTestApp\MemcachedTestApp.csproj", "{3D46F286-A19A-4942-8E3F-8999E953A6F2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MemcachedTestApp", "ContainerApplications\MemcachedTestApp\MemcachedTestApp.csproj", "{3D46F286-A19A-4942-8E3F-8999E953A6F2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestSerializationHelpers", "..\Shared\TestSerializationHelpers\TestSerializationHelpers.csproj", "{C8DF0638-A57D-4F5F-9ED2-E54879685DBC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestSerializationHelpers.Test", "..\Shared\TestSerializationHelpers.Test\TestSerializationHelpers.Test.csproj", "{76A0FABB-B5B2-46B4-8C81-239F1C7E056A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -79,6 +83,14 @@ Global {3D46F286-A19A-4942-8E3F-8999E953A6F2}.Debug|Any CPU.Build.0 = Debug|Any CPU {3D46F286-A19A-4942-8E3F-8999E953A6F2}.Release|Any CPU.ActiveCfg = Release|Any CPU {3D46F286-A19A-4942-8E3F-8999E953A6F2}.Release|Any CPU.Build.0 = Release|Any CPU + {C8DF0638-A57D-4F5F-9ED2-E54879685DBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8DF0638-A57D-4F5F-9ED2-E54879685DBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8DF0638-A57D-4F5F-9ED2-E54879685DBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8DF0638-A57D-4F5F-9ED2-E54879685DBC}.Release|Any CPU.Build.0 = Release|Any CPU + {76A0FABB-B5B2-46B4-8C81-239F1C7E056A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76A0FABB-B5B2-46B4-8C81-239F1C7E056A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76A0FABB-B5B2-46B4-8C81-239F1C7E056A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76A0FABB-B5B2-46B4-8C81-239F1C7E056A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tests/Agent/IntegrationTests/ContainerIntegrationTests/Fixtures/AwsSdkContainerTestFixtures.cs b/tests/Agent/IntegrationTests/ContainerIntegrationTests/Fixtures/AwsSdkContainerTestFixtures.cs index 5de25d69b5..b70159f279 100644 --- a/tests/Agent/IntegrationTests/ContainerIntegrationTests/Fixtures/AwsSdkContainerTestFixtures.cs +++ b/tests/Agent/IntegrationTests/ContainerIntegrationTests/Fixtures/AwsSdkContainerTestFixtures.cs @@ -34,43 +34,92 @@ public class AwsSdkContainerSQSTestFixture : AwsSdkContainerTestFixtureBase private const ContainerApplication.Architecture Architecture = ContainerApplication.Architecture.X64; private const string DistroTag = "jammy"; - public AwsSdkContainerSQSTestFixture() : base(DistroTag, Architecture, Dockerfile) { } + private readonly string BaseUrl; - public void ExerciseSQS_SendReceivePurge(string queueName) + public AwsSdkContainerSQSTestFixture() : base(DistroTag, Architecture, Dockerfile) { - var address = $"http://localhost:{Port}/awssdk"; + BaseUrl = $"http://localhost:{Port}/awssdksqs"; + } + public void ExerciseSQS_SendReceivePurge(string queueName) + { // The exerciser will return a 500 error if the `RequestMessage.MessageAttributeNames` collection is modified by our instrumentation. // See https://github.com/newrelic/newrelic-dotnet-agent/pull/2646 - GetAndAssertStatusCode($"{address}/SQS_SendReceivePurge?queueName={queueName}", System.Net.HttpStatusCode.OK); + GetAndAssertStatusCode($"{BaseUrl}/SQS_SendReceivePurge?queueName={queueName}", System.Net.HttpStatusCode.OK); } public string ExerciseSQS_SendAndReceiveInSeparateTransactions(string queueName) { - var address = $"http://localhost:{Port}/awssdk"; - - var queueUrl = GetString($"{address}/SQS_InitializeQueue?queueName={queueName}"); + var queueUrl = GetString($"{BaseUrl}/SQS_InitializeQueue?queueName={queueName}"); - GetAndAssertStatusCode($"{address}/SQS_SendMessageToQueue?message=Hello&messageQueueUrl={queueUrl}", System.Net.HttpStatusCode.OK); + GetAndAssertStatusCode($"{BaseUrl}/SQS_SendMessageToQueue?message=Hello&messageQueueUrl={queueUrl}", System.Net.HttpStatusCode.OK); - var messagesJson = GetString($"{address}/SQS_ReceiveMessageFromQueue?messageQueueUrl={queueUrl}"); + var messagesJson = GetString($"{BaseUrl}/SQS_ReceiveMessageFromQueue?messageQueueUrl={queueUrl}"); - GetAndAssertStatusCode($"{address}/SQS_DeleteQueue?messageQueueUrl={queueUrl}", System.Net.HttpStatusCode.OK); + GetAndAssertStatusCode($"{BaseUrl}/SQS_DeleteQueue?messageQueueUrl={queueUrl}", System.Net.HttpStatusCode.OK); return messagesJson; } public string ExerciseSQS_ReceiveEmptyMessage(string queueName) { - var address = $"http://localhost:{Port}/awssdk"; + var queueUrl = GetString($"{BaseUrl}/SQS_InitializeQueue?queueName={queueName}"); - var queueUrl = GetString($"{address}/SQS_InitializeQueue?queueName={queueName}"); + var messagesJson = GetString($"{BaseUrl}/SQS_ReceiveMessageFromQueue?messageQueueUrl={queueUrl}"); - var messagesJson = GetString($"{address}/SQS_ReceiveMessageFromQueue?messageQueueUrl={queueUrl}"); - - GetAndAssertStatusCode($"{address}/SQS_DeleteQueue?messageQueueUrl={queueUrl}", System.Net.HttpStatusCode.OK); + GetAndAssertStatusCode($"{BaseUrl}/SQS_DeleteQueue?messageQueueUrl={queueUrl}", System.Net.HttpStatusCode.OK); return messagesJson; } } + +public class AwsSdkContainerDynamoDBTestFixture : AwsSdkContainerTestFixtureBase +{ + private const string Dockerfile = "AwsSdkTestApp/Dockerfile"; + private const ContainerApplication.Architecture Architecture = ContainerApplication.Architecture.X64; + private const string DistroTag = "jammy"; + + private readonly string BaseUrl; + + public AwsSdkContainerDynamoDBTestFixture() : base(DistroTag, Architecture, Dockerfile) + { + BaseUrl = $"http://localhost:{Port}/awssdkdynamodb"; + } + + public void CreateTableAsync(string tableName) + { + GetAndAssertStatusCode($"{BaseUrl}/CreateTableAsync?tableName={tableName}", System.Net.HttpStatusCode.OK); + } + public void DeleteTableAsync(string tableName) + { + GetAndAssertStatusCode($"{BaseUrl}/DeleteTableAsync?tableName={tableName}", System.Net.HttpStatusCode.OK); + } + + public void PutItemAsync(string tableName, string title, string year) + { + GetAndAssertStatusCode($"{BaseUrl}/PutItemAsync?tableName={tableName}&title={title}&year={year}", System.Net.HttpStatusCode.OK); + } + public void GetItemAsync(string tableName, string title, string year) + { + GetAndAssertStatusCode($"{BaseUrl}/GetItemAsync?tableName={tableName}&title={title}&year={year}", System.Net.HttpStatusCode.OK); + } + public void UpdateItemAsync(string tableName, string title, string year) + { + GetAndAssertStatusCode($"{BaseUrl}/UpdateItemAsync?tableName={tableName}&title={title}&year={year}", System.Net.HttpStatusCode.OK); + } + + public void DeleteItemAsync(string tableName, string title, string year) + { + GetAndAssertStatusCode($"{BaseUrl}/DeleteItemAsync?tableName={tableName}&title={title}&year={year}", System.Net.HttpStatusCode.OK); + } + public void QueryAsync(string tableName, string title, string year) + { + GetAndAssertStatusCode($"{BaseUrl}/QueryAsync?tableName={tableName}&title={title}&year={year}", System.Net.HttpStatusCode.OK); + } + public void ScanAsync(string tableName) + { + GetAndAssertStatusCode($"{BaseUrl}/ScanAsync?tableName={tableName}", System.Net.HttpStatusCode.OK); + } + +} diff --git a/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkDynamoDBTest.cs b/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkDynamoDBTest.cs new file mode 100644 index 0000000000..c136e83bf7 --- /dev/null +++ b/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkDynamoDBTest.cs @@ -0,0 +1,120 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +using System; +using System.Collections.Generic; +using System.Linq; +using NewRelic.Agent.IntegrationTestHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace NewRelic.Agent.ContainerIntegrationTests.Tests.AwsSdk; + +public abstract class AwsSdkDynamoDBTestBase : NewRelicIntegrationTest +{ + private readonly AwsSdkContainerDynamoDBTestFixture _fixture; + + private readonly string _tableName = $"TestTable-{Guid.NewGuid()}"; + private readonly string _title = "Ghost"; + private readonly string _year = "1990"; + + protected AwsSdkDynamoDBTestBase(AwsSdkContainerDynamoDBTestFixture fixture, ITestOutputHelper output) : base(fixture) + { + _fixture = fixture; + _fixture.TestLogger = output; + + _fixture.Actions(setupConfiguration: () => + { + var configModifier = new NewRelicConfigModifier(_fixture.DestinationNewRelicConfigFilePath); + configModifier.SetLogLevel("finest"); + configModifier.ForceTransactionTraces(); + configModifier.EnableDistributedTrace(); + configModifier.ConfigureFasterMetricsHarvestCycle(15); + configModifier.ConfigureFasterSpanEventsHarvestCycle(15); + configModifier.ConfigureFasterTransactionTracesHarvestCycle(15); + configModifier.LogToConsole(); + + }, + exerciseApplication: () => + { + _fixture.Delay(5); + + _fixture.CreateTableAsync(_tableName); + + _fixture.PutItemAsync(_tableName, _title, _year); + _fixture.GetItemAsync(_tableName, _title, _year); + _fixture.UpdateItemAsync(_tableName, _title, _year); + + _fixture.QueryAsync(_tableName, _title, _year); + _fixture.ScanAsync(_tableName); + + _fixture.DeleteItemAsync(_tableName, _title, _year); + _fixture.DeleteTableAsync(_tableName); + + _fixture.AgentLog.WaitForLogLine(AgentLogBase.MetricDataLogLineRegex, TimeSpan.FromMinutes(2)); + _fixture.AgentLog.WaitForLogLine(AgentLogBase.TransactionTransformCompletedLogLineRegex, TimeSpan.FromMinutes(2)); + + // shut down the container and wait for the agent log to see it + _fixture.ShutdownRemoteApplication(); + _fixture.AgentLog.WaitForLogLine(AgentLogBase.ShutdownLogLineRegex, TimeSpan.FromSeconds(10)); + }); + + _fixture.Initialize(); + } + + + [Fact] + public void Test() + { + Assert.Equal(0, _fixture.AgentLog.GetWrapperExceptionLineCount()); + Assert.Equal(0, _fixture.AgentLog.GetApplicationErrorLineCount()); + + var metrics = _fixture.AgentLog.GetMetrics().ToList(); + + var metricScopeBase = "WebTransaction/MVC/AwsSdkDynamoDB/"; + var createTableScope = metricScopeBase + "CreateTable/{tableName}"; + var scanScope = metricScopeBase + "Scan/{tableName}"; + var deleteTableScope = metricScopeBase + "DeleteTable/{tableName}"; + var putItemScope = metricScopeBase + "PutItem/{tableName}/{title}/{year}"; + var getItemScope = metricScopeBase + "GetItem/{tableName}/{title}/{year}"; + var updateItemScope = metricScopeBase + "UpdateItem/{tableName}/{title}/{year}"; + var deleteItemScope = metricScopeBase + "DeleteItem/{tableName}/{title}/{year}"; + var queryScope = metricScopeBase + "Query/{tableName}/{title}/{year}"; + + var expectedMetrics = new List + { + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/create_table", callCount = 1}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/create_table", callCount = 1, metricScope = createTableScope}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/describe_table", callCount = 1}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/describe_table", callCount = 1, metricScope = createTableScope}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/put_item", callCount = 1}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/put_item", callCount = 1, metricScope = putItemScope}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/get_item", callCount = 1}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/get_item", callCount = 1, metricScope = getItemScope}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/update_item", callCount = 1}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/update_item", callCount = 1, metricScope = updateItemScope}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/delete_item", callCount = 1}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/delete_item", callCount = 1, metricScope = deleteItemScope}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/query", callCount = 1}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/query", callCount = 1, metricScope = queryScope}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/scan", callCount = 1}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/scan", callCount = 1, metricScope = scanScope}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/delete_table", callCount = 1}, + new() { metricName = $"Datastore/statement/DynamoDB/{_tableName}/delete_table", callCount = 1, metricScope = deleteTableScope}, + + }; + + Assertions.MetricsExist(expectedMetrics, metrics); + } +} + +// Base class with derived classes pattern copied from another tests file +// but we currently don't need to use it for anything + +public class AwsSdkDynamoDBTest : AwsSdkDynamoDBTestBase +{ + public AwsSdkDynamoDBTest(AwsSdkContainerDynamoDBTestFixture fixture, ITestOutputHelper output) : base(fixture, output) + { + } +} + diff --git a/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkSQSTest.cs b/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkSQSTest.cs index 73b5a4b13e..edd06af3fb 100644 --- a/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkSQSTest.cs +++ b/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkSQSTest.cs @@ -18,8 +18,8 @@ public abstract class AwsSdkSQSTestBase : NewRelicIntegrationTest queryParameters = null) { methodCall = methodCall ?? GetDefaultMethodCall(agent); - var segment = agent.CurrentTransaction.StartDatastoreSegment(methodCall, new ParsedSqlStatement(vendor, model, operation), new ConnectionInfo(vendor.ToKnownName(), host, portPathOrId, databaseName), commandText, queryParameters); + var segment = agent.CurrentTransaction.StartDatastoreSegment(methodCall, new ParsedSqlStatement(vendor, model, operation), new ConnectionInfo(host, portPathOrId, databaseName), commandText, queryParameters); if (segment == null) throw new NullReferenceException("segment"); @@ -67,7 +67,7 @@ public static ISegment StartStackExchangeRedisDatastoreRequestSegmentOrThrow(thi { methodCall = methodCall ?? GetDefaultMethodCall(agent); var xTransaction = (ITransactionExperimental)agent.CurrentTransaction; - var segment = xTransaction.StartStackExchangeRedisSegment(RuntimeHelpers.GetHashCode(methodCall), ParsedSqlStatement.FromOperation(vendor, operation), new ConnectionInfo(vendor.ToKnownName(), host, portPathOrId, databaseName), relativeStartTime, relativeEndTime); + var segment = xTransaction.StartStackExchangeRedisSegment(RuntimeHelpers.GetHashCode(methodCall), ParsedSqlStatement.FromOperation(vendor, operation), new ConnectionInfo(host, portPathOrId, databaseName), relativeStartTime, relativeEndTime); if (segment == null) throw new NullReferenceException("segment"); diff --git a/tests/Agent/UnitTests/Core.UnitTest/Spans/SpanEventMakerTests.cs b/tests/Agent/UnitTests/Core.UnitTest/Spans/SpanEventMakerTests.cs index 28c8161eff..0fd6e1e378 100644 --- a/tests/Agent/UnitTests/Core.UnitTest/Spans/SpanEventMakerTests.cs +++ b/tests/Agent/UnitTests/Core.UnitTest/Spans/SpanEventMakerTests.cs @@ -177,7 +177,7 @@ public void SetUp() _childGenericSegment.SetSegmentData(new SimpleSegmentData(SegmentName)); // Datastore Segments - _connectionInfo = new ConnectionInfo(DatastoreVendor.MSSQL.ToKnownName(), "localhost", 1234, "default", "maininstance"); + _connectionInfo = new ConnectionInfo("localhost", 1234, "default", "maininstance"); _parsedSqlStatement = SqlParser.GetParsedDatabaseStatement(DatastoreVendor.MSSQL, System.Data.CommandType.Text, ShortQuery); _obfuscatedSql = _databaseService.GetObfuscatedSql(ShortQuery, DatastoreVendor.MSSQL); @@ -648,7 +648,7 @@ public void Do_Not_Generate_DbCollection_Attribute_When_Model_IsNullOrEmpty() var testSegment = new Segment(CreateTransactionSegmentState(3, null, 777), new MethodCallData(MethodCallType, MethodCallMethod, 1)); testSegment.SetSegmentData(new DatastoreSegmentData(_databaseService, parsedSqlStatement: new ParsedSqlStatement(DatastoreVendor.CosmosDB, string.Empty, "ReadDatabase"), - connectionInfo: new ConnectionInfo("none", "localhost", "1234", "default", "maininstance"))); + connectionInfo: new ConnectionInfo("localhost", "1234", "default", "maininstance"))); // ARRANGE var segments = new List() diff --git a/tests/Agent/UnitTests/Core.UnitTest/Transformers/DatastoreSegmentTransformerTests.cs b/tests/Agent/UnitTests/Core.UnitTest/Transformers/DatastoreSegmentTransformerTests.cs index f1f2c4f83e..614740b2fc 100644 --- a/tests/Agent/UnitTests/Core.UnitTest/Transformers/DatastoreSegmentTransformerTests.cs +++ b/tests/Agent/UnitTests/Core.UnitTest/Transformers/DatastoreSegmentTransformerTests.cs @@ -340,7 +340,7 @@ private Segment GetSegment(DatastoreVendor vendor, string operation, string mode private Segment GetSegment(DatastoreVendor vendor, string operation, string model, double duration, CrossApplicationResponseData catResponseData = null, string host = null, string portPathOrId = null) { var methodCallData = new MethodCallData("foo", "bar", 1); - var data = new DatastoreSegmentData(_databaseService, new ParsedSqlStatement(vendor, model, operation), null, new ConnectionInfo("none", host, portPathOrId, null)); + var data = new DatastoreSegmentData(_databaseService, new ParsedSqlStatement(vendor, model, operation), null, new ConnectionInfo(host, portPathOrId, null)); var segment = new Segment(TransactionSegmentStateHelpers.GetItransactionSegmentState(), methodCallData); segment.SetSegmentData(data); diff --git a/tests/Agent/UnitTests/Core.UnitTest/Transformers/TransactionTransformer/SqlTraceMakerTests.cs b/tests/Agent/UnitTests/Core.UnitTest/Transformers/TransactionTransformer/SqlTraceMakerTests.cs index 2b764f87e6..3f48f38ae6 100644 --- a/tests/Agent/UnitTests/Core.UnitTest/Transformers/TransactionTransformer/SqlTraceMakerTests.cs +++ b/tests/Agent/UnitTests/Core.UnitTest/Transformers/TransactionTransformer/SqlTraceMakerTests.cs @@ -165,7 +165,7 @@ private ImmutableTransaction BuildTestTransaction(string uri = null, string guid private Segment BuildSegment(DatastoreVendor vendor, string model, string commandText, TimeSpan startTime = new TimeSpan(), TimeSpan? duration = null, string name = "", MethodCallData methodCallData = null, IEnumerable> parameters = null, string host = null, string portPathOrId = null, string databaseName = null) { var data = new DatastoreSegmentData(_databaseService, new ParsedSqlStatement(vendor, model, null), commandText, - new ConnectionInfo("none", host, portPathOrId, databaseName)); + new ConnectionInfo(host, portPathOrId, databaseName)); methodCallData = methodCallData ?? new MethodCallData("typeName", "methodName", 1); var segment = new Segment(TransactionSegmentStateHelpers.GetItransactionSegmentState(), methodCallData); diff --git a/tests/Agent/UnitTests/Core.UnitTest/Transformers/TransactionTransformer/TestTransactions.cs b/tests/Agent/UnitTests/Core.UnitTest/Transformers/TransactionTransformer/TestTransactions.cs index cc98f9f3f6..2598380bb9 100644 --- a/tests/Agent/UnitTests/Core.UnitTest/Transformers/TransactionTransformer/TestTransactions.cs +++ b/tests/Agent/UnitTests/Core.UnitTest/Transformers/TransactionTransformer/TestTransactions.cs @@ -118,7 +118,7 @@ public static ImmutableTransaction CreateTestTransactionWithSegments(IEnumerable txSegmentState = TransactionSegmentStateHelpers.GetItransactionSegmentState(); methodCallData = methodCallData ?? new MethodCallData("typeName", "methodName", 1); - var data = new DatastoreSegmentData(_databaseService, new ParsedSqlStatement(vendor, model, null), commandText, new ConnectionInfo("none", host, portPathOrId, databaseName)); + var data = new DatastoreSegmentData(_databaseService, new ParsedSqlStatement(vendor, model, null), commandText, new ConnectionInfo(host, portPathOrId, databaseName)); var segment = new Segment(txSegmentState, methodCallData); segment.SetSegmentData(data); diff --git a/tests/Agent/UnitTests/Core.UnitTest/Transformers/TransactionTransformer/TransactionTraceMakerTests.cs b/tests/Agent/UnitTests/Core.UnitTest/Transformers/TransactionTransformer/TransactionTraceMakerTests.cs index 1df4d45378..6f46dd5733 100644 --- a/tests/Agent/UnitTests/Core.UnitTest/Transformers/TransactionTransformer/TransactionTraceMakerTests.cs +++ b/tests/Agent/UnitTests/Core.UnitTest/Transformers/TransactionTransformer/TransactionTraceMakerTests.cs @@ -354,7 +354,7 @@ private static ImmutableSegmentTreeNode BuildNode(ImmutableTransaction transacti var data = new DatastoreSegmentData(_databaseService, new ParsedSqlStatement(DatastoreVendor.MSSQL, "test_table", "SELECT"), "SELECT * FROM test_table", - new ConnectionInfo("My Vendor", "My Host", "My Port", "My Database")); + new ConnectionInfo("My Host", "My Port", "My Database")); var segment = new Segment(TransactionSegmentStateHelpers.GetItransactionSegmentState(), methodCallData); segment.SetSegmentData(data); diff --git a/tests/Agent/UnitTests/NewRelic.Agent.Extensions.Tests/Parsing/StringsHelperTest.cs b/tests/Agent/UnitTests/NewRelic.Agent.Extensions.Tests/Parsing/StringsHelperTest.cs index 72ff4eead0..25deb83b80 100644 --- a/tests/Agent/UnitTests/NewRelic.Agent.Extensions.Tests/Parsing/StringsHelperTest.cs +++ b/tests/Agent/UnitTests/NewRelic.Agent.Extensions.Tests/Parsing/StringsHelperTest.cs @@ -135,6 +135,32 @@ public void validate_CleanUri_String_Version(string uri, string expected) Assert.That(actual, Is.EqualTo(expected)); } + [TestCase("AlphaBeta", "alpha_beta")] + [TestCase("Alpha", "alpha")] + [TestCase("alpha", "alpha")] + [TestCase("alpha_beta", "alpha_beta")] + [TestCase("alphaBeta", "alpha_beta")] + [TestCase("AlphaBetaGamma", "alpha_beta_gamma")] + [TestCase("A", "a")] + [TestCase("", "")] + public void validate_ToSnakeCase(string input, string expected) + { + var actual = input.ToSnakeCase(); + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void validate_ToSnakeCase_nullInput_throwsException() + { + Assert.Throws(TryNullSnakeCase); + } + + private void TryNullSnakeCase() + { + string input = null; + input.ToSnakeCase(); + } + #if NET6_0_OR_GREATER [Test] public void validate_CleanUri_handles_invalidoperationexception()