Skip to content

Conversation

@becandier
Copy link

@becandier becandier commented Nov 27, 2025

  • Add TalkerGraphQLLink for logging GraphQL operations
  • Support for queries, mutations, and subscriptions
  • Request/response duration tracking
  • Variable obfuscation for sensitive fields (password, token, etc.)
  • Configurable logging settings
  • Custom pen colors for different log types
  • Request/response/error filtering
  • Add TalkerKey constants for GraphQL logs
  • Comprehensive test coverage (20 tests)
  • Example and documentation

Thanks a lot for contributing!

Provide a description of your changes below

Summary by Sourcery

Add a new GraphQL-specific logging package integrated with Talker, including link integration, log types, settings, and documentation.

New Features:

  • Introduce the talker_graphql_logger package for logging GraphQL client operations via Talker.
  • Add TalkerGraphQLLink to intercept and log GraphQL queries, mutations, and subscriptions, including duration and error handling.
  • Provide configurable TalkerGraphQLLoggerSettings for log levels, variable/response printing, field obfuscation, colors, and request/response/error filtering.
  • Define specialized GraphQL log entries for requests, responses, errors, and subscription events, keyed via new TalkerKey GraphQL constants.

Enhancements:

  • Extend TalkerKey with dedicated GraphQL keys for request, response, error, and subscription logs.

Documentation:

  • Add README and example applications demonstrating setup and customization of GraphQL logging with Talker.

Tests:

  • Add comprehensive unit tests for TalkerGraphQLLink behavior, logger settings, and all GraphQL log types.
- Add TalkerGraphQLLink for logging GraphQL operations - Support for queries, mutations, and subscriptions - Request/response duration tracking - Variable obfuscation for sensitive fields (password, token, etc.) - Configurable logging settings - Custom pen colors for different log types - Request/response/error filtering - Add TalkerKey constants for GraphQL logs - Comprehensive test coverage (20 tests) - Example and documentation
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Nov 27, 2025

Reviewer's Guide

Introduces a new talker_graphql_logger package that provides a TalkerGraphQLLink for logging GraphQL operations (queries, mutations, subscriptions) with configurable settings, obfuscation, filtering, and custom log types, along with tests, examples, and documentation; also extends the core TalkerKey set with GraphQL-specific keys.

Sequence diagram for query/mutation logging via TalkerGraphQLLink

sequenceDiagram actor Developer participant GraphQLClient participant LinkChain participant TalkerGraphQLLink participant NextLink participant Server as HttpOrOtherLink participant Talker Developer->>GraphQLClient: execute(Request request) GraphQLClient->>LinkChain: request(request) LinkChain->>TalkerGraphQLLink: request(request, forward) alt logging disabled TalkerGraphQLLink-->>NextLink: forward(request) NextLink-->>Server: request(request) Server-->>NextLink: Stream<Response> NextLink-->>GraphQLClient: Stream<Response> else logging enabled and request accepted TalkerGraphQLLink->>Talker: logCustom(GraphQLRequestLog) Talker-->>Talker: store and print loop each Response from forward(request) TalkerGraphQLLink->>NextLink: forward(request) NextLink->>Server: request(request) Server-->>NextLink: Response response NextLink-->>TalkerGraphQLLink: Response response TalkerGraphQLLink->>TalkerGraphQLLink: measure durationMs alt response has errors opt errorFilter accepts TalkerGraphQLLink->>Talker: logCustom(GraphQLErrorLog) Talker-->>Talker: store and print end else successful response opt responseFilter accepts TalkerGraphQLLink->>Talker: logCustom(GraphQLResponseLog) Talker-->>Talker: store and print end end TalkerGraphQLLink-->>GraphQLClient: yield Response response end Note over TalkerGraphQLLink: On exceptions from forward(request) TalkerGraphQLLink->>TalkerGraphQLLink: compute durationMs opt errorFilter accepts TalkerGraphQLLink->>Talker: logCustom(GraphQLErrorLog(linkException)) Talker-->>Talker: store and print end TalkerGraphQLLink-->>GraphQLClient: rethrow error end 
Loading

Sequence diagram for subscription logging via TalkerGraphQLLink

