cancel
Showing results for 
Search instead for 
Did you mean: 

Stuck on the last stage of the GraphQL course

bluabaleno
Node Clone

Hey there, I'm not too familiar with JWT token and I can't seems to figure out how to create a new users with the admin token.

I thought you were suppose to modify the GraphQL schema to add

extend type Customer @auth(rules: [{operations: [CREATE, UPDATE, DELETE], roles: ["admin"]}])

But I can't seem to use mutation to create a new Customer still?

Any help?

3 REPLIES 3

William_Lyon
Graph Fellow

Can you share the request header and the GraphQL mutation you're trying?

Yep sure thing.

Http request header:

{
  "authorization" :"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJCb2JMb2JsYXc3Njg3Iiwicm9sZXMiOlsiYWRtaW4iXSwiaWF0IjoxNTE2MjM5MDIyfQ.f2GKIu31gz39fMJwj5_byFCMDPDy3ncdWOIhhqcwBxk"
}

GraphQL mutation:

mutation {
  createCustomers(
    input: [
      {
        username: "BookWorm"
        reviews: {
          create: [
            {
              rating: 10
              text: "Why not"
              book: { connect: { where: { isbn: "190962151X" } } }
            }
          ]
        }
        orders: {
          create: {
            books: {
              connect: [
                { where: { title: "Ross Poldark" } }
                { where: { isbn: "1119387507" } }
                { where: { isbn: "1492047686" } }
              ]
            }
            shipTo: {
              create: {
                address: "Nowhere"
                location: { latitude: 55.6122270502, longitude: 12.99481772774 }
              }
            }
          }
        }
      }
    ]
  )
}

Here is my GraphQL Schema for good measures:

type Order {
  orderID: ID! @id
  placedAt: DateTime @timestamp
  shippingCost: Float
  shipTo: Address @relationship(type: "SHIPS_TO", direction: OUT)
  customer: Customer @relationship(type: "PLACED", direction: IN)
  books: [Book] @relationship(type: "CONTAINS", direction: OUT)
}

extend type Order {
  subTotal: Float @cypher(statement:"MATCH (this)-[:CONTAINS]->(b:Book) RETURN sum(b.price)")
  shippingCost: Float @cypher(statement:"MATCH (this)-[:SHIPS_TO]->(a:Address) RETURN round(0.01 * distance(a.location, Point({latitude: 40.7128, longitude: -74.0060})) / 1000, 2)")
  estimatedDelivery: DateTime @ignore
}

extend type Order @auth(rules: [{allow: {customer: {username: "$jwt.sub"}}}, {roles: ["admin"]}])

type Customer {
  username: String
  orders: [Order] @relationship(type: "PLACED", direction: OUT)
  reviews: [Review] @relationship(type: "WROTE", direction: OUT)
  recommended(limit: Int = 3): [Book] @cypher(statement: "MATCH (this)-[:PLACED]->(:Order)-[:CONTAINS]->(:Book)<-[:CONTAINS]-(:Order)<-[:PLACED]-(c:Customer) MATCH (c)-[:PLACED]->(:Order)-[:CONTAINS]->(rec:Book) WHERE NOT EXISTS((this)-[:PLACED]->(:Order)-[:CONTAINS]->(rec)) RETURN rec LIMIT toInteger($limit)")
}

extend type Customer @auth(rules: [{where: {username: "$jwt.sub"}}])

extend type Customer @auth(rules: [{operations: [CREATE, UPDATE, DELETE], roles: ["admin"]}])

type Address {
  address: String
  location: Point
  order: Order @relationship(type: "SHIPS_TO", direction: IN)
}

extend type Address {
  currentWeather: Weather @cypher(statement:"CALL apoc.load.json('https://www.7timer.info/bin/civil.php?lon=' + this.location.longitude + '&lat=' + this.location.latitude + '&ac=0&unit=metric&output=json&tzshift=0') YIELD value WITH value.dataseries[0] as weather RETURN {temperature: toFloat(weather.temp2m), windSpeed: toFloat(weather.wind10m.speed), windDirection: weather.wind10m.direction, precipitation: weather.prec_type, summary: weather.weather} AS conditions")
}

type Weather {
  temperature: Float
  windSpeed: Float
  windDirection: String
  precipitation: String
  summary: String
}

type Book {
  isbn: ID!
  title: String
  price: Float
  description: String
  authors: [Author] @relationship(type: "AUTHOR_OF", direction: IN)
  subjects: [Subject] @relationship(type: "ABOUT", direction: OUT)
  reviews: [Review] @relationship(type: "REVIEWS", direction: IN)
}

