Repository: newrelic/newrelic-node-apollo-server-plugin (js)

Issue:
Similar metrics on every apollo server request
## Is your feature request related to a problem? Please describe.
In #247 we added `GraphQL/typedResolve/ApolloServer/<parentType>.<fieldName>` to further clarify the resolver names when they were ambiguous.  At the time we decided to keep the `GraphQL/resolve/ApolloServer/<fieldName>`.  You can get what you need from both typedResolve and there may not be a need for the resolve metrics.

## Feature Description
Only create `GraphQL/typedResolve/ApolloServer/<parentType>.<fieldName>`

## Describe Alternatives
We could provide another config to `config.captureTypedResolvers` and when true it will only create the typedResolve metric and when false it would only create the resolve metric.

## Additional context
There was a report from custuomers via GTS of metric explosion after upgrading to a version that has this.  We are still digging into  how much of an increase.  I don't see how it could be more than 1 additional metric per resolver though.


Pull request:
feat!: Removed GraphQL/typedResolve/ApolloServer/<parentType>.<resolver> in lieu of including the <parentType> in GraphQL/resolve/ApolloServer metrics
<!--
Thank you for submitting a Pull Request.

This code is leveraged to monitor critical services. Please consider the following:
* Tests are required.
* Performance matters.
* Features that are specific to just your app are unlikely to make it in.

Please fill out the relevant sections as follows:
* Proposed Release Notes: Bulleted list of recommended release notes for the change(s).
* Links: Any relevant links for the change.
* Details: In-depth description of changes, other technical notes, etc.
-->

## Proposed Release Notes

## Links
Closes #283

## Details
We had complaints about producing very similar metrics.  You can still query what you need with this update that includes the parent type in all resolve metrics you just have to wildcard it.  This is a breaking change and is marked as such.


Patch:
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 30f423b..b8158dc 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -6,22 +6,14 @@ This code is leveraged to monitor critical services. Please consider the followi
 * Performance matters.
 * Features that are specific to just your app are unlikely to make it in.
 
-Ensure that your Pull Request title adheres to our Conventional Commit standards
-as described in CONTRIBUTING.md
-
-Please update the Pull Request description to add relevant context or documentation about
-the submitted change.
+Please fill out the relevant sections as follows:
+* Proposed Release Notes: Bulleted list of recommended release notes for the change(s).
+* Links: Any relevant links for the change.
+* Details: In-depth description of changes, other technical notes, etc.
 -->
-## Description
-
-Please provide a brief description of the changes introduced in this pull request.
-What problem does it solve? What is the context of this change?
-
-## How to Test
 
-Please describe how you have tested these changes. Have you run the code against an example application?
-What steps did you take to ensure that the changes are working correctly?
+## Proposed Release Notes
 
-## Related Issues
+## Links
 
-Please include any related issues or pull requests in this section, using the format `Closes #<issue number>` or `Fixes #<issue number>` if applicable.
\ No newline at end of file
+## Details
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index e78e0f7..0000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-version: 2
-updates:
-  - package-ecosystem: "npm"
-    # Disable version updates for npm dependencies, security updates don't use this configuration
-    # See: https://docs.github.com/en/code-security/dependabot/dependabot-security-updates/configuring-dependabot-security-updates
-    open-pull-requests-limit: 0
-    directory: "/"
-    schedule:
-      interval: "daily"
-    commit-message:
-      prefix: "security"
-      prefix-development: "chore"
-      include: "scope"
\ No newline at end of file
diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml
index f62b504..28a3ef0 100644
--- a/.github/workflows/prepare-release.yml
+++ b/.github/workflows/prepare-release.yml
@@ -6,16 +6,10 @@ on:
       release_type:
         description: Type of release. patch or minor (major if breaking)
         required: true
-        type: choice
         default: patch
-        options:
-          - patch
-          - minor
-          - major
 
 jobs:
   release-notes:
     uses: newrelic/node-newrelic/.github/workflows/prep-release.yml@main
     with:
       release_type: ${{ github.event.inputs.release_type }}
