""" Products of Shelf. `id` is a GraphQL ID, so it is sent as a string ("3"); in REST the same id is a number (3). """ type Product { id: ID! sku: String name: String! brand: String! "A category slug, as in Query.categories." category: String! "Price in currency units: the same number as `price` in REST." price: String! currency: String! stock: Int! inStock: Boolean! freeShipping: Boolean! "Average rating 1.0–5.0, or null when there are no reviews." rating: Float reviews: Int! description: String! specs: [Spec!]! "True for a product you created. Other students never see yours." own: Boolean! } type Spec { name: String! value: String! } type ProductPage { items: [Product!]! page: Int! pageSize: Int! total: Int! totalPages: Int! } type Category { slug: String! name: String! count: Int! } enum ProductSort { FEATURED PRICE_ASC PRICE_DESC NAME_ASC RATING } type User { id: ID! username: String! createdAt: String! } type AuthPayload { "Send it as Authorization: Bearer . Valid for 8 hours." token: String! user: User! } type Coupon { code: String! label: String! } type CartItem { "The cart item id – not the product id." id: ID! quantity: Int! lineTotal: Float! product: Product! } type Cart { items: [CartItem!]! count: Int! subtotal: Float! coupon: Coupon "Taken off by the coupon: at most the subtotal." discount: Float! shipping: Float! "subtotal − discount + shipping" total: Float! currency: String! freeShippingFrom: Float! } enum OrderStatus { AWAITING_PAYMENT PAID CANCELLED } type Address { name: String! address: String! city: String! postcode: String! country: String! } type OrderItem { id: ID! productId: ID name: String! unitPrice: Float! quantity: Int! lineTotal: Float! } type Payment { id: ID! "succeeded or declined" result: String! declineCode: String cardLast4: String! amount: Float! currency: String! "The trace id of the request that made this attempt – find it in the logs." traceId: String createdAt: String! } type Order { id: ID! "Order number as printed for the customer, SH-100042." number: String! status: OrderStatus! subtotal: Float! discount: Float! couponCode: String shipping: Float! total: Float! currency: String! createdAt: String! paidAt: String "Date (YYYY-MM-DD) once the order is paid." deliveryBy: String cardLast4: String itemCount: Int! items: [OrderItem!]! payments: [Payment!]! shippingAddress: Address! } type OrderPage { items: [Order!]! page: Int! pageSize: Int! total: Int! totalPages: Int! } type TestCard { number: String! brand: String! outcome: String! label: String! } type Task { id: ID! "The lesson this task belongs to, e.g. C6.4." lesson: String! title: String! goal: String! done: Boolean! "Your personal code once the task is done: type it into the lesson." code: String "False for an account that was not opened from a lesson: the platform cannot check its codes." verifiable: Boolean! } type ResetResult { message: String! cartItems: Int! orders: Int! products: Int! } type Query { "The shared catalog plus your own products. pageSize 1–48." products(category: String, search: String, sort: ProductSort = FEATURED, inStock: Boolean, freeShipping: Boolean, mine: Boolean, page: Int = 1, pageSize: Int = 12): ProductPage! "One product. An error NOT_FOUND when there is none (or it is another student's)." product(id: ID!): Product categories: [Category!]! testCards: [TestCard!]! "You, or null when the request carries no valid token or key." me: User "Your cart. Needs a token or key." cart: Cart! "Your orders, newest first. pageSize 1–50." orders(status: OrderStatus, page: Int = 1, pageSize: Int = 10): OrderPage! "One of your orders. An error NOT_FOUND for an order that is not yours." order(id: ID!): Order "Practice tasks of the API course and your codes." tasks: [Task!]! } input ProductInput { name: String! brand: String! category: String! price: Float! stock: Int description: String freeShipping: Boolean } input ProductPatch { name: String brand: String category: String price: Float stock: Int description: String freeShipping: Boolean } "Use made-up details: this is a practice shop." input AddressInput { name: String! address: String! city: String! postcode: String! country: String! } "Only test cards work: see Query.testCards." input CardInput { number: String! expiryMonth: Int! expiryYear: Int! cvc: String! cardholder: String! } type Mutation { "Create an account: username 3–15 letters, digits or _, password 8–72 characters." register(username: String!, password: String!): AuthPayload! login(username: String!, password: String!): AuthPayload! "A product only you can see." createProduct(input: ProductInput!): Product! "Change some fields of your product." updateProduct(id: ID!, input: ProductPatch!): Product! deleteProduct(id: ID!): Boolean! addToCart(productId: ID!, quantity: Int = 1): Cart! updateCartItem(itemId: ID!, quantity: Int!): Cart! removeFromCart(itemId: ID!): Cart! "One coupon per cart. Try SAVE25." applyCoupon(code: String!): Cart! removeCoupon: Cart! "Place an order from your cart; it waits for payment." placeOrder(shipping: AddressInput!): Order! "Pay with a test card. A declined card is an error PAYMENT_DECLINED; the attempt is kept in Order.payments." payOrder(id: ID!, card: CardInput!): Order! "Cancel an order that waits for payment. An error NOT_FOUND when there is no such order of yours." cancelOrder(id: ID!): Order "Delete your cart, orders, payments, products and task progress. The account and keys stay." resetMyData: ResetResult! }