extend type Book @auth(rules: [{operations: [CREATE, UPDATE, DELETE], roles: ["admin"]}])


extend type Book {
  similar: [Book] @cypher(statement: """
  MATCH (this)-[:ABOUT]->(s:Subject)<-[:ABOUT]-(rec:Book)
  WITH rec, COUNT(*) AS num
  RETURN rec ORDER BY num DESC
  """)
}

type Review {
  rating: Int
  text: String
  createdAt: DateTime @timestamp
  book: Book @relationship(type: "REVIEWS", direction: OUT)
  author: Customer @relationship(type: "WROTE", direction: IN)
}

extend type Review @auth(rules: [{operations: [CREATE,UPDATE], bind: {author: {username: "$jwt.sub"} }}])

type Author {
  name: String!
  books: [Book] @relationship(type: "AUTHOR_OF", direction: OUT)
}

type Subject {
  name: String!
  books: [Book] @relationship(type: "ABOUT", direction: IN)
}

extend type Subject @auth(rules: [{isAuthenticated: true}])

type Mutation {
  mergeBookSubjects(subject: String!, bookTitles: [String!]!): Subject @cypher(statement: """
  MERGE (s:Subject {name: $subject})
  WITH s
  UNWIND $bookTitles AS bookTitle
  MATCH (t:Book {title: bookTitle})
  MERGE (t)-[:ABOUT]->(s)
  RETURN s
  """)
}

type Query {
  bookSearch(searchString: String!): [Book] @cypher(statement: """
  CALL db.index.fulltext.queryNodes('bookIndex', $searchString+'~')
  YIELD node RETURN node
  """)
}

extend type Query {
 booksForCurrentUser: [Book] @auth(rules: [{ isAuthenticated: true }]) @cypher(statement: """
 MATCH (c:Customer {username: $auth.jwt.sub})-[:PLACED]->(:Order)-[:CONTAINS]->(b:Book)
 MATCH (b)-[:ABOUT]->(s:Subject)<-[:ABOUT]-(rec:Book)
 WITH rec, COUNT(*) AS score ORDER BY score DESC
 RETURN rec
 """)
} 

error message:

{
  "error": {
    "errors": [
      {
        "message": "Field \"createCustomers\" of type \"CreateCustomersMutationResponse!\" must have a selection of subfields. Did you mean \"createCustomers { ... }\"?",
        "locations": [
          {
            "line": 2,
            "column": 3
          }
        ],
        "extensions": {
          "code": "GRAPHQL_VALIDATION_FAILED",
          "exception": {
            "stacktrace": [
              "GraphQLError: Field \"createCustomers\" of type \"CreateCustomersMutationResponse!\" must have a selection of subfields. Did you mean \"createCustomers { ... }\"?",
              "    at Object.Field (/sandbox/node_modules/graphql/validation/rules/ScalarLeafsRule.js:40:31)",
              "    at Object.enter (/sandbox/node_modules/graphql/language/visitor.js:323:29)",
              "    at Object.enter (/sandbox/node_modules/graphql/utilities/TypeInfo.js:370:25)",
              "    at visit (/sandbox/node_modules/graphql/language/visitor.js:243:26)",
              "    at Object.validate (/sandbox/node_modules/graphql/validation/validate.js:69:24)",
              "    at validate (/sandbox/node_modules/apollo-server-core/dist/requestPipeline.js:233:34)",
              "    at Object.<anonymous> (/sandbox/node_modules/apollo-server-core/dist/requestPipeline.js:119:42)",
              "    at Generator.next (<anonymous>)",
              "    at fulfilled (/sandbox/node_modules/apollo-server-core/dist/requestPipeline.js:5:58)",
              "    at processTicksAndRejections (internal/process/task_queues.js:93:5)"
            ]
          }
        }
      }
    ]
  }
}

"CreateCustomersMutationResponse!\" must have a selection of subfields. Did you mean \"createCustomers { ... }\"?"

Seems like you're having issues with the mutation itself and not JWT.

did you specify what data should be returned.. Looks like you need to add the fields that need to be returned by the mutation.

add what's inside the '{}' like this;

mutation{
Customer(
 // add you params here
){
  username
  orders{
   orderID
   placedAt
   shippingCont
   ...
   }
  reviews{
    rating 
    text
    createdAt
    ...
  }
 recommended {
   isbn
   title
   price
   ...
  }
}
}
Nodes 2022
Nodes
NODES 2022, Neo4j Online Education Summit

On November 16 and 17 for 24 hours across all timezones, you’ll learn about best practices for beginners and experts alike.