Skip to content

Commit

Permalink
Initial working implementation of execution
Browse files Browse the repository at this point in the history
This commit is a first take at porting over execution from the
TypeScript implementation as well as surrounding scaffolding for a PoC
server runtime and process manager. This is an initial stab for feedback
and will be split up into multiple commits for review in different PRs
around structure, execution, and basic server scaffold after testing is
done.
  • Loading branch information
jbaxleyiii authored and Ran Magen committed Sep 25, 2020
1 parent 378eb6b commit 9b6a131
Show file tree
Hide file tree
Showing 20 changed files with 1,201 additions and 704 deletions.
694 changes: 0 additions & 694 deletions Cargo.lock

This file was deleted.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
members = [
"stargate",
"graphql-parser",
"query-planner",
"query-planner-wasm"
Expand Down
11 changes: 3 additions & 8 deletions query-planner/src/federation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use graphql_parser::schema::*;
use graphql_parser::{parse_query, Pos};
use std::collections::HashMap;

use crate::helpers::directive_args_as_map;

#[derive(Debug, PartialEq)]
struct FederationTypeMetadata<'q> {
is_value_type: HashMap<Pos, bool>,
Expand Down Expand Up @@ -185,14 +187,7 @@ fn as_selection_set_ref(value: &str) -> query::SelectionSet {
letp!(query::Definition::SelectionSet(ss) = ss => ss)
}

fn directive_args_as_map<'q>(args: &'q [(Txt<'q>, Value<'q>)]) -> HashMap<Txt<'q>, Txt<'q>> {
args.iter()
.map(|(k, v)| {
let str = letp!(Value::String(str) = v => str);
(*k, str.as_str())
})
.collect()
}


// lazy_static! {
// static ref STRINGS: Mutex<Vec<String>> = Mutex::new(vec![]);
Expand Down
9 changes: 9 additions & 0 deletions query-planner/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,15 @@ pub fn is_not_introspection_field(selection: &SelectionRef) -> bool {
}
}

pub fn directive_args_as_map<'q>(args: &'q [(Txt<'q>, Value<'q>)]) -> HashMap<Txt<'q>, Txt<'q>> {
args.iter()
.map(|(k, v)| {
let str = letp!(Value::String(str) = v => str);
(*k, str.as_str())
})
.collect()
}

