Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix building of GraphQL queries arguments hash #3887

Merged
merged 19 commits into from
Sep 11, 2024

Conversation

y9v
Copy link
Member

@y9v y9v commented Sep 4, 2024

Fixes #3874

What does this PR do?
This PR changes the way we extract arguments from GraphQL queries.
It fixes following things:

  • handling of fragments
  • handling for field aliases
  • handling of default values
  • handling of mutation input object

Motivation:
This issue

Additional Notes:
This PR also changes the keys for arguments, they were query names before, but according to our RFC it should be resolver names.

How to test the change?
Unit tests are included. I also set up a local test GraphQL application for testing which I can share.

@y9v y9v self-assigned this Sep 4, 2024
@y9v y9v requested a review from a team as a code owner September 4, 2024 14:24
@y9v y9v requested review from lloeki and removed request for a team September 4, 2024 14:24
@github-actions github-actions bot added appsec Application Security monitoring product integrations Involves tracing integrations labels Sep 4, 2024
@pr-commenter
Copy link

pr-commenter bot commented Sep 4, 2024

Benchmarks

Benchmark execution time: 2024-09-11 15:37:47

Comparing candidate commit 0259cdb in PR branch appsec-fix-graphql-query-args with baseline commit bc84181 in branch master.

Found 0 performance improvements and 0 performance regressions! Performance is the same for 23 metrics, 2 unstable metrics.

@codecov-commenter
Copy link

codecov-commenter commented Sep 4, 2024

Codecov Report

Attention: Patch coverage is 98.63946% with 2 lines in your changes missing coverage. Please review.

Project coverage is 97.85%. Comparing base (bc84181) to head (0259cdb).
Report is 5 commits behind head on master.

Files with missing lines Patch % Lines
.../datadog/appsec/contrib/graphql/gateway/watcher.rb 84.61% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master    #3887   +/-   ##
=======================================
  Coverage   97.84%   97.85%           
=======================================
  Files        1279     1280    +1     
  Lines       76530    76621   +91     
  Branches     3752     3753    +1     
=======================================
+ Hits        74883    74976   +93     
+ Misses       1647     1645    -2     

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

@y9v y9v force-pushed the appsec-fix-graphql-query-args branch from 55606b3 to 35f8ccd Compare September 5, 2024 10:37
Copy link
Member

@anmarchenko anmarchenko left a comment

Choose a reason for hiding this comment

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

Thanks for tackling this, it looks cleaner and I like the additional tests. Left a few comments on some quick performance wins.

Copy link
Member

@anmarchenko anmarchenko left a comment

Choose a reason for hiding this comment

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

Looks good, thank you 👍🏻

@TonyCTHsu
Copy link
Contributor

We've talked about being slightly more defensive:

  1. Rescuing unexpected error and return a benign value
  2. Validate the presence of constant which the implementation depends on before applying the patch

@y9v
Copy link
Member Author

y9v commented Sep 9, 2024

@TonyCTHsu as discussed, I removed direct usage of graphql-ruby constants here and added a compatibility check based on whether they are defined or not.

And here I added rescuing of unexpected errors during AppSec instrumentation.

@y9v y9v force-pushed the appsec-fix-graphql-query-args branch from b2fb79e to c0b27d0 Compare September 9, 2024 09:59
Copy link
Contributor

@TonyCTHsu TonyCTHsu left a comment

Choose a reason for hiding this comment

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

Approve!

end

def self.auto_instrument?
true
end

def self.ast_node_classes_defined?
Copy link
Contributor

Choose a reason for hiding this comment

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

Adding a couple of test cases

  1. Test positive with our graphql appraisal groups.
  2. Test negative with hide_const

Copy link
Member Author

@y9v y9v Sep 9, 2024

Choose a reason for hiding this comment

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

added here: 20a00b8

@github-actions github-actions bot requested a review from a team as a code owner September 9, 2024 10:31
@y9v y9v requested a review from vpellan September 9, 2024 12:09
Copy link
Contributor

@vpellan vpellan left a comment