-      use_new_release: ${{ vars.USE_NEW_RELEASE }}
diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml
deleted file mode 100644
index 15c37f6..0000000
--- a/.github/workflows/validate-pr.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-name: Validate Pull Request
-
-permissions:
-  pull-requests: write
-
-on:
-  pull_request:
-    types:
-      - opened
-      - edited
-      - synchronize
-
-jobs:
-  validate-pr:
-    runs-on: ubuntu-latest
-    steps:
-      - name: Pull Request Title is Conventional
-        id: lint_pr_title
-        uses: amannn/action-semantic-pull-request@v5
-        with:
-          # Recommended Prefixes from https://github.com/conventional-changelog/commitlint/blob/master/%40commitlint/config-conventional/README.md#type-enum
-          types: |
-            build
-            chore
-            ci
-            docs
-            feat
-            fix
-            perf
-            refactor
-            revert
-            security
-            style
-            test
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      - if: failure()
-        name: Add PR comment if failed
-        uses: marocchino/sticky-pull-request-comment@v2
-        with:
-          header: pr-title-lint-error
-          message: |
-            Thank you for your contribution! We require all PR titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/).
-            Please update your PR title with the appropriate type and we'll try again!
-            ```
-            ${{ steps.lint_pr_title.outputs.error_message}}
-            ```
-      - if: success()
-        name: Remove PR comment if valid
-        uses: marocchino/sticky-pull-request-comment@v2
-        with:   
-          header: pr-title-lint-error
-          delete: true
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index cc69654..bc4be55 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -53,31 +53,6 @@ If you’re planning on contributing a new feature or an otherwise complex contr
 
 In general, we try to limit adding third-party production dependencies. If one is necessary, please be prepared to make a clear case for the need.
 
-### Commit Guidelines
-
-We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) to format commit messages for this repository. Conventional Commits provide a standardized format for commit messages that allows for automatic generation of changelogs and easy tracking of changes.
-
-When contributing to this repository, please ensure that your commit messages adhere to the Conventional Commit guidelines. Specifically, your commit messages should:
-
-* Start with a type, indicating the kind of change being made (e.g. `feat` for a new feature, `fix` for a bugfix, etc.). The types we support are:
-  * `build`: changes that affect the build system or external dependencies
-  * `chore`: changes that do not modify source or test files
-  * `ci`: changes to our CI configuration files and scripts
-  * `docs`: documentation additions or updates
-  * `feat`: new features or capabilities added to the agent
-  * `fix`: bugfixes or corrections to existing functionality
-  * `perf`: performance improvements
-  * `refactor`: changes that do not add new feature or fix bugs, but improve code structure or readability
-  * `revert`: revert a previous commit
-  * `security`: changes related to the security of the agent, including the updating of dependencies due to CVE
-  * `style` - changes that do not affect the meaning of the code (e.g. formatting, white-space, etc.)
-  * `test` - adding new tests or modifying existing tests
-* Use the imperative, present tense (e.g. "add feature" instead of "added feature")
-* Optionally, include a scope in parantheses after the type to indicate which part of the repository is affected (e.g. `feat(instrumentation): add support for Prisma Client`)
-
-Please note that we use the [Squash and Merge](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-commits) method when merging Pull Requests into the main branch. We do not use the original commit messages from each individual commit. Instead, we use the Pull Request title as the commit message for the squashed commit, and as such, require that the Pull Request title adheres to our Conventional Commit standards. Any additional documentation or information relevant to the release notes should be added to the "optional extended description" section of the squash commit on merge.
-
-
 ### Coding Style Guidelines/Conventions
 
 We use eslint to enforce certain coding standards. Please see our [.eslintrc](./.eslintrc.js) file for specific rule configuration.
diff --git a/changelog.json b/changelog.json
deleted file mode 100644
index 45de459..0000000
--- a/changelog.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "repository": "newrelic/newrelic-node-apollo-server-plugin",
-  "entries": []
-}
diff --git a/docs/metrics.md b/docs/metrics.md
index 3b22136..3637925 100644
--- a/docs/metrics.md
+++ b/docs/metrics.md
@@ -18,10 +18,9 @@ Operation metrics include the operation type, operation name and deepest-path. T
 
 ## Field Resolve Metrics
 
