Skip to content

Commit fee109a

Browse files
authored
add sorbet typechecking. (#190)
1 parent e580756 commit fee109a

41 files changed

Lines changed: 22595 additions & 533 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,17 @@ jobs:
3939
gem install bundler -v 2.4.22
4040
bundle install --jobs 4 --retry 3
4141
bundle exec rake test
42+
43+
typecheck:
44+
runs-on: ubuntu-latest
45+
env:
46+
BUNDLE_GEMFILE: Gemfile
47+
steps:
48+
- uses: actions/checkout@v4
49+
- name: Setup Ruby
50+
uses: ruby/setup-ruby@v1
51+
with:
52+
ruby-version: 3.3
53+
bundler-cache: true
54+
- name: Run typecheck
55+
run: bundle exec rake typecheck

‎Gemfile‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
source 'https://rubygems.org'
44
gemspec
55

6+
group :development do
7+
gem 'sorbet', require: false
8+
gem 'tapioca', require: false
9+
end
10+
611
gem 'pry'
712
gem 'pry-byebug'
813
gem 'warning'

‎Rakefile‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ namespace :benchmark do
1616
end
1717
end
1818

19+
desc "Run Sorbet typecheck"
20+
task :typecheck do
21+
sh "bundle exec srb tc"
22+
end
23+
1924
desc "Run benchmarks"
2025
task benchmark: "benchmark:planner"
2126

‎bin/tapioca‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
#
5+
# This file was generated by Bundler.
6+
#
7+
# The application 'tapioca' is installed as part of a gem, and
8+
# this file is here to facilitate running it.
9+
#
10+
11+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12+
13+
require "rubygems"
14+
require "bundler/setup"
15+
16+
load Gem.bin_path("tapioca", "tapioca")

‎lib/graphql/stitching.rb‎

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,42 @@
11
# frozen_string_literal: true
2+
# typed: true
23

34
require "graphql"
45

56
module GraphQL
67
module Stitching
7-
# scope name of query operations.
8-
QUERY_OP = "query"
9-
10-
# scope name of mutation operations.
11-
MUTATION_OP = "mutation"
12-
13-
# scope name of subscription operations.
14-
SUBSCRIPTION_OP = "subscription"
15-
16-
# introspection typename field.
17-
TYPENAME = "__typename"
18-
19-
# @api private
20-
EMPTY_OBJECT = {}.freeze
21-
22-
# @api private
23-
EMPTY_ARRAY = [].freeze
8+
QUERY_OP = "query".freeze #: String
9+
10+
MUTATION_OP = "mutation".freeze #: String
11+
12+
SUBSCRIPTION_OP = "subscription".freeze #: String
13+
14+
TYPENAME = "__typename".freeze #: String
15+
16+
EMPTY_OBJECT = {}.freeze #: Hash[untyped, untyped]
17+
18+
EMPTY_ARRAY = [].freeze #: Array[untyped]
2419

2520
class StitchingError < StandardError; end
2621
class CompositionError < StitchingError; end
2722
class ValidationError < CompositionError; end
2823
class DocumentError < StandardError
24+
#: (String element) -> void
2925
def initialize(element)
3026
super("Invalid #{element} encountered in document")
3127
end
3228
end
3329

30+
MIN_VISIBILITY_VERSION = "2.5.3".freeze #: String
31+
3432
class << self
35-
# Proc used to compute digests; uses SHA2 by default.
36-
# @returns [Proc] proc used to compute digests.
33+
# @rbs!
34+
# @digest: ^(String) -> String
35+
# @stitch_directive: String
36+
# @visibility_directive: String
37+
# @supports_visibility: bool
38+
39+
#: ?{ (String) -> String } -> ^(String) -> String
3740
def digest(&block)
3841
if block_given?
3942
@digest = block
@@ -42,25 +45,23 @@ def digest(&block)
4245
end
4346
end
4447

45-
# Name of the directive used to mark type resolvers.
46-
# @returns [String] name of the type resolver directive.
48+
#: -> String
4749
def stitch_directive
48-
@stitch_directive ||= "stitch"
50+
@stitch_directive ||= "stitch".freeze
4951
end
5052

53+
#: String
5154
attr_writer :stitch_directive
5255

53-
# Name of the directive used to denote member visibilities.
54-
# @returns [String] name of the visibility directive.
56+
#: -> String
5557
def visibility_directive
56-
@visibility_directive ||= "visibility"
58+
@visibility_directive ||= "visibility".freeze
5759
end
5860

61+
#: String
5962
attr_writer :visibility_directive
6063

61-
MIN_VISIBILITY_VERSION = "2.5.3"
62-
63-
# @returns Boolean true if GraphQL::Schema::Visibility is fully supported
64+
#: -> bool
6465
def supports_visibility?
6566
return @supports_visibility if defined?(@supports_visibility)
6667

‎lib/graphql/stitching/client.rb‎

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
11
# frozen_string_literal: true
2+
# typed: true
23

34
require "json"
45

56
module GraphQL
67
module Stitching
7-
# Client is an out-of-the-box helper that assembles all
8-
# stitching components into a workflow that executes requests.
98
class Client
109
class << self
10+
#: (String | singleton(GraphQL::Schema) schema, executables: Hash[Location | Symbol, Executable]) -> Client
1111
def from_definition(schema, executables:)
1212
new(supergraph: Supergraph.from_definition(schema, executables: executables))
1313
end
1414
end
1515

16-
# @return [Supergraph] composed supergraph that services incoming requests.
16+
#: Supergraph
1717
attr_reader :supergraph
1818

19-
# Builds a new client instance. Either `supergraph` or `locations` configuration is required.
20-
# @param supergraph [Supergraph] optional, a pre-composed supergraph that bypasses composer setup.
21-
# @param locations [Hash<Symbol, Hash<Symbol, untyped>>] optional, composer configurations for each graph location.
22-
# @param composer_options [Hash] optional, composer options for configuring composition.
19+
#: (?locations: untyped, ?supergraph: Supergraph?, ?composer_options: Hash[Symbol, untyped]) -> void
2320
def initialize(locations: nil, supergraph: nil, composer_options: {})
2421
@supergraph = if locations && supergraph
2522
raise ArgumentError, "Cannot provide both locations and a supergraph."
@@ -34,15 +31,26 @@ def initialize(locations: nil, supergraph: nil, composer_options: {})
3431
composer.perform(locations)
3532
end
3633

37-
@on_cache_read = nil
38-
@on_cache_write = nil
39-
@on_error = nil
34+
@on_cache_read = nil #: CacheReadHandler?
35+
@on_cache_write = nil #: CacheWriteHandler?
36+
@on_error = nil #: ErrorHandler?
4037
end
4138

39+
#: (
40+
#| ?String | DocumentNode | nil raw_query,
41+
#| ?query: String | DocumentNode | nil,
42+
#| ?variables: Variables?,
43+
#| ?operation_name: String?,
44+
#| ?context: untyped,
45+
#| ?validate: bool
46+
#| ) -> untyped
4247
def execute(raw_query = nil, query: nil, variables: nil, operation_name: nil, context: nil, validate: true)
48+
source = raw_query || query
49+
raise ArgumentError, "A query string or document is required." unless source
50+
4351
request = Request.new(
4452
@supergraph,
45-
raw_query || query, # << for parity with GraphQL Ruby Schema.execute
53+
source,
4654
operation_name: operation_name,
4755
variables: variables,
4856
context: context,
@@ -62,23 +70,27 @@ def execute(raw_query = nil, query: nil, variables: nil, operation_name: nil, co
6270
error_result(request, [{ "message" => custom_message || "An unexpected error occured." }])
6371
end
6472

73+
#: ?{ (Request) -> String? } -> CacheReadHandler
6574
def on_cache_read(&block)
66-
raise ArgumentError, "A cache read block is required." unless block_given?
75+
raise ArgumentError, "A cache read block is required." unless block
6776
@on_cache_read = block
6877
end
6978

79+
#: ?{ (Request, String) -> void } -> CacheWriteHandler
7080
def on_cache_write(&block)
71-
raise ArgumentError, "A cache write block is required." unless block_given?
81+
raise ArgumentError, "A cache write block is required." unless block
7282
@on_cache_write = block
7383
end
7484

85+
#: ?{ (Request?, StandardError) -> String? } -> ErrorHandler
7586
def on_error(&block)
76-
raise ArgumentError, "An error handler block is required." unless block_given?
87+
raise ArgumentError, "An error handler block is required." unless block
7788
@on_error = block
7889
end
7990

8091
private
8192

93+
#: (Request request) -> Plan
8294
def load_plan(request)
8395
if @on_cache_read && plan_json = @on_cache_read.call(request)
8496
plan = Plan.from_json(JSON.parse(plan_json))
@@ -98,6 +110,7 @@ def load_plan(request)
98110
plan
99111
end
100112

113+
#: (Request? request, Array[PublicErrorObject | PublicError] errors) -> GraphQL::Query::Result
101114
def error_result(request, errors)
102115
public_errors = errors.map! do |e|
103116
e.is_a?(Hash) ? e : e.to_h

0 commit comments

Comments
 (0)