sequenceDiagram actor Developer participant GraphQLClient participant LinkChain participant TalkerGraphQLLink participant NextLink participant Server as SubscriptionLink participant Talker Developer->>GraphQLClient: subscribe(Request subscriptionRequest) GraphQLClient->>LinkChain: request(subscriptionRequest) LinkChain->>TalkerGraphQLLink: request(subscriptionRequest, forward) TalkerGraphQLLink->>TalkerGraphQLLink: _isSubscription(request) == true TalkerGraphQLLink->>Talker: logCustom(GraphQLRequestLog) Talker-->>Talker: store and print loop each subscription event TalkerGraphQLLink->>NextLink: forward(request) NextLink->>Server: start/continue subscription Server-->>NextLink: Response event NextLink-->>TalkerGraphQLLink: Response event alt event has errors TalkerGraphQLLink->>Talker: logCustom(GraphQLSubscriptionLog(eventType=error)) else data event TalkerGraphQLLink->>Talker: logCustom(GraphQLSubscriptionLog(eventType=data)) end Talker-->>Talker: store and print TalkerGraphQLLink-->>GraphQLClient: yield Response event end TalkerGraphQLLink->>Talker: logCustom(GraphQLSubscriptionLog(eventType=done)) Talker-->>Talker: store and print Note over TalkerGraphQLLink: On subscription stream error TalkerGraphQLLink->>Talker: logCustom(GraphQLErrorLog(linkException)) Talker-->>Talker: store and print TalkerGraphQLLink-->>GraphQLClient: rethrow error 
Loading

Class diagram for TalkerGraphQLLink and GraphQL log types

classDiagram class TalkerGraphQLLink { +TalkerGraphQLLink(talker, settings) -Talker _talker +TalkerGraphQLLoggerSettings settings +Stream~Response~ request(Request request, NextLink forward) -bool _isSubscription(Request request) -Stream~Response~ _handleOperation(Request request, NextLink forward) -Stream~Response~ _handleSubscription(Request request, NextLink forward) -String _getOperationName(Request request) } class TalkerGraphQLLoggerSettings { +TalkerGraphQLLoggerSettings() +bool enabled +LogLevel logLevel +bool printVariables +bool printResponse +bool printQuery +int responseMaxLength +Set~String~ obfuscateFields +AnsiPen requestPen +AnsiPen responsePen +AnsiPen errorPen +AnsiPen subscriptionPen +GraphQLRequestFilter requestFilter +GraphQLResponseFilter responseFilter +GraphQLErrorFilter errorFilter +TalkerGraphQLLoggerSettings copyWith(enabled, logLevel, printVariables, printResponse, printQuery, responseMaxLength, obfuscateFields, requestPen, responsePen, errorPen, subscriptionPen, requestFilter, responseFilter, errorFilter) } class GraphQLRequestLog { +GraphQLRequestLog(message, request, settings) +Request request +TalkerGraphQLLoggerSettings settings +AnsiPen pen +String key +LogLevel logLevel +String generateTextMessage(timeFormat) } class GraphQLResponseLog { +GraphQLResponseLog(message, request, response, durationMs, settings) +Request request +Response response +int durationMs +TalkerGraphQLLoggerSettings settings +AnsiPen pen +String key +LogLevel logLevel +String generateTextMessage(timeFormat) } class GraphQLErrorLog { +GraphQLErrorLog(message, request, response, linkException, durationMs, settings) +Request request +Response response +Object linkException +int durationMs +TalkerGraphQLLoggerSettings settings +AnsiPen pen +String key +LogLevel logLevel +String generateTextMessage(timeFormat) } class GraphQLSubscriptionLog { +GraphQLSubscriptionLog(message, request, response, eventType, settings) +Request request +Response response +GraphQLSubscriptionEventType eventType +TalkerGraphQLLoggerSettings settings +AnsiPen pen +String key +LogLevel logLevel +String generateTextMessage(timeFormat) } class GraphQLSubscriptionEventType { <<enumeration>> data error done } class Talker { +Talker() +TalkerSettings settings +void logCustom(TalkerLog log) } class TalkerSettings { +void registerKeys(List~String~ keys) } class TalkerLog { +String title +AnsiPen pen +String key +LogLevel logLevel +String generateTextMessage(timeFormat) } class TalkerKey { <<abstract>> +String graphqlRequest +String graphqlResponse +String graphqlError +String graphqlSubscription } class Request { +Operation operation +Map~String,dynamic~ variables } class Response { +Map~String,dynamic~ data +List~GraphQLError~ errors } class Link { +Stream~Response~ request(Request request, NextLink forward) +Link concat(Link next) } class NextLink { +Stream~Response~ call(Request request) } class Operation { +String operationName +DocumentNode document } class GraphQLError { +String message +List~ErrorLocation~ locations +List~Object~ path } TalkerGraphQLLink --> Talker : uses TalkerGraphQLLink --> TalkerGraphQLLoggerSettings : has TalkerGraphQLLink --> Request : handles TalkerGraphQLLink --> Response : handles TalkerGraphQLLink --> Link : extends GraphQLRequestLog --> Request : logs GraphQLRequestLog --> TalkerGraphQLLoggerSettings : uses GraphQLRequestLog --> TalkerLog : extends GraphQLRequestLog --> TalkerKey : uses key GraphQLResponseLog --> Request : logs GraphQLResponseLog --> Response : logs GraphQLResponseLog --> TalkerGraphQLLoggerSettings : uses GraphQLResponseLog --> TalkerLog : extends GraphQLResponseLog --> TalkerKey : uses key GraphQLErrorLog --> Request : logs GraphQLErrorLog --> Response : logs GraphQLErrorLog --> TalkerGraphQLLoggerSettings : uses GraphQLErrorLog --> TalkerLog : extends GraphQLErrorLog --> TalkerKey : uses key GraphQLSubscriptionLog --> Request : logs GraphQLSubscriptionLog --> Response : logs GraphQLSubscriptionLog --> GraphQLSubscriptionEventType : uses GraphQLSubscriptionLog --> TalkerGraphQLLoggerSettings : uses GraphQLSubscriptionLog --> TalkerLog : extends GraphQLSubscriptionLog --> TalkerKey : uses key Talker --> TalkerSettings : has TalkerLog <|-- GraphQLRequestLog TalkerLog <|-- GraphQLResponseLog TalkerLog <|-- GraphQLErrorLog TalkerLog <|-- GraphQLSubscriptionLog Link <|-- TalkerGraphQLLink 
Loading