Choose a reason for hiding this comment

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

LGTM!

@y9v y9v requested a review from Strech September 10, 2024 09:19
@y9v y9v force-pushed the appsec-fix-graphql-query-args branch from f818b5e to 9c460bc Compare September 10, 2024 11:58
Copy link
Member

@Strech Strech left a comment

Choose a reason for hiding this comment

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

Can't comment much on the correctness of the code, but here are my 50c with the tests.

RSpec.describe Datadog::AppSec::Contrib::GraphQL::Integration do
describe '.ast_node_classes_defined?' do
it 'returns true when all AST node classes are defined' do
expect(described_class.ast_node_classes_defined?).to eq(true)
Copy link
Member

Choose a reason for hiding this comment

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

Just mine 50c since I can't add more to the actual code 😄

Suggested change
expect(described_class.ast_node_classes_defined?).to eq(true)
expect(described_class.ast_node_classes_defined?).to be(true)

Explanation: https://stackoverflow.com/questions/26779937/what-is-the-difference-between-be-true-and-be-true-in-rspec

Copy link
Member

Choose a reason for hiding this comment

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

hm, I still don't see in the link above what is the difference in this case between be and eq: I always use eq for booleans as it reads similar to == true or == false

Copy link
Member Author

Choose a reason for hiding this comment

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

it is the same actually, I just thought that be(true) would be a bit more readable

Copy link
Member

Choose a reason for hiding this comment

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

I personally type expect(xxx).to be true because it saves me two shift presses with pinky but as far as reading the code I don't see any difference between be true and eq(true). Semantically I believe the two are identical.


it 'returns false when at least one of AST node classes is not defined' do
hide_const('GraphQL::Language::Nodes::Field')
expect(described_class.ast_node_classes_defined?).to eq(false)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
expect(described_class.ast_node_classes_defined?).to eq(false)
expect(described_class.ast_node_classes_defined?).to be(false)

Comment on lines 374 to 377
let(:queries) { [query] }

it 'returns queries' do
expect(dd_multiplex.queries).to eq(queries)
Copy link
Member

@Strech Strech Sep 11, 2024

Choose a reason for hiding this comment

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

Just a suggestion over the simplicity, I think the queries var doesn't bring much value (hiding setup complexity) nor explaining the value. Because of that I think it's easier just to use the real expectation value

Suggested change
let(:queries) { [query] }
it 'returns queries' do
expect(dd_multiplex.queries).to eq(queries)
it 'returns queries' do
expect(dd_multiplex.queries).to eq([query])

Copy link
Member Author

@y9v y9v Sep 11, 2024

Choose a reason for hiding this comment

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

thanks! fixed in 242eb7c

@y9v y9v force-pushed the appsec-fix-graphql-query-args branch from 3562af9 to 242eb7c Compare September 11, 2024 11:08
@y9v y9v force-pushed the appsec-fix-graphql-query-args branch from 242eb7c to c7fcae4 Compare September 11, 2024 12:38
@lloeki
Copy link
Member

lloeki commented Sep 11, 2024

Approved, although. I can't say much about the more intricate technical details here.

@p-datadog
Copy link
Member

p-datadog commented Sep 11, 2024

The GraphQL logic itself is above my pay grade I'm afraid.

@y9v y9v requested a review from p-datadog September 11, 2024 15:09
@y9v
Copy link
Member Author

y9v commented Sep 11, 2024

Merging this, the failing system test will be fixed by DataDog/system-tests#2988

@y9v y9v merged commit fa5d009 into master Sep 11, 2024
190 of 191 checks passed
@y9v y9v deleted the appsec-fix-graphql-query-args branch September 11, 2024 17:39
@TonyCTHsu TonyCTHsu added this to the 2.4.0 milestone Sep 12, 2024
@y9v y9v mentioned this pull request Oct 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
appsec Application Security monitoring product integrations Involves tracing integrations
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Upgrade from 2.2.0 to 2.3.0 breaks app (GraphQL queries with fragments)
8 participants