Skip to content

Commit 4e58054

Browse files
modified: juniper/src/lib.rs
modified: juniper/src/schema/model.rs# modified: juniper/src/schema/translate/graphql_parser.rs Added new function to create a schema with custom directives Added doctest Added directives to be included in the schema output
1 parent dee3890 commit 4e58054

File tree

3 files changed

+190
-8
lines changed

3 files changed

+190
-8
lines changed

juniper/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ pub use crate::{
8585
parser::{ParseError, ScalarToken, Span, Spanning},
8686
schema::{
8787
meta,
88-
model::{RootNode, SchemaType},
88+
model::{RootNode, SchemaType, DirectiveType, DirectiveLocation},
8989
},
9090
types::{
9191
async_await::{GraphQLTypeAsync, GraphQLValueAsync},

juniper/src/schema/model.rs

Lines changed: 137 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::{borrow::Cow, fmt};
22

33
use fnv::FnvHashMap;
4+
45
#[cfg(feature = "schema-language")]
56
use graphql_parser::schema::Document;
67

@@ -53,7 +54,7 @@ pub struct SchemaType<'a, S> {
5354
pub(crate) query_type_name: String,
5455
pub(crate) mutation_type_name: Option<String>,
5556
pub(crate) subscription_type_name: Option<String>,
56-
directives: FnvHashMap<String, DirectiveType<'a, S>>,
57+
pub(crate) directives: FnvHashMap<String, DirectiveType<'a, S>>,
5758
}
5859

5960
impl<S> Context for SchemaType<'_, S> {}
@@ -66,33 +67,51 @@ pub enum TypeType<'a, S: 'a> {
6667
}
6768

6869
#[derive(Debug)]
70+
/// Represents a graphql directive
6971
pub struct DirectiveType<'a, S> {
72+
/// required a unique name per schema
7073
pub name: String,
74+
/// optional a description
7175
pub description: Option<String>,
76+
/// required atleast a location
7277
pub locations: Vec<DirectiveLocation>,
78+
/// Optional arguments
7379
pub arguments: Vec<Argument<'a, S>>,
80+
/// Allow repeating a directive
7481
pub is_repeatable: bool,
7582
}
7683

7784
#[derive(Clone, PartialEq, Eq, Debug, GraphQLEnum)]
7885
#[graphql(name = "__DirectiveLocation", internal)]
86+
/// Describes the different allowed locations of a directive
7987
pub enum DirectiveLocation {
88+
/// directive for query
8089
Query,
90+
/// directive for Mutation
8191
Mutation,
92+
/// directive for Subscription
8293
Subscription,
94+
/// directive for Field
8395
Field,
96+
/// directive for Scalar
8497
Scalar,
8598
#[graphql(name = "FRAGMENT_DEFINITION")]
99+
/// directive for FragmentDefinition
86100
FragmentDefinition,
87101
#[graphql(name = "FIELD_DEFINITION")]
102+
/// directive for FieldDefinition
88103
FieldDefinition,
89104
#[graphql(name = "VARIABLE_DEFINITION")]
105+
/// directive for VariableDefinition
90106
VariableDefinition,
91107
#[graphql(name = "FRAGMENT_SPREAD")]
108+
/// directive for FragmentSpread
92109
FragmentSpread,
93110
#[graphql(name = "INLINE_FRAGMENT")]
111+
/// directive for InlineFragment
94112
InlineFragment,
95113
#[graphql(name = "ENUM_VALUE")]
114+
/// directive for Enum
96115
EnumValue,
97116
}
98117