File-Level Changes

Change Details Files
Add GraphQL-specific Talker log keys to the core talker package so GraphQL logs are first-class citizens.
  • Introduce graphqlRequest, graphqlResponse, graphqlError, and graphqlSubscription string constants in the TalkerKey class.
  • Group the new keys under a documented GraphQL section to mirror existing HTTP/GRPC sections.
packages/talker/lib/src/talker_key.dart
Create the talker_graphql_logger package with a configurable settings object used by all GraphQL logging components.
  • Define TalkerGraphQLLoggerSettings with flags for enabling logging, variable/response/query output, response length truncation, obfuscation fields, custom AnsiPen colors, and request/response/error filters.
  • Expose copyWith to allow non-breaking configuration mutation while preserving unspecified values.
  • Define typed filter callbacks for requests, responses, and errors using gql_exec Request/Response types.
packages/talker_graphql_logger/lib/src/talker_graphql_logger_settings.dart
packages/talker_graphql_logger/pubspec.yaml
packages/talker_graphql_logger/analysis_options.yaml
packages/talker_graphql_logger/CHANGELOG.md
packages/talker_graphql_logger/LICENSE
Implement concrete TalkerLog subclasses for GraphQL requests, responses, errors, and subscription events with duration tracking and obfuscation support.
  • Add helper functions for deriving operation type/name from the GraphQL AST and recursively obfuscating sensitive variables based on a configurable field set.
  • Implement GraphQLRequestLog to render operation type/name, optionally pretty-printed variables (with obfuscated fields) and query text, using a configurable or default pen and TalkerKey.graphqlRequest.
  • Implement GraphQLResponseLog to render operation info plus duration and optionally truncated pretty-printed response data, keyed as graphqlResponse and honoring responseMaxLength.
  • Implement GraphQLErrorLog to render operation info, duration, GraphQL errors (messages, locations, path), optional link exceptions, and obfuscated variables, with error-level severity and TalkerKey.graphqlError.
  • Implement GraphQLSubscriptionLog to render subscription operation name, event type (data/error/done), and optional truncated response data for subscriptions, with TalkerKey.graphqlSubscription and an enum GraphQLSubscriptionEventType.