pub trait Head<T> {
/// gets the head and tail of a vector
fn head(self) -> (T, Vec<T>);
Expand Down
5 changes: 3 additions & 2 deletions query-planner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ mod consts;
mod context;
mod federation;
mod groups;
mod helpers;
pub mod helpers;
pub mod model;
mod visitors;

Expand All @@ -30,8 +30,9 @@ pub enum QueryPlanError {

pub type Result<T> = std::result::Result<T, QueryPlanError>;

#[derive(Clone)]
pub struct QueryPlanner<'s> {
schema: schema::Document<'s>,
pub schema: schema::Document<'s>,
}

impl<'s> QueryPlanner<'s> {
Expand Down
22 changes: 22 additions & 0 deletions stargate/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "stargate"
version = "0.1.0"
authors = ["jbaxleyiii <james@apollographql.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tide = "0.13.0"
async-trait = "0.1.40"
async-std = { version = "1.6.0", features = ["attributes"] }
graphql-parser = { path = "../graphql-parser" }
apollo-query-planner = { path = "../query-planner" }
async-graphql = "1.17.21"
serde = { version = "1.0.115", features = ["derive"] }
serde_json = "1.0.57"
futures = "0.3.5"
surf = "1.0.3"
http-types = "2.4.0"
structopt = "0.3.15"
tide-compress = "0.5.0"
19 changes: 19 additions & 0 deletions stargate/fixtures/accounts.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
directive @date(defaultFormat: String = "mmmm d, yyyy") on FIELD_DEFINITION
extend type Query {
"""
The currently authenticated user root. All nodes off of this
root will be authenticated as the current user
"""
me: User
}
"The base User in Acephei"
type User @key(fields: "id") {
"A globally unique id for the user"
id: ID!
"The formatted date a user was created in our system"
created: String
"The users full name as provided"
name: String
"The account username of the user"
username: String
}
11 changes: 11 additions & 0 deletions stargate/fixtures/books.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"The basic book in the graph"
type Book @key(fields: "isbn") {
"All books can be found by an isbn"
isbn: String!
"The title of the book"
title: String
"The year the book was published"
year: Int
"A simple list of similar books"
similarBooks: [Book]
}
80 changes: 80 additions & 0 deletions stargate/fixtures/products.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"Information about the brand Amazon"
type Amazon {
"The url of a referrer for a product"
referrer: String
}
extend type Book implements Product @key(fields: "isbn") {
isbn: String! @external
title: String @external
year: Int @external
"Since books are now products, we can also use their upc as a primary id"
upc: String!
"The name of a book is the book's title + year published"
name(delimeter: String = " "): String @requires(fields: "title year")
price: Int
weight: Int
}
"A union of all brands represented within the store"
union Brand = Ikea | Amazon
"""
The Furniture type represents all products which are items
of furniture.
"""
type Furniture implements Product @key(fields: "upc") @key(fields: "sku") {
"The modern primary identifier for furniture"
upc: String!
"The SKU field is how furniture was previously stored, and still exists in some legacy systems"
sku: String!
name: String
price: Int
"The brand of furniture"
brand: Brand
weight: Int
}
"Information about the brand Ikea"
type Ikea {
"Which asile to find an item"
asile: Int
}
"""
The PageInfo type provides pagination helpers for determining
if more data can be fetched from the list
"""
type PageInfo {
"More items exist in the list"
hasNextPage: Boolean
"Items earlier in the list exist"
hasPreviousPage: Boolean
}
"The Product type represents all products within the system"
interface Product {
"The primary identifier of products in the graph"
upc: String!
"The display name of the product"
name: String
"A simple integer price of the product in US dollars"
price: Int
"How much the product weighs in kg"
weight: Int @deprecated(reason: "Not all product's have a weight")
}
"A connection wrapper for lists of products"
type ProductConnection {
"Helpful metadata about the connection"
pageInfo: PageInfo
"List of products returned by the search"
edges: [ProductEdge]
}
"A connection edge for the Product type"
type ProductEdge {
product: Product
}
enum ProductType {
LATEST
TRENDING
}
extend type Query {
"Fetch a simple list of products with an offset"
topProducts(first: Int = 5): [Product] @deprecated(reason: "Use `products` instead")
"Fetch a paginated list of products based on a filter type."
products(first: Int = 5, after: Int = 0, type: ProductType): ProductConnection
}
70 changes: 70 additions & 0 deletions stargate/fixtures/reviews.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
extend type Book implements Product @key(fields: "isbn") {
isbn: String! @external
similarBooks: [Book] @external
reviews: [Review]
reviewList(first: Int = 5, after: Int = 0): ReviewConnection
"""
relatedReviews for a book use the knowledge of `similarBooks` from the books
service to return related reviews that may be of interest to the user
"""
relatedReviews(first: Int = 5, after: Int = 0): ReviewConnection @requires(fields: "similarBooks { isbn }")
}
extend type Furniture implements Product @key(fields: "upc") {
upc: String! @external
reviews: [Review]
reviewList(first: Int = 5, after: Int = 0): ReviewConnection
}
type PageInfo {
hasNextPage: Boolean
hasPreviousPage: Boolean
}
extend interface Product {
"A simple list of all reviews for a product"
reviews: [Review] @deprecated(reason: "The `reviews` field on product is deprecated to roll over the return\ntype from a simple list to a paginated list. The easiest way to fix your\noperations is to alias the new field `reviewList` to `review`:\n \n {\n ... on Product {\n reviews: reviewList {\n edges {\n review {\n body\n }\n }\n }\n }\n }\n\nOnce all clients have updated, we will roll over this field and deprecate\n`reviewList` in favor of the field name `reviews` again")
"""
A paginated list of reviews. This field naming is temporary while all clients
migrate off of the un-paginated version of this field call reviews. To ease this migration,
alias your usage of `reviewList` to `reviews` so that after the roll over is finished, you
can remove the alias and use the final field name:
{
... on Product {
reviews: reviewList {
edges {
review {
body
}
}
}
}
}
"""
reviewList(first: Int = 5, after: Int = 0): ReviewConnection
}
"A review is any feedback about products across the graph"
type Review @key(fields: "id") {
id: ID!
"The plain text version of the review"
body: String
"The user who authored the review"
author: User @provides(fields: "username")
"The product which this review is about"
product: Product
}
"A connection wrapper for lists of reviews"
type ReviewConnection {
"Helpful metadata about the connection"
pageInfo: PageInfo
"List of reviews returned by the search"
edges: [ReviewEdge]
}
"A connection edge for the Review type"
type ReviewEdge {
review: Review
}
extend type User @key(fields: "id") {
id: ID! @external
username: String @external
"A list of all reviews by the user"
reviews: [Review]
}
Loading

0 comments on commit 9b6a131

Please sign in to comment.