@@ -104,7 +123,7 @@ where
104123
SubscriptionT: GraphQLType<DefaultScalarValue, TypeInfo = ()>,
105124
{
106125
/// Constructs a new [`RootNode`] from `query`, `mutation` and `subscription` nodes,
107-
/// parametrizing it with a [`DefaultScalarValue`].
126+
/// parametrizing it with a [`DefaultScalarValue`] .
108127
pub fn new(query: QueryT, mutation: MutationT, subscription: SubscriptionT) -> Self {
109128
Self::new_with_info(query, mutation, subscription, (), (), ())
110129
}
@@ -118,14 +137,67 @@ where
118137
SubscriptionT: GraphQLType<S, TypeInfo = ()>,
119138
{
120139
/// Constructs a new [`RootNode`] from `query`, `mutation` and `subscription` nodes,
121-
/// parametrizing it with the provided [`ScalarValue`].
140+
/// parametrized it with the provided [`ScalarValue`].
122141
pub fn new_with_scalar_value(
123142
query: QueryT,
124143
mutation: MutationT,
125144
subscription: SubscriptionT,
126145
) -> Self {
127146
RootNode::new_with_info(query, mutation, subscription, (), (), ())
128147
}
148+
149+
/// Constructs a new [`RootNode`] from `query`, `mutation` and `subscription` nodes,
150+
/// parametrized it with a [`ScalarValue`] and directives
151+
/// ```rust
152+
/// use juniper::{
153+
/// graphql_object, graphql_vars, EmptyMutation, EmptySubscription, GraphQLError,
154+
/// RootNode, DirectiveLocation , DirectiveType
155+
/// };
156+
///
157+
/// struct Query{}
158+
///
159+
/// #[graphql_object]
160+
/// impl Query {
161+
/// pub fn hello() -> String {
162+
/// "Hello".to_string()
163+
/// }
164+
/// }
165+
///
166+
/// type Schema = RootNode<'static, Query, EmptyMutation, EmptySubscription>;
167+
///
168+
/// let schema = Schema::new_with_directives(Query {}, EmptyMutation::new(), EmptySubscription::new()
169+
/// ,vec![ DirectiveType::new("my_directive", &[DirectiveLocation::Query] , &[] , false )]);
170+
///
171+
/// let query = "query @my_directive { hello }";
172+
///
173+
/// match juniper::execute_sync(query, None, &schema, &graphql_vars! {}, &()) {
174+
/// Err(GraphQLError::ValidationError(errs)) => { panic!("should not give an error"); }
175+
/// res => {}
176+
/// }
177+
///
178+
/// let query = "query @non_existing_directive { hello }";
179+
///
180+
/// match juniper::execute_sync(query, None, &schema, &graphql_vars! {}, &()) {
181+
/// Err(GraphQLError::ValidationError(errs)) => { }
182+
/// res => { panic!("should give an error"); }
183+
/// }
184+
/// ```
185+
pub fn new_with_directives(
186+
query: QueryT,
187+
mutation: MutationT,
188+
subscription: SubscriptionT,
189+
custom_directives: Vec<DirectiveType<'a, S>>,
190+
) -> Self {
191+
Self::new_with_directives_and_info(
192+
query,
193+
mutation,
194+
subscription,
195+
custom_directives,
196+
(),
197+
(),
198+
(),
199+
)
200+
}
129201
}
130202

131203
impl<'a, S, QueryT, MutationT, SubscriptionT> RootNode<'a, QueryT, MutationT, SubscriptionT, S>
@@ -162,6 +234,34 @@ where
162234
}
163235
}
164236

237+
/// Construct a new root node with default meta types
238+
/// and with custom directives
239+
pub fn new_with_directives_and_info(
240+
query_obj: QueryT,
241+
mutation_obj: MutationT,
242+
subscription_obj: SubscriptionT,
243+
custom_directives: Vec<DirectiveType<'a, S>>,
244+
query_info: QueryT::TypeInfo,
245+
mutation_info: MutationT::TypeInfo,
246+
subscription_info: SubscriptionT::TypeInfo,
247+
) -> Self {
248+
Self {
249+
query_type: query_obj,
250+
mutation_type: mutation_obj,
251+
subscription_type: subscription_obj,
252+
schema: SchemaType::new_with_directives::<QueryT, MutationT, SubscriptionT>(
253+
&query_info,
254+
&mutation_info,
255+
&subscription_info,
256+
custom_directives.into(),
257+
),
258+
query_info,
259+
mutation_info,
260+
subscription_info,
261+
introspection_disabled: false,
262+
}
263+
}
264+
165265
/// Disables introspection for this [`RootNode`], making it to return a [`FieldError`] whenever
166266
/// its `__schema` or `__type` field is resolved.
167267
///
@@ -257,12 +357,29 @@ where
257357
}
258358

