diff --git a/docs/client.md b/docs/client.md index 3532d8e1..d8b81f10 100644 --- a/docs/client.md +++ b/docs/client.md @@ -68,6 +68,12 @@ client.on_cache_write do |request, payload| end ``` +All request digests use SHA2 by default. You can swap in [a faster algorithm](https://github.com/Shopify/blake3-rb) and/or add base scoping by reconfiguring the stitching library: + +```ruby +GraphQL::Stitching.digest { |str| Digest::MD5.hexdigest("v2/#{str}") } +``` + Note that inlined input data works against caching, so you should _avoid_ these input literals when possible: ```graphql diff --git a/lib/graphql/stitching.rb b/lib/graphql/stitching.rb index 941514bd..5690fdb4 100644 --- a/lib/graphql/stitching.rb +++ b/lib/graphql/stitching.rb @@ -27,12 +27,24 @@ class CompositionError < StitchingError; end class ValidationError < CompositionError; end class << self + attr_writer :stitch_directive + + # Proc used to compute digests; uses SHA2 by default. + # @returns [Proc] proc used to compute digests. + def digest(&block) + if block_given? + @digest = block + else + @digest ||= ->(str) { Digest::SHA2.hexdigest(str) } + end + end + + # Name of the directive used to mark type resolvers. + # @returns [String] name of the type resolver directive. def stitch_directive @stitch_directive ||= "stitch" end - attr_writer :stitch_directive - # Names of stitching directives to omit from the composed supergraph. # @returns [Array] list of stitching directive names. def stitching_directive_names diff --git a/lib/graphql/stitching/request.rb b/lib/graphql/stitching/request.rb index 7a6f0824..681f2e37 100644 --- a/lib/graphql/stitching/request.rb +++ b/lib/graphql/stitching/request.rb @@ -75,12 +75,12 @@ def normalized_string # @return [String] a digest of the original document string. Generally faster but less consistent. def digest - @digest ||= Digest::SHA2.hexdigest(string) + @digest ||= Stitching.digest.call("#{Stitching::VERSION}/#{string}") end # @return [String] a digest of the normalized document string. Slower but more consistent. def normalized_digest - @normalized_digest ||= Digest::SHA2.hexdigest(normalized_string) + @normalized_digest ||= Stitching.digest.call("#{Stitching::VERSION}/#{normalized_string}") end # @return [GraphQL::Language::Nodes::OperationDefinition] The selected root operation for the request. diff --git a/lib/graphql/stitching/type_resolver.rb b/lib/graphql/stitching/type_resolver.rb index 3f2aaa04..b19a7a84 100644 --- a/lib/graphql/stitching/type_resolver.rb +++ b/lib/graphql/stitching/type_resolver.rb @@ -47,7 +47,7 @@ def list? end def version - @version ||= Digest::SHA2.hexdigest("#{Stitching::VERSION}/#{as_json.to_json}") + @version ||= Stitching.digest.call("#{Stitching::VERSION}/#{as_json.to_json}") end def ==(other) diff --git a/test/graphql/stitching/request/request_test.rb b/test/graphql/stitching/request/request_test.rb index 1d74c698..2497dd6f 100644 --- a/test/graphql/stitching/request/request_test.rb +++ b/test/graphql/stitching/request/request_test.rb @@ -136,8 +136,8 @@ def test_provides_digest_and_normalized_digest | request = GraphQL::Stitching::Request.new(@supergraph, string) - expected = "ad4b4eb706f67020084a7927ed5bd73b7196e393e0af3535d25ae2d22df33232" - expected_normalized = "88908d0790f7b20afe4a7508a8bba6343c62f98abb9c5abff17345c64d90c0d0" + expected = "9f3c5afb51b7155611921d0e8537c8556b8d757ac90b63d85ea52964d18076d0" + expected_normalized = "71369fe5ee8f33e15b049991a294e6eeeb6e743310c339877f770fa8beb29f21" assert_equal expected, request.digest assert_equal expected_normalized, request.normalized_digest diff --git a/test/graphql/stitching_test.rb b/test/graphql/stitching_test.rb new file mode 100644 index 00000000..f88a2914 --- /dev/null +++ b/test/graphql/stitching_test.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require "test_helper" + +describe "GraphQL::Stitching" do + def test_digest_gets_and_sets_hashing_implementation + expected_sha = "f5a163f364ac65dfd8ef60edb3ba39d6c2b44bccc289af3ced96b06e3f25df59" + expected_md5 = "fec9ff7a551c37ef692994407710fa54" + + fn = GraphQL::Stitching.digest + assert_equal expected_sha, new_type_resolver.version + + GraphQL::Stitching.digest { |str| Digest::MD5.hexdigest(str) } + assert_equal expected_md5, new_type_resolver.version + + GraphQL::Stitching.digest(&fn) + assert_equal expected_sha, new_type_resolver.version + end + + private + + def new_type_resolver + GraphQL::Stitching::TypeResolver.new( + location: "a", + type_name: "Test", + list: false, + field: "a", + key: GraphQL::Stitching::TypeResolver.parse_key("id"), + arguments: GraphQL::Stitching::TypeResolver.parse_arguments_with_type_defs("id: $.id", "id: ID"), + ) + end +end