Skip to content

Commit 7670057

Browse files
authored
Visibility followups (#174)
1 parent 110fd14 commit 7670057

4 files changed

Lines changed: 53 additions & 10 deletions

File tree

docs/visibility.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Visibility
22

3-
Visibility controls can hide parts of a supergraph from select audiences without compromising stitching operations. Restricted schema elements are hidden from introspection and validate as though they do not exist (which is different from traditional authorization where an element is acknowledged as restricted). Visibility is useful for managing multiple distributions of a schema for different audiences.
3+
Visibility controls can hide parts of a supergraph from select audiences without compromising stitching operations. Restricted schema elements are hidden from introspection and validate as though they do not exist (which is different from traditional authorization where an element is acknowledged as restricted). Visibility is useful for managing multiple distributions of a schema for different audiences, and provides a flexible analog to Apollo Federation's `@inaccessible` rule.
44

5-
Under the hood, this system wraps `GraphQL::Schema::Visibility` (with nil profile support) and requires at least GraphQL Ruby v2.5.3.
5+
Under the hood, this system wraps [GraphQL visibility](https://graphql-ruby.org/authorization/visibility) (specifically, the newer `GraphQL::Schema::Visibility` with nil profile support) and requires at least GraphQL Ruby v2.5.3.
66

77
## Example
88

@@ -41,7 +41,7 @@ type Query {
4141
}
4242
```
4343

44-
When composing a stitching client, the names of all possible visibility profiles that the supergraph should respond to must be specified in composer options:
44+
When composing a stitching client, the names of all possible visibility profiles that the supergraph should respond to are specified in composer options:
4545

4646
```ruby
4747
client = GraphQL::Stitching::Client.new(
@@ -61,7 +61,7 @@ client = GraphQL::Stitching::Client.new(
6161
)
6262
```
6363

64-
The client can then execute requests with a `visibility_profile` parameter in context that specifies the name of any profile the supergraph was composed with:
64+
The client can then execute requests with a `visibility_profile` parameter in context that specifies one of these names:
6565

6666
```ruby
6767
query = %|{
@@ -84,7 +84,7 @@ The `visibility_profile` parameter will select which visibility distribution to
8484
- Using `visibility_profile: "private"` will accesses the `msrp` field as usual.
8585
- Providing no profile parameter (or `visibility_profile: nil`) will access the entire graph without any visibility constraints.
8686

87-
The full potential of visibility comes when hiding stitching implementation details, such as the `id` field (which is the stitching key for the Product type). While the `id` field is hidden from all named profiles, it remains operational for the stitching implementation.
87+
The full potential of visibility comes when hiding stitching implementation details, such as the `id` field (which is the stitching key for the Product type). While the `id` field is hidden from all named profiles, it remains operational for use by the stitching implementation.
8888

8989
## Adding visibility directives
9090

@@ -165,4 +165,14 @@ type Query {
165165
}
166166
```
167167

168-
In this example, hiding the `Widget` type will also hide the `Query.widget` field that returns it.
168+
In this example, hiding the `Widget` type will also hide the `Query.widget` field that returns it. You can review materialized visibility profiles by printing their respective schemas:
169+
170+
```ruby
171+
public_schema = client.supergraph.to_definition(visibility_profile: "public")
172+
File.write("schemas/supergraph_public.graphql", public_schema)
173+
174+
private_schema = client.supergraph.to_definition(visibility_profile: "private")
175+
File.write("schemas/supergraph_private.graphql", private_schema)
176+
```
177+
178+
It's helpful to commit these outputs to your repo where you can monitor their diffs during the PR process.

lib/graphql/stitching/supergraph.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,10 @@ def initialize(schema:, fields: {}, resolvers: {}, visibility_profiles: [], exec
5858
end
5959
end
6060

61-
def to_definition
62-
@schema.to_definition
61+
def to_definition(visibility_profile: nil)
62+
@schema.to_definition(context: {
63+
visibility_profile: visibility_profile,
64+
}.tap(&:compact!))
6365
end
6466

6567
def resolvers_by_version

test/graphql/stitching/supergraph/visibility_test.rb

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,37 @@ def test_activates_visibility_profile_definitions_and_nil_profile
2424
assert_equal ["", "private", "public"], profiles.sort
2525
end
2626

27+
def test_to_definition_prints_specific_profile
28+
schema_sdl = %|
29+
#{visibility_definition_with_profiles(["public", "private"])}
30+
type Query {
31+
a: String
32+
b: String @visibility(profiles: ["private"])
33+
c: String @visibility(profiles: [])
34+
}
35+
|
36+
37+
expected_public = %|
38+
#{visibility_definition_with_profiles(["public", "private"])}
39+
type Query {
40+
a: String
41+
}
42+
|
43+
44+
expected_private = %|
45+
#{visibility_definition_with_profiles(["public", "private"])}
46+
type Query {
47+
a: String
48+
b: String @visibility(profiles: ["private"])
49+
}
50+
|
51+
52+
supergraph = GraphQL::Stitching::Supergraph.from_definition(schema_sdl, executables: @exec)
53+
assert_equal squish_string(schema_sdl), squish_string(supergraph.to_definition)
54+
assert_equal squish_string(expected_public), squish_string(supergraph.to_definition(visibility_profile: "public"))
55+
assert_equal squish_string(expected_private), squish_string(supergraph.to_definition(visibility_profile: "private"))
56+
end
57+
2758
def test_controls_field_visibility
2859
schema_sdl = %|
2960
#{visibility_definition_with_profiles(["public", "private"])}
@@ -337,7 +368,7 @@ def test_controls_union_visibility
337368
def visibility_definition_with_profiles(profiles)
338369
VISIBILITY_DEFINITION.sub(
339370
%|@visibility(profiles: [String!]!)|,
340-
%|@visibility(profiles: [String!]! = #{profiles.to_json})|,
371+
%|@visibility(profiles: [String!]! = #{profiles.to_json.gsub!(%|","|, %|", "|)})|,
341372
)
342373
end
343374

test/test_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
CompositionError = GraphQL::Stitching::CompositionError
2121
ValidationError = GraphQL::Stitching::ValidationError
2222
STITCH_DEFINITION = "directive @stitch(key: String!, arguments: String, typeName: String) repeatable on FIELD_DEFINITION\n"
23-
VISIBILITY_DEFINITION = "directive @visibility(profiles: [String!]!) on OBJECT | INTERFACE | UNION | INPUT_OBJECT | ENUM | SCALAR | FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE\n"
23+
VISIBILITY_DEFINITION = "directive @visibility(profiles: [String!]!) on ARGUMENT_DEFINITION | ENUM | ENUM_VALUE | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION\n"
2424

2525
class Matcher
2626
def match?(value)

0 commit comments

Comments
 (0)