259359
impl<'a, S> SchemaType<'a, S> {
360+
260361
/// Create a new schema.
261362
pub fn new<QueryT, MutationT, SubscriptionT>(
262363
query_info: &QueryT::TypeInfo,
263364
mutation_info: &MutationT::TypeInfo,
264365
subscription_info: &SubscriptionT::TypeInfo,
265366
) -> Self
367+
where
368+
S: ScalarValue + 'a,
369+
QueryT: GraphQLType<S>,
370+
MutationT: GraphQLType<S>,
371+
SubscriptionT: GraphQLType<S>,
372+
{
373+
Self::new_with_directives::<QueryT,MutationT,SubscriptionT>(query_info, mutation_info, subscription_info, None)
374+
}
375+
376+
/// Create a new schema with custom directives
377+
pub fn new_with_directives<QueryT, MutationT, SubscriptionT>(
378+
query_info: &QueryT::TypeInfo,
379+
mutation_info: &MutationT::TypeInfo,
380+
subscription_info: &SubscriptionT::TypeInfo,
381+
custom_directives: Option<Vec<DirectiveType<'a, S>>>,
382+
) -> Self
266383
where
267384
S: ScalarValue + 'a,
268385
QueryT: GraphQLType<S>,
@@ -298,6 +415,12 @@ impl<'a, S> SchemaType<'a, S> {
298415
DirectiveType::new_specified_by(&mut registry),
299416
);
300417

418+
if let Some(custom_directives) = custom_directives {
419+
for custom_directive in custom_directives.into_iter() {
420+
directives.insert(custom_directive.name.clone(), custom_directive);
421+
}
422+
}
423+
301424
let mut meta_fields = vec![
302425
registry.field::<SchemaType<S>>("__schema", &()),
303426
registry
@@ -585,6 +708,7 @@ impl<'a, S> DirectiveType<'a, S>
585708
where
586709
S: ScalarValue + 'a,
587710
{
711+
/// Create a new default directive
588712
pub fn new(
589713
name: &str,
590714
locations: &[DirectiveLocation],
@@ -600,6 +724,15 @@ where
600724
}
601725
}
602726

727+
/// skip,include,deprecated,specifiedBy are standard graphQL directive
728+
/// wiil not show up in generated scheme
729+
pub fn is_builtin(&self) -> bool {
730+
match self.name.as_str() {
731+
"skip" | "include" | "deprecated" | "specifiedBy" => true,
732+
_ => false,
733+
}
734+
}
735+
603736
fn new_skip(registry: &mut Registry<'a, S>) -> DirectiveType<'a, S>
604737
where
605738
S: ScalarValue,
@@ -659,6 +792,7 @@ where
659792
)
660793
}
661794

795+
/// Set description of directive
662796
pub fn description(mut self, description: &str) -> DirectiveType<'a, S> {
663797
self.description = Some(description.into());
664798
self

juniper/src/schema/translate/graphql_parser.rs

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@ use graphql_parser::{
44
Pos,
55
query::{Directive as ExternalDirective, Number as ExternalNumber, Type as ExternalType},
66
schema::{
7-
Definition, Document, EnumType as ExternalEnum, EnumValue as ExternalEnumValue,
8-
Field as ExternalField, InputObjectType as ExternalInputObjectType,
9-
InputValue as ExternalInputValue, InterfaceType as ExternalInterfaceType,
10-
ObjectType as ExternalObjectType, ScalarType as ExternalScalarType, SchemaDefinition, Text,
7+
Definition, DirectiveDefinition as ExternalDirectiveDefinition,
8+
DirectiveLocation as ExternalDirectiveLocation, Document, EnumType as ExternalEnum,
9+
EnumValue as ExternalEnumValue, Field as ExternalField,
10+
InputObjectType as ExternalInputObjectType, InputValue as ExternalInputValue,
11+
InterfaceType as ExternalInterfaceType, ObjectType as ExternalObjectType,
12+
ScalarType as ExternalScalarType, SchemaDefinition, Text,
1113
TypeDefinition as ExternalTypeDefinition, UnionType as ExternalUnionType,
1214
Value as ExternalValue,
1315
},
1416
};
1517

1618
use crate::{
19+
DirectiveLocation,
1720
ast::{InputValue, Type},
1821
schema::{
1922
meta::{Argument, DeprecationStatus, EnumValue, Field, MetaType},
@@ -76,11 +79,56 @@ where
7679
.map(|s| From::from(s.as_str())),
7780
}));
7881

82+
let mut directives = input
83+
.directives
84+
.iter()
85+
.filter(|(_, directive)| !directive.is_builtin())
86+
.map(|(_, directive)| ExternalDirectiveDefinition::<T> {
87+
position: Pos::default(),
88+
description: directive.description.clone(),
89+
name: From::from(directive.name.as_str()),
90+
arguments: directive
91+
.arguments
92+
.iter()
93+
.map(GraphQLParserTranslator::translate_argument)
94+
.collect(),
95+
repeatable: directive.is_repeatable,
96+
locations: directive
97+
.locations
98+
.iter()
99+
.map(GraphQLParserTranslator::translate_location::<S,T>)
100+
.collect(),
101+
})
102+
.map(Definition::DirectiveDefinition)
103+
.collect();
104+
105+
doc.definitions.append(&mut directives);
106+
79107
doc
80108
}
81109
}
82110

83111
impl GraphQLParserTranslator {
112+
fn translate_location<'a, S, T>(location: &DirectiveLocation) -> ExternalDirectiveLocation
113+
where
114+
S: ScalarValue,
115+
T: Text<'a>,
116+
{
117+
match location {
118+
DirectiveLocation::Query => ExternalDirectiveLocation::Query,
119+
DirectiveLocation::Mutation => ExternalDirectiveLocation::Mutation,
120+
DirectiveLocation::Subscription => ExternalDirectiveLocation::Subscription,
121+
DirectiveLocation::Field => ExternalDirectiveLocation::Field,
122+
DirectiveLocation::Scalar => ExternalDirectiveLocation::Scalar,
123+
DirectiveLocation::FragmentDefinition => ExternalDirectiveLocation::FragmentDefinition,
124+
DirectiveLocation::FieldDefinition => ExternalDirectiveLocation::FieldDefinition,
125+
DirectiveLocation::VariableDefinition => ExternalDirectiveLocation::VariableDefinition,
126+
DirectiveLocation::FragmentSpread => ExternalDirectiveLocation::FragmentSpread,
127+
DirectiveLocation::InlineFragment => ExternalDirectiveLocation::InlineFragment,
128+
DirectiveLocation::EnumValue => ExternalDirectiveLocation::EnumValue,
129+
}
130+
}
131+
84132
fn translate_argument<'a, S, T>(input: &'a Argument<S>) -> ExternalInputValue<'a, T>
85133
where
86134
S: ScalarValue,

0 commit comments

Comments
 (0)