-`/GraphQL/resolve/ApolloServer/[field-name]`
-`/GraphQL/typedResolve/ApolloServer/[parent-type].[field-name]`
+`/GraphQL/resolve/ApolloServer/[parent-type].[field-name]`
 
-Resolve metrics capture the duration spent resolving a particular piece of requested GraphQL data. These can be useful to find specific resolvers that may contribute to slowing down incoming queries. The metric with `typedResolve` in its prefix can be helpful for distinguishing field resolvers that happen to have the same name but are on different types. The metric without the parent type can be helpful in case of the same resolver being used across different types. 
+Resolve metrics capture the duration spent resolving a particular piece of requested GraphQL data. These can be useful to find specific resolvers that may contribute to slowing down incoming queries. It can be helpful for distinguishing field resolvers that happen to have the same name but are on different types. It can also be can be helpful in case of the same resolver being used across different types. 
 
 These differ slightly in naming from their segment/span counterparts. To better visualize relationships, the full path to a field is represented in segments/spans (e.g. libraries.books.title). To understand the duration aggregated across all usages and transactions, these metrics use the field name without the full path.
 
@@ -74,6 +73,13 @@ This is best viewed with the 'Line' chart type which allows for viewing all oper
 
 If you would like to have a list of the top 10 slowest resolves, the following query can be used to pull the data on demand or as a part of a dashboard.
 