packages/talker_graphql_logger/lib/src/graphql_logs.dart
Provide a TalkerGraphQLLink implementation that hooks into gql_link and emits the new GraphQL log events for queries, mutations, and subscriptions with filtering and error handling.
  • Construct TalkerGraphQLLink with an optional Talker instance and TalkerGraphQLLoggerSettings, registering the GraphQL TalkerKey values during initialization.
  • Override request to short-circuit logging when disabled and apply a requestFilter prior to forwarding; detect subscription operations via OperationDefinitionNode type.
  • Implement _handleOperation to log a GraphQLRequestLog at start, then for each response log either GraphQLResponseLog or GraphQLErrorLog based on presence of errors while honoring responseFilter and errorFilter, measuring and including duration; on thrown exceptions log GraphQLErrorLog with linkException and rethrow.
  • Implement _handleSubscription to log GraphQLRequestLog for the subscription start, then for each event emit GraphQLSubscriptionLog with data or error eventType and finally a done event; on stream exceptions log GraphQLErrorLog with linkException and rethrow.
  • Add a private helper for resolving the operation name from Request, falling back to parsing OperationDefinitionNode when operationName is unset.
packages/talker_graphql_logger/lib/src/talker_graphql_link.dart
Add examples demonstrating direct log usage and link-based integration with real and mock GraphQL links.
  • Create a console_demo.dart showcasing manual construction of GraphQLRequestLog, GraphQLResponseLog, GraphQLErrorLog (GraphQL and network errors), and GraphQLSubscriptionLog, then a full-flow example using TalkerGraphQLLink with a mocked Link.
  • Add a more realistic example main.dart that composes TalkerGraphQLLink with an HttpLink targeting the SpaceX API, builds a Request with variables, and iterates the resulting stream, demonstrating typical usage.
  • Provide a dedicated example pubspec with the necessary gql and talker dependencies referencing the local talker_graphql_logger package path.
packages/talker_graphql_logger/example/console_demo.dart
packages/talker_graphql_logger/example/lib/main.dart
packages/talker_graphql_logger/example/pubspec.yaml
Add comprehensive tests that validate settings behavior, logging side effects, message formatting, obfuscation, truncation, and event typing for the new GraphQL logger.
  • Test TalkerGraphQLLoggerSettings defaults and copyWith semantics, especially around enabled flags, printing options, and obfuscateFields.
  • Set up a Talker instance with a stream listener to capture emitted TalkerData and validate that TalkerGraphQLLink logs nothing when disabled, logs expected request/response pairs in the happy path, and logs GraphQLErrorLog when errors are present.
  • Verify requestFilter suppresses logs for filtered operations while permitting others.
  • Assert that each log type uses the correct TalkerKey and, where relevant, logLevel, and that message text contains expected markers: operation type/name, variables, obfuscated password values, duration text, truncation markers, GraphQL error details (message, path), exception text, and subscription event type labels.
  • Introduce a MockTerminalLink that returns a fixed Response to make TalkerGraphQLLink tests deterministic and stream-based.
packages/talker_graphql_logger/test/talker_graphql_logger_test.dart
Expose the GraphQL logger public API and basic package metadata for consumption.
  • Create the main library entrypoint talker_graphql_logger.dart that exports graphql_logs.dart, talker_graphql_link.dart, and talker_graphql_logger_settings.dart for external use.
  • Document the package in README.md with setup instructions, configuration examples, feature list, log type table, and integration notes with talker_flutter and other GraphQL client stacks.
packages/talker_graphql_logger/lib/talker_graphql_logger.dart
packages/talker_graphql_logger/README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • The helper for deriving the operation name is duplicated in graphql_logs.dart and talker_graphql_link.dart; consider centralizing this logic to a shared private function to avoid divergence and keep behavior consistent.
  • The _obfuscateVariables helper only recurses into nested Map<String, dynamic> values and ignores lists, so sensitive fields inside arrays (e.g. a list of input objects) will not be obfuscated; consider adding list handling to obfuscate nested structures uniformly.
  • In TalkerGraphQLLink.request, you rely on forward! being non-null but don't enforce it; adding an assertion or explicit error when forward is null would make misuse of this link easier to diagnose.