+```
+FROM Metric
+SELECT average(newrelic.timeslice.value) * 1000 as 'Average Duration (MS)' WHERE appName = '[YOUR APP NAME]' WITH METRIC_FORMAT 'GraphQL/resolve/ApolloServer/{type}.{field}' FACET field LIMIT 20
+```
+
+If you would like to include the parent type.
+
 ```
 FROM Metric
 SELECT average(newrelic.timeslice.value) * 1000 as 'Average Duration (MS)' WHERE appName = '[YOUR APP NAME]' WITH METRIC_FORMAT 'GraphQL/resolve/ApolloServer/{field}' FACET field LIMIT 20
diff --git a/lib/create-plugin.js b/lib/create-plugin.js
index b5f5f4c..c5c5d25 100644
--- a/lib/create-plugin.js
+++ b/lib/create-plugin.js
@@ -19,7 +19,6 @@ const OPERATION_PREFIX = CATEGORY + '/operation/' + FRAMEWORK
 const RESOLVE_PREFIX = CATEGORY + '/resolve/' + FRAMEWORK
 const ARG_PREFIX = `${CATEGORY}/arg/${FRAMEWORK}`
 const FIELD_PREFIX = `${CATEGORY}/field/${FRAMEWORK}`
-const TYPED_RESOLVE_PREFIX = CATEGORY + '/typedResolve/' + FRAMEWORK
 const BATCH_PREFIX = 'batch'
 
 const DEFAULT_OPERATION_NAME = `${OPERATION_PREFIX}/<unknown>`
@@ -66,7 +65,6 @@ function createPlugin(api, config = {}) {
   config.customResolverAttributes = config.customResolverAttributes || null
   config.customOperationAttributes = config.customOperationAttributes || null
   config.captureFieldMetrics = config.captureFieldMetrics || false
-
   logger.debug('Plugin configuration: ', config)
 
   createModuleUsageMetric(instrumentationApi.agent)
@@ -437,6 +435,7 @@ function getDeepestPathAndQueryArguments(definition) {
  * Creates metrics for resolver fields when transaction is ended.
  * This will record how long specific resolvers took.
  *
+ * @param {Object} params.config plugin config
  * @param {Object} segment relevant resolver segment
  * @param {string} scope name of transaction
  */
@@ -448,22 +447,13 @@ function recordResolveSegment(segment, scope) {
 
   const attributes = segment.getAttributes()
   const fieldName = attributes[FIELD_NAME_ATTR]
+  const fieldType = attributes[PARENT_TYPE_ATTR]
 
   // The segment name uses the path to differentiate between duplicate
   // names resolving across different types. Here we use the field name
-  // without the path to compare resolver across usage and transactions.
-  if (fieldName) {
-    const fieldNameMetric = `${RESOLVE_PREFIX}/${fieldName}`
-    createMetricPairs(transaction, fieldNameMetric, scope, duration, exclusive)
-  }
-
-  const fieldType = attributes[PARENT_TYPE_ATTR]
-
-  // We therefore also record the field with typename. This can be
-  // helpful in case two field names happen to match but are scoped to
-  // different types.
-  if (fieldType) {
-    const typedFieldMetric = `${TYPED_RESOLVE_PREFIX}/${fieldType}.${fieldName}`
+  // with parent type to compare resolver across usage and transactions.
+  if (fieldName && fieldType) {
+    const typedFieldMetric = `${RESOLVE_PREFIX}/${fieldType}.${fieldName}`
     createMetricPairs(transaction, typedFieldMetric, scope, duration, exclusive)
   }
 }
diff --git a/package-lock.json b/package-lock.json
index a5d1a40..bc6e3d7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7617,9 +7617,9 @@
       "dev": true
     },
     "node_modules/follow-redirects": {
-      "version": "1.15.4",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
-      "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
+      "version": "1.15.3",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
+      "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
       "dev": true,
       "funding": [
         {
@@ -20585,9 +20585,9 @@
       "dev": true
     },
     "follow-redirects": {
-      "version": "1.15.4",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
-      "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
+      "version": "1.15.3",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
+      "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
       "dev": true
     },
     "foreground-child": {


Test patch:
diff --git a/tests/metrics-tests.js b/tests/metrics-tests.js
index 248a7d2..f2a5798 100644
--- a/tests/metrics-tests.js
+++ b/tests/metrics-tests.js
@@ -15,7 +15,6 @@ const OPERATION_PREFIX = 'GraphQL/operation/ApolloServer'
 const FIELD_PREFIX = 'GraphQL/field/ApolloServer'
 const RESOLVE_PREFIX = 'GraphQL/resolve/ApolloServer'
 const ARG_PREFIX = 'GraphQL/arg/ApolloServer'
-const TYPED_RESOLVE_PREFIX = 'GraphQL/typedResolve/ApolloServer'
 
 module.exports = createMetricsTests
 
@@ -29,7 +28,7 @@ function createMetricsTests(captureFieldMetrics, t) {
 
     helper.agent.once('transactionFinished', () => {
       const operationPart = `query/${ANON_PLACEHOLDER}/hello`
-      const metrics = [`${OPERATION_PREFIX}/${operationPart}`, `${RESOLVE_PREFIX}/hello`]
+      const metrics = [`${OPERATION_PREFIX}/${operationPart}`, `${RESOLVE_PREFIX}/Query.hello`]
 
       if (captureFieldMetrics) {
         metrics.push(`${FIELD_PREFIX}/Query.hello`)
@@ -54,11 +53,7 @@ function createMetricsTests(captureFieldMetrics, t) {
 
     helper.agent.once('transactionFinished', () => {
       const operationPart = `query/${expectedName}/hello`
-      const metrics = [
-        `${OPERATION_PREFIX}/${operationPart}`,
-        `${RESOLVE_PREFIX}/hello`,
-        `${TYPED_RESOLVE_PREFIX}/Query.hello`
-      ]
+      const metrics = [`${OPERATION_PREFIX}/${operationPart}`, `${RESOLVE_PREFIX}/Query.hello`]
 
       if (captureFieldMetrics) {
         metrics.push(`${FIELD_PREFIX}/Query.hello`)
@@ -94,12 +89,9 @@ function createMetricsTests(captureFieldMetrics, t) {
 
       const metrics = [
         `${OPERATION_PREFIX}/${operationPart}`,
-        `${RESOLVE_PREFIX}/libraries`,
-        `${RESOLVE_PREFIX}/books`,
-        `${RESOLVE_PREFIX}/author`,
-        `${TYPED_RESOLVE_PREFIX}/Query.libraries`,
-        `${TYPED_RESOLVE_PREFIX}/Library.books`,
-        `${TYPED_RESOLVE_PREFIX}/Book.author`
+        `${RESOLVE_PREFIX}/Query.libraries`,
+        `${RESOLVE_PREFIX}/Library.books`,
+        `${RESOLVE_PREFIX}/Book.author`
       ]
 
       if (captureFieldMetrics) {
@@ -152,18 +144,14 @@ function createMetricsTests(captureFieldMetrics, t) {
 
       const operationMetrics1 = [
         `${OPERATION_PREFIX}/${operationPart1}`,
-        `${RESOLVE_PREFIX}/library`,
-        `${RESOLVE_PREFIX}/books`,
-        `${RESOLVE_PREFIX}/author`,
-        `${TYPED_RESOLVE_PREFIX}/Query.library`,
-        `${TYPED_RESOLVE_PREFIX}/Library.books`,
-        `${TYPED_RESOLVE_PREFIX}/Book.author`
+        `${RESOLVE_PREFIX}/Query.library`,
+        `${RESOLVE_PREFIX}/Library.books`,
+        `${RESOLVE_PREFIX}/Book.author`
       ]
 
       const operationMetrics2 = [
         `${OPERATION_PREFIX}/${operationPart2}`,
-        `${RESOLVE_PREFIX}/addThing`,
-        `${TYPED_RESOLVE_PREFIX}/Mutation.addThing`
+        `${RESOLVE_PREFIX}/Mutation.addThing`
       ]
 
       if (captureFieldMetrics) {
@@ -208,8 +196,7 @@ function createMetricsTests(captureFieldMetrics, t) {
       const operationPart = `query/${expectedName}/${path}`
       const operationMetrics = [
         `${OPERATION_PREFIX}/${operationPart}`,
-        `${RESOLVE_PREFIX}/${path}`,
-        `${TYPED_RESOLVE_PREFIX}/Query.${path}`
+        `${RESOLVE_PREFIX}/Query.${path}`
       ]
 
       if (captureFieldMetrics) {
@@ -246,12 +233,9 @@ function createMetricsTests(captureFieldMetrics, t) {
 
       const metrics = [
         `${OPERATION_PREFIX}/${operationPart}`,
-        `${RESOLVE_PREFIX}/libraries`,
-        `${RESOLVE_PREFIX}/books`,
-        `${RESOLVE_PREFIX}/author`,
-        `${TYPED_RESOLVE_PREFIX}/Query.libraries`,
-        `${TYPED_RESOLVE_PREFIX}/Library.books`,
-        `${TYPED_RESOLVE_PREFIX}/Book.author`
+        `${RESOLVE_PREFIX}/Query.libraries`,
+        `${RESOLVE_PREFIX}/Library.books`,
+        `${RESOLVE_PREFIX}/Book.author`
       ]
 
       if (captureFieldMetrics) {
@@ -331,12 +315,9 @@ function createMetricsTests(captureFieldMetrics, t) {
 
       const metrics = [
         `${OPERATION_PREFIX}/${operationPart}`,
-        `${RESOLVE_PREFIX}/library`,
-        `${RESOLVE_PREFIX}/books`,
-        `${RESOLVE_PREFIX}/author`,
-        `${TYPED_RESOLVE_PREFIX}/Query.library`,
-        `${TYPED_RESOLVE_PREFIX}/Library.books`,
-        `${TYPED_RESOLVE_PREFIX}/Book.author`
+        `${RESOLVE_PREFIX}/Query.library`,
+        `${RESOLVE_PREFIX}/Library.books`,
+        `${RESOLVE_PREFIX}/Book.author`
       ]
 
       if (captureFieldMetrics) {
@@ -386,12 +367,9 @@ function createMetricsTests(captureFieldMetrics, t) {
 
       const metrics = [
         `${OPERATION_PREFIX}/${operationPart}`,
-        `${RESOLVE_PREFIX}/library`,
-        `${RESOLVE_PREFIX}/books`,
-        `${RESOLVE_PREFIX}/author`,
-        `${TYPED_RESOLVE_PREFIX}/Query.library`,
-        `${TYPED_RESOLVE_PREFIX}/Library.books`,
-        `${TYPED_RESOLVE_PREFIX}/Book.author`
+        `${RESOLVE_PREFIX}/Query.library`,
+        `${RESOLVE_PREFIX}/Library.books`,
+        `${RESOLVE_PREFIX}/Book.author`
       ]
 
       if (captureFieldMetrics) {