Prompt for AI Agents
Please address the comments from this code review: ## Overall Comments - The helper for deriving the operation name is duplicated in `graphql_logs.dart` and `talker_graphql_link.dart`; consider centralizing this logic to a shared private function to avoid divergence and keep behavior consistent. - The `_obfuscateVariables` helper only recurses into nested `Map<String, dynamic>` values and ignores lists, so sensitive fields inside arrays (e.g. a list of input objects) will not be obfuscated; consider adding list handling to obfuscate nested structures uniformly. - In `TalkerGraphQLLink.request`, you rely on `forward!` being non-null but don't enforce it; adding an assertion or explicit error when `forward` is null would make misuse of this link easier to diagnose. ## Individual Comments ### Comment 1 <location> `packages/talker_graphql_logger/lib/src/graphql_logs.dart:60-66` </location> <code_context> + for (final entry in variables.entries) { + if (lowerCaseFields.contains(entry.key.toLowerCase())) { +  result[entry.key] = _obfuscatedValue; + } else if (entry.value is Map<String, dynamic>) { +  result[entry.key] = _obfuscateVariables( +  entry.value as Map<String, dynamic>, +  obfuscateFields, +  ); + } else { +  result[entry.key] = entry.value; + } + } </code_context> <issue_to_address> **🚨 issue (security):** Variable obfuscation does not handle lists containing nested maps, which may leave sensitive values unmasked. Because the logic only recurses into `Map<String, dynamic>` values, it skips nested maps inside collections (e.g. `[{password: ...}]`). For payloads that include lists of objects, sensitive fields in those objects remain unmasked. Please update `_obfuscateVariables` to also handle `List` values by recursing into any map elements so obfuscation covers deeply nested structures. </issue_to_address> ### Comment 2 <location> `packages/talker_graphql_logger/lib/src/talker_graphql_link.dart:62` </location> <code_context> + @override + Stream<Response> request(Request request, [NextLink? forward]) { + if (!settings.enabled) { +  return forward!(request); + } + </code_context> <issue_to_address> **issue (bug_risk):** The link assumes `forward` is always non-null, which can throw if used as a terminating link. In `request`, you call `forward!(request)` both in the `!settings.enabled` path and when the request is filtered. That’s safe only when this link sits in the middle of a chain; if it’s ever used as a terminating link, `forward` will be null and this will throw at runtime. To make this more robust, either assert `forward != null` at the start of `request`, or handle `forward == null` explicitly (e.g., return an error or empty stream with a clear message). </issue_to_address> ### Comment 3 <location> `packages/talker_graphql_logger/lib/src/talker_graphql_link.dart:274-283` </location> <code_context> +} + +/// Returns the operation name from the request. +String _getOperationName(Request request) { + final operationName = request.operation.operationName; + if (operationName != null && operationName.isNotEmpty) { + return operationName; + } + + final definitions = request.operation.document.definitions; + for (final definition in definitions) { + if (definition is OperationDefinitionNode) { +  final name = definition.name?.value; +  if (name != null) return name; + } + } + return 'Anonymous'; +} + </code_context> <issue_to_address> **suggestion:** Operation-name extraction logic is duplicated with `_getOperationName` in `graphql_logs.dart`. This implementation is effectively identical to `_getOperationName` in `graphql_logs.dart`, which risks divergence if only one is updated (for example, when supporting additional AST shapes). Please extract this into a shared helper (e.g., a private utility in a common file) and call it from both `TalkerGraphQLLink` and the logging code. Suggested implementation: ``` import 'graphql_logs.dart'; import 'graphql_operation_name.dart'; import 'talker_graphql_logger_settings.dart';  ``` ```  }  ``` ```  ``` ``` getOperationName(  ``` To fully implement the deduplication and avoid divergence: 1. Create a new shared helper file, for example at `packages/talker_graphql_logger/lib/src/graphql_operation_name.dart`: ```dart  import 'package:gql/ast.dart';  import 'package:gql_exec/gql_exec.dart';   /// Returns the operation name from the request.  String getOperationName(Request request) {  final operationName = request.operation.operationName;  if (operationName != null && operationName.isNotEmpty) {  return operationName;  }   final definitions = request.operation.document.definitions;  for (final definition in definitions) {  if (definition is OperationDefinitionNode) {  final name = definition.name?.value;  if (name != null) return name;  }  }  return 'Anonymous';  }  ``` 2. In `packages/talker_graphql_logger/lib/src/graphql_logs.dart`: - Remove the existing `_getOperationName` implementation. - Add `import 'graphql_operation_name.dart';`. - Replace any calls to `_getOperationName(request)` with `getOperationName(request)`. These changes will centralize the operation-name extraction logic while keeping the call sites (`TalkerGraphQLLink` and the logging code) simple and consistent. </issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Comment on lines +60 to +66
} else if (entry.value is Map<String, dynamic>) {
result[entry.key] = _obfuscateVariables(
entry.value as Map<String, dynamic>,
obfuscateFields,
);
} else {
result[entry.key] = entry.value;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 issue (security): Variable obfuscation does not handle lists containing nested maps, which may leave sensitive values unmasked.

Because the logic only recurses into Map<String, dynamic> values, it skips nested maps inside collections (e.g. [{password: ...}]). For payloads that include lists of objects, sensitive fields in those objects remain unmasked. Please update _obfuscateVariables to also handle List values by recursing into any map elements so obfuscation covers deeply nested structures.

@override
Stream<Response> request(Request request, [NextLink? forward]) {
if (!settings.enabled) {
return forward!(request);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The link assumes forward is always non-null, which can throw if used as a terminating link.

In request, you call forward!(request) both in the !settings.enabled path and when the request is filtered. That’s safe only when this link sits in the middle of a chain; if it’s ever used as a terminating link, forward will be null and this will throw at runtime. To make this more robust, either assert forward != null at the start of request, or handle forward == null explicitly (e.g., return an error or empty stream with a clear message).

Comment on lines +274 to +283
String _getOperationName(Request request) {
final operationName = request.operation.operationName;
if (operationName != null && operationName.isNotEmpty) {
return operationName;
}

final definitions = request.operation.document.definitions;
for (final definition in definitions) {
if (definition is OperationDefinitionNode) {
final name = definition.name?.value;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Operation-name extraction logic is duplicated with _getOperationName in graphql_logs.dart.

This implementation is effectively identical to _getOperationName in graphql_logs.dart, which risks divergence if only one is updated (for example, when supporting additional AST shapes). Please extract this into a shared helper (e.g., a private utility in a common file) and call it from both TalkerGraphQLLink and the logging code.

Suggested implementation:

import 'graphql_logs.dart'; import 'graphql_operation_name.dart'; import 'talker_graphql_logger_settings.dart'; 
 } 
 
getOperationName( 

To fully implement the deduplication and avoid divergence:

  1. Create a new shared helper file, for example at packages/talker_graphql_logger/lib/src/graphql_operation_name.dart:

    import 'package:gql/ast.dart'; import 'package:gql_exec/gql_exec.dart'; /// Returns the operation name from the request. String getOperationName(Request request) { final operationName = request.operation.operationName; if (operationName != null && operationName.isNotEmpty) { return operationName; } final definitions = request.operation.document.definitions; for (final definition in definitions) { if (definition is OperationDefinitionNode) { final name = definition.name?.value; if (name != null) return name; } } return 'Anonymous'; }
  2. In packages/talker_graphql_logger/lib/src/graphql_logs.dart:

    • Remove the existing _getOperationName implementation.
    • Add import 'graphql_operation_name.dart';.
    • Replace any calls to _getOperationName(request) with getOperationName(request).

These changes will centralize the operation-name extraction logic while keeping the call sites (TalkerGraphQLLink and the logging code) simple and consistent.

@becandier becandier closed this Nov 27, 2025
@becandier becandier reopened this Nov 27, 2025
@Frezyx Frezyx added the addons Related to addons/bridge packages like dio_logger and bloc_logger label Dec 2, 2025
@codecov-commenter
Copy link

codecov-commenter commented Dec 2, 2025

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.97%. Comparing base (1ed15ab) to head (722c4ce).
⚠️ Report is 143 commits behind head on master.
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@ Coverage Diff @@ ## master #443 +/- ## ========================================== - Coverage 98.63% 90.97% -7.66%  ========================================== Files 3 13 +10 Lines 146 266 +120 ========================================== + Hits 144 242 +98  - Misses 2 24 +22 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

addons Related to addons/bridge packages like dio_logger and bloc_logger

3 participants