diff options
| author | Wes Bos <wesbos@gmail.com> | 2018-06-14 16:44:21 -0400 |
|---|---|---|
| committer | Wes Bos <wesbos@gmail.com> | 2018-06-14 16:44:21 -0400 |
| commit | b1600adec47a04f60e1da07d94a3ed3906ff5aee (patch) | |
| tree | 828f786da6806d7237ee5cbc5df61e552353b85b /finished-application/backend/src | |
| parent | a95e08cd2dd5d7ec0a0412adae02515a5f9cec4f (diff) | |
starter files
Diffstat (limited to 'finished-application/backend/src')
| -rw-r--r-- | finished-application/backend/src/createServer.js | 20 | ||||
| -rw-r--r-- | finished-application/backend/src/db.js | 10 | ||||
| -rw-r--r-- | finished-application/backend/src/generated/prisma.graphql | 2126 | ||||
| -rw-r--r-- | finished-application/backend/src/index.js | 41 | ||||
| -rw-r--r-- | finished-application/backend/src/mail.js | 28 | ||||
| -rw-r--r-- | finished-application/backend/src/resolvers/Mutation.js | 354 | ||||
| -rw-r--r-- | finished-application/backend/src/resolvers/Query.js | 62 | ||||
| -rw-r--r-- | finished-application/backend/src/schema.graphql | 39 | ||||
| -rw-r--r-- | finished-application/backend/src/stripe.js | 1 | ||||
| -rw-r--r-- | finished-application/backend/src/utils.js | 17 |
10 files changed, 2698 insertions, 0 deletions
diff --git a/finished-application/backend/src/createServer.js b/finished-application/backend/src/createServer.js new file mode 100644 index 0000000..036d15a --- /dev/null +++ b/finished-application/backend/src/createServer.js @@ -0,0 +1,20 @@ +const { GraphQLServer } = require('graphql-yoga'); +const Mutation = require('./resolvers/Mutation'); +const Query = require('./resolvers/Query'); +const db = require('./db'); + +function createServer() { + return new GraphQLServer({ + typeDefs: 'src/schema.graphql', + resolverValidationOptions: { + requireResolversForResolveType: false, + }, + resolvers: { + Mutation, + Query, + }, + context: req => ({ ...req, db }), + }); +} + +module.exports = createServer; diff --git a/finished-application/backend/src/db.js b/finished-application/backend/src/db.js new file mode 100644 index 0000000..e8caa14 --- /dev/null +++ b/finished-application/backend/src/db.js @@ -0,0 +1,10 @@ +const { Prisma } = require('prisma-binding'); + +const db = new Prisma({ + typeDefs: 'src/generated/prisma.graphql', + endpoint: process.env.PRISMA_ENDPOINT, // the endpoint of the Prisma DB service (value is set in variables.env) + secret: process.env.PRISMA_SECRET, // taken from prisma/prisma.yml (value is set in .env) + debug: false, // log all GraphQL queries & mutations +}); + +module.exports = db;
\ No newline at end of file diff --git a/finished-application/backend/src/generated/prisma.graphql b/finished-application/backend/src/generated/prisma.graphql new file mode 100644 index 0000000..953ddc2 --- /dev/null +++ b/finished-application/backend/src/generated/prisma.graphql @@ -0,0 +1,2126 @@ +# source: https://us1.prisma.sh/wesbos/sick-fits/dev +# timestamp: Fri May 25 2018 10:46:45 GMT-0400 (EDT) + +type AggregateCartItem { + count: Int! +} + +type AggregateItem { + count: Int! +} + +type AggregateOrder { + count: Int! +} + +type AggregateOrderItem { + count: Int! +} + +type AggregateUser { + count: Int! +} + +type BatchPayload { + """The number of nodes that have been affected by the Batch operation.""" + count: Long! +} + +type CartItem implements Node { + id: ID! + quantity: Int! + item(where: ItemWhereInput): Item + user(where: UserWhereInput): User! + createdAt: DateTime! + updatedAt: DateTime! +} + +"""A connection to a list of items.""" +type CartItemConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [CartItemEdge]! + aggregate: AggregateCartItem! +} + +input CartItemCreateInput { + quantity: Int + item: ItemCreateOneInput + user: UserCreateOneWithoutCartInput! +} + +input CartItemCreateManyWithoutUserInput { + create: [CartItemCreateWithoutUserInput!] + connect: [CartItemWhereUniqueInput!] +} + +input CartItemCreateWithoutUserInput { + quantity: Int + item: ItemCreateOneInput +} + +"""An edge in a connection.""" +type CartItemEdge { + """The item at the end of the edge.""" + node: CartItem! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum CartItemOrderByInput { + id_ASC + id_DESC + quantity_ASC + quantity_DESC + createdAt_ASC + createdAt_DESC + updatedAt_ASC + updatedAt_DESC +} + +type CartItemPreviousValues { + id: ID! + quantity: Int! + createdAt: DateTime! + updatedAt: DateTime! +} + +type CartItemSubscriptionPayload { + mutation: MutationType! + node: CartItem + updatedFields: [String!] + previousValues: CartItemPreviousValues +} + +input CartItemSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [CartItemSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [CartItemSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [CartItemSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: CartItemWhereInput +} + +input CartItemUpdateInput { + quantity: Int + item: ItemUpdateOneInput + user: UserUpdateOneWithoutCartInput +} + +input CartItemUpdateManyWithoutUserInput { + create: [CartItemCreateWithoutUserInput!] + connect: [CartItemWhereUniqueInput!] + disconnect: [CartItemWhereUniqueInput!] + delete: [CartItemWhereUniqueInput!] + update: [CartItemUpdateWithWhereUniqueWithoutUserInput!] + upsert: [CartItemUpsertWithWhereUniqueWithoutUserInput!] +} + +input CartItemUpdateWithoutUserDataInput { + quantity: Int + item: ItemUpdateOneInput +} + +input CartItemUpdateWithWhereUniqueWithoutUserInput { + where: CartItemWhereUniqueInput! + data: CartItemUpdateWithoutUserDataInput! +} + +input CartItemUpsertWithWhereUniqueWithoutUserInput { + where: CartItemWhereUniqueInput! + update: CartItemUpdateWithoutUserDataInput! + create: CartItemCreateWithoutUserInput! +} + +input CartItemWhereInput { + """Logical AND on all given filters.""" + AND: [CartItemWhereInput!] + + """Logical OR on all given filters.""" + OR: [CartItemWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [CartItemWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID + quantity: Int + + """All values that are not equal to given value.""" + quantity_not: Int + + """All values that are contained in given list.""" + quantity_in: [Int!] + + """All values that are not contained in given list.""" + quantity_not_in: [Int!] + + """All values less than the given value.""" + quantity_lt: Int + + """All values less than or equal the given value.""" + quantity_lte: Int + + """All values greater than the given value.""" + quantity_gt: Int + + """All values greater than or equal the given value.""" + quantity_gte: Int + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime + updatedAt: DateTime + + """All values that are not equal to given value.""" + updatedAt_not: DateTime + + """All values that are contained in given list.""" + updatedAt_in: [DateTime!] + + """All values that are not contained in given list.""" + updatedAt_not_in: [DateTime!] + + """All values less than the given value.""" + updatedAt_lt: DateTime + + """All values less than or equal the given value.""" + updatedAt_lte: DateTime + + """All values greater than the given value.""" + updatedAt_gt: DateTime + + """All values greater than or equal the given value.""" + updatedAt_gte: DateTime + item: ItemWhereInput + user: UserWhereInput +} + +input CartItemWhereUniqueInput { + id: ID +} + +scalar DateTime + +type Item implements Node { + id: ID! + title: String! + description: String! + image: String + largeImage: String + price: Int! + createdAt: DateTime! + updatedAt: DateTime! + user(where: UserWhereInput): User! +} + +"""A connection to a list of items.""" +type ItemConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [ItemEdge]! + aggregate: AggregateItem! +} + +input ItemCreateInput { + title: String! + description: String! + image: String + largeImage: String + price: Int! + user: UserCreateOneInput! +} + +input ItemCreateOneInput { + create: ItemCreateInput + connect: ItemWhereUniqueInput +} + +"""An edge in a connection.""" +type ItemEdge { + """The item at the end of the edge.""" + node: Item! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum ItemOrderByInput { + id_ASC + id_DESC + title_ASC + title_DESC + description_ASC + description_DESC + image_ASC + image_DESC + largeImage_ASC + largeImage_DESC + price_ASC + price_DESC + createdAt_ASC + createdAt_DESC + updatedAt_ASC + updatedAt_DESC +} + +type ItemPreviousValues { + id: ID! + title: String! + description: String! + image: String + largeImage: String + price: Int! + createdAt: DateTime! + updatedAt: DateTime! +} + +type ItemSubscriptionPayload { + mutation: MutationType! + node: Item + updatedFields: [String!] + previousValues: ItemPreviousValues +} + +input ItemSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [ItemSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [ItemSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [ItemSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: ItemWhereInput +} + +input ItemUpdateDataInput { + title: String + description: String + image: String + largeImage: String + price: Int + user: UserUpdateOneInput +} + +input ItemUpdateInput { + title: String + description: String + image: String + largeImage: String + price: Int + user: UserUpdateOneInput +} + +input ItemUpdateOneInput { + create: ItemCreateInput + connect: ItemWhereUniqueInput + disconnect: Boolean + delete: Boolean + update: ItemUpdateDataInput + upsert: ItemUpsertNestedInput +} + +input ItemUpsertNestedInput { + update: ItemUpdateDataInput! + create: ItemCreateInput! +} + +input ItemWhereInput { + """Logical AND on all given filters.""" + AND: [ItemWhereInput!] + + """Logical OR on all given filters.""" + OR: [ItemWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [ItemWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID + title: String + + """All values that are not equal to given value.""" + title_not: String + + """All values that are contained in given list.""" + title_in: [String!] + + """All values that are not contained in given list.""" + title_not_in: [String!] + + """All values less than the given value.""" + title_lt: String + + """All values less than or equal the given value.""" + title_lte: String + + """All values greater than the given value.""" + title_gt: String + + """All values greater than or equal the given value.""" + title_gte: String + + """All values containing the given string.""" + title_contains: String + + """All values not containing the given string.""" + title_not_contains: String + + """All values starting with the given string.""" + title_starts_with: String + + """All values not starting with the given string.""" + title_not_starts_with: String + + """All values ending with the given string.""" + title_ends_with: String + + """All values not ending with the given string.""" + title_not_ends_with: String + description: String + + """All values that are not equal to given value.""" + description_not: String + + """All values that are contained in given list.""" + description_in: [String!] + + """All values that are not contained in given list.""" + description_not_in: [String!] + + """All values less than the given value.""" + description_lt: String + + """All values less than or equal the given value.""" + description_lte: String + + """All values greater than the given value.""" + description_gt: String + + """All values greater than or equal the given value.""" + description_gte: String + + """All values containing the given string.""" + description_contains: String + + """All values not containing the given string.""" + description_not_contains: String + + """All values starting with the given string.""" + description_starts_with: String + + """All values not starting with the given string.""" + description_not_starts_with: String + + """All values ending with the given string.""" + description_ends_with: String + + """All values not ending with the given string.""" + description_not_ends_with: String + image: String + + """All values that are not equal to given value.""" + image_not: String + + """All values that are contained in given list.""" + image_in: [String!] + + """All values that are not contained in given list.""" + image_not_in: [String!] + + """All values less than the given value.""" + image_lt: String + + """All values less than or equal the given value.""" + image_lte: String + + """All values greater than the given value.""" + image_gt: String + + """All values greater than or equal the given value.""" + image_gte: String + + """All values containing the given string.""" + image_contains: String + + """All values not containing the given string.""" + image_not_contains: String + + """All values starting with the given string.""" + image_starts_with: String + + """All values not starting with the given string.""" + image_not_starts_with: String + + """All values ending with the given string.""" + image_ends_with: String + + """All values not ending with the given string.""" + image_not_ends_with: String + largeImage: String + + """All values that are not equal to given value.""" + largeImage_not: String + + """All values that are contained in given list.""" + largeImage_in: [String!] + + """All values that are not contained in given list.""" + largeImage_not_in: [String!] + + """All values less than the given value.""" + largeImage_lt: String + + """All values less than or equal the given value.""" + largeImage_lte: String + + """All values greater than the given value.""" + largeImage_gt: String + + """All values greater than or equal the given value.""" + largeImage_gte: String + + """All values containing the given string.""" + largeImage_contains: String + + """All values not containing the given string.""" + largeImage_not_contains: String + + """All values starting with the given string.""" + largeImage_starts_with: String + + """All values not starting with the given string.""" + largeImage_not_starts_with: String + + """All values ending with the given string.""" + largeImage_ends_with: String + + """All values not ending with the given string.""" + largeImage_not_ends_with: String + price: Int + + """All values that are not equal to given value.""" + price_not: Int + + """All values that are contained in given list.""" + price_in: [Int!] + + """All values that are not contained in given list.""" + price_not_in: [Int!] + + """All values less than the given value.""" + price_lt: Int + + """All values less than or equal the given value.""" + price_lte: Int + + """All values greater than the given value.""" + price_gt: Int + + """All values greater than or equal the given value.""" + price_gte: Int + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime + updatedAt: DateTime + + """All values that are not equal to given value.""" + updatedAt_not: DateTime + + """All values that are contained in given list.""" + updatedAt_in: [DateTime!] + + """All values that are not contained in given list.""" + updatedAt_not_in: [DateTime!] + + """All values less than the given value.""" + updatedAt_lt: DateTime + + """All values less than or equal the given value.""" + updatedAt_lte: DateTime + + """All values greater than the given value.""" + updatedAt_gt: DateTime + + """All values greater than or equal the given value.""" + updatedAt_gte: DateTime + user: UserWhereInput +} + +input ItemWhereUniqueInput { + id: ID +} + +""" +The `Long` scalar type represents non-fractional signed whole numeric values. +Long can represent values between -(2^63) and 2^63 - 1. +""" +scalar Long + +type Mutation { + createUser(data: UserCreateInput!): User! + createCartItem(data: CartItemCreateInput!): CartItem! + createItem(data: ItemCreateInput!): Item! + createOrderItem(data: OrderItemCreateInput!): OrderItem! + createOrder(data: OrderCreateInput!): Order! + updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User + updateCartItem(data: CartItemUpdateInput!, where: CartItemWhereUniqueInput!): CartItem + updateItem(data: ItemUpdateInput!, where: ItemWhereUniqueInput!): Item + updateOrderItem(data: OrderItemUpdateInput!, where: OrderItemWhereUniqueInput!): OrderItem + updateOrder(data: OrderUpdateInput!, where: OrderWhereUniqueInput!): Order + deleteUser(where: UserWhereUniqueInput!): User + deleteCartItem(where: CartItemWhereUniqueInput!): CartItem + deleteItem(where: ItemWhereUniqueInput!): Item + deleteOrderItem(where: OrderItemWhereUniqueInput!): OrderItem + deleteOrder(where: OrderWhereUniqueInput!): Order + upsertUser(where: UserWhereUniqueInput!, create: UserCreateInput!, update: UserUpdateInput!): User! + upsertCartItem(where: CartItemWhereUniqueInput!, create: CartItemCreateInput!, update: CartItemUpdateInput!): CartItem! + upsertItem(where: ItemWhereUniqueInput!, create: ItemCreateInput!, update: ItemUpdateInput!): Item! + upsertOrderItem(where: OrderItemWhereUniqueInput!, create: OrderItemCreateInput!, update: OrderItemUpdateInput!): OrderItem! + upsertOrder(where: OrderWhereUniqueInput!, create: OrderCreateInput!, update: OrderUpdateInput!): Order! + updateManyUsers(data: UserUpdateInput!, where: UserWhereInput): BatchPayload! + updateManyCartItems(data: CartItemUpdateInput!, where: CartItemWhereInput): BatchPayload! + updateManyItems(data: ItemUpdateInput!, where: ItemWhereInput): BatchPayload! + updateManyOrderItems(data: OrderItemUpdateInput!, where: OrderItemWhereInput): BatchPayload! + updateManyOrders(data: OrderUpdateInput!, where: OrderWhereInput): BatchPayload! + deleteManyUsers(where: UserWhereInput): BatchPayload! + deleteManyCartItems(where: CartItemWhereInput): BatchPayload! + deleteManyItems(where: ItemWhereInput): BatchPayload! + deleteManyOrderItems(where: OrderItemWhereInput): BatchPayload! + deleteManyOrders(where: OrderWhereInput): BatchPayload! +} + +enum MutationType { + CREATED + UPDATED + DELETED +} + +"""An object with an ID""" +interface Node { + """The id of the object.""" + id: ID! +} + +type Order implements Node { + id: ID! + items(where: OrderItemWhereInput, orderBy: OrderItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [OrderItem!] + total: Int! + user(where: UserWhereInput): User! + createdAt: DateTime! + updatedAt: DateTime! + charge: String! +} + +"""A connection to a list of items.""" +type OrderConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [OrderEdge]! + aggregate: AggregateOrder! +} + +input OrderCreateInput { + total: Int! + charge: String! + items: OrderItemCreateManyInput + user: UserCreateOneWithoutOrdersInput! +} + +input OrderCreateManyWithoutUserInput { + create: [OrderCreateWithoutUserInput!] + connect: [OrderWhereUniqueInput!] +} + +input OrderCreateWithoutUserInput { + total: Int! + charge: String! + items: OrderItemCreateManyInput +} + +"""An edge in a connection.""" +type OrderEdge { + """The item at the end of the edge.""" + node: Order! + + """A cursor for use in pagination.""" + cursor: String! +} + +type OrderItem implements Node { + id: ID! + title: String! + description: String! + image: String + largeImage: String + price: Int! + createdAt: DateTime! + updatedAt: DateTime! + quantity: Int! +} + +"""A connection to a list of items.""" +type OrderItemConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [OrderItemEdge]! + aggregate: AggregateOrderItem! +} + +input OrderItemCreateInput { + title: String! + description: String! + image: String + largeImage: String + price: Int! + quantity: Int +} + +input OrderItemCreateManyInput { + create: [OrderItemCreateInput!] + connect: [OrderItemWhereUniqueInput!] +} + +"""An edge in a connection.""" +type OrderItemEdge { + """The item at the end of the edge.""" + node: OrderItem! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum OrderItemOrderByInput { + id_ASC + id_DESC + title_ASC + title_DESC + description_ASC + description_DESC + image_ASC + image_DESC + largeImage_ASC + largeImage_DESC + price_ASC + price_DESC + createdAt_ASC + createdAt_DESC + updatedAt_ASC + updatedAt_DESC + quantity_ASC + quantity_DESC +} + +type OrderItemPreviousValues { + id: ID! + title: String! + description: String! + image: String + largeImage: String + price: Int! + createdAt: DateTime! + updatedAt: DateTime! + quantity: Int! +} + +type OrderItemSubscriptionPayload { + mutation: MutationType! + node: OrderItem + updatedFields: [String!] + previousValues: OrderItemPreviousValues +} + +input OrderItemSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [OrderItemSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [OrderItemSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [OrderItemSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: OrderItemWhereInput +} + +input OrderItemUpdateDataInput { + title: String + description: String + image: String + largeImage: String + price: Int + quantity: Int +} + +input OrderItemUpdateInput { + title: String + description: String + image: String + largeImage: String + price: Int + quantity: Int +} + +input OrderItemUpdateManyInput { + create: [OrderItemCreateInput!] + connect: [OrderItemWhereUniqueInput!] + disconnect: [OrderItemWhereUniqueInput!] + delete: [OrderItemWhereUniqueInput!] + update: [OrderItemUpdateWithWhereUniqueNestedInput!] + upsert: [OrderItemUpsertWithWhereUniqueNestedInput!] +} + +input OrderItemUpdateWithWhereUniqueNestedInput { + where: OrderItemWhereUniqueInput! + data: OrderItemUpdateDataInput! +} + +input OrderItemUpsertWithWhereUniqueNestedInput { + where: OrderItemWhereUniqueInput! + update: OrderItemUpdateDataInput! + create: OrderItemCreateInput! +} + +input OrderItemWhereInput { + """Logical AND on all given filters.""" + AND: [OrderItemWhereInput!] + + """Logical OR on all given filters.""" + OR: [OrderItemWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [OrderItemWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID + title: String + + """All values that are not equal to given value.""" + title_not: String + + """All values that are contained in given list.""" + title_in: [String!] + + """All values that are not contained in given list.""" + title_not_in: [String!] + + """All values less than the given value.""" + title_lt: String + + """All values less than or equal the given value.""" + title_lte: String + + """All values greater than the given value.""" + title_gt: String + + """All values greater than or equal the given value.""" + title_gte: String + + """All values containing the given string.""" + title_contains: String + + """All values not containing the given string.""" + title_not_contains: String + + """All values starting with the given string.""" + title_starts_with: String + + """All values not starting with the given string.""" + title_not_starts_with: String + + """All values ending with the given string.""" + title_ends_with: String + + """All values not ending with the given string.""" + title_not_ends_with: String + description: String + + """All values that are not equal to given value.""" + description_not: String + + """All values that are contained in given list.""" + description_in: [String!] + + """All values that are not contained in given list.""" + description_not_in: [String!] + + """All values less than the given value.""" + description_lt: String + + """All values less than or equal the given value.""" + description_lte: String + + """All values greater than the given value.""" + description_gt: String + + """All values greater than or equal the given value.""" + description_gte: String + + """All values containing the given string.""" + description_contains: String + + """All values not containing the given string.""" + description_not_contains: String + + """All values starting with the given string.""" + description_starts_with: String + + """All values not starting with the given string.""" + description_not_starts_with: String + + """All values ending with the given string.""" + description_ends_with: String + + """All values not ending with the given string.""" + description_not_ends_with: String + image: String + + """All values that are not equal to given value.""" + image_not: String + + """All values that are contained in given list.""" + image_in: [String!] + + """All values that are not contained in given list.""" + image_not_in: [String!] + + """All values less than the given value.""" + image_lt: String + + """All values less than or equal the given value.""" + image_lte: String + + """All values greater than the given value.""" + image_gt: String + + """All values greater than or equal the given value.""" + image_gte: String + + """All values containing the given string.""" + image_contains: String + + """All values not containing the given string.""" + image_not_contains: String + + """All values starting with the given string.""" + image_starts_with: String + + """All values not starting with the given string.""" + image_not_starts_with: String + + """All values ending with the given string.""" + image_ends_with: String + + """All values not ending with the given string.""" + image_not_ends_with: String + largeImage: String + + """All values that are not equal to given value.""" + largeImage_not: String + + """All values that are contained in given list.""" + largeImage_in: [String!] + + """All values that are not contained in given list.""" + largeImage_not_in: [String!] + + """All values less than the given value.""" + largeImage_lt: String + + """All values less than or equal the given value.""" + largeImage_lte: String + + """All values greater than the given value.""" + largeImage_gt: String + + """All values greater than or equal the given value.""" + largeImage_gte: String + + """All values containing the given string.""" + largeImage_contains: String + + """All values not containing the given string.""" + largeImage_not_contains: String + + """All values starting with the given string.""" + largeImage_starts_with: String + + """All values not starting with the given string.""" + largeImage_not_starts_with: String + + """All values ending with the given string.""" + largeImage_ends_with: String + + """All values not ending with the given string.""" + largeImage_not_ends_with: String + price: Int + + """All values that are not equal to given value.""" + price_not: Int + + """All values that are contained in given list.""" + price_in: [Int!] + + """All values that are not contained in given list.""" + price_not_in: [Int!] + + """All values less than the given value.""" + price_lt: Int + + """All values less than or equal the given value.""" + price_lte: Int + + """All values greater than the given value.""" + price_gt: Int + + """All values greater than or equal the given value.""" + price_gte: Int + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime + updatedAt: DateTime + + """All values that are not equal to given value.""" + updatedAt_not: DateTime + + """All values that are contained in given list.""" + updatedAt_in: [DateTime!] + + """All values that are not contained in given list.""" + updatedAt_not_in: [DateTime!] + + """All values less than the given value.""" + updatedAt_lt: DateTime + + """All values less than or equal the given value.""" + updatedAt_lte: DateTime + + """All values greater than the given value.""" + updatedAt_gt: DateTime + + """All values greater than or equal the given value.""" + updatedAt_gte: DateTime + quantity: Int + + """All values that are not equal to given value.""" + quantity_not: Int + + """All values that are contained in given list.""" + quantity_in: [Int!] + + """All values that are not contained in given list.""" + quantity_not_in: [Int!] + + """All values less than the given value.""" + quantity_lt: Int + + """All values less than or equal the given value.""" + quantity_lte: Int + + """All values greater than the given value.""" + quantity_gt: Int + + """All values greater than or equal the given value.""" + quantity_gte: Int +} + +input OrderItemWhereUniqueInput { + id: ID +} + +enum OrderOrderByInput { + id_ASC + id_DESC + total_ASC + total_DESC + createdAt_ASC + createdAt_DESC + updatedAt_ASC + updatedAt_DESC + charge_ASC + charge_DESC +} + +type OrderPreviousValues { + id: ID! + total: Int! + createdAt: DateTime! + updatedAt: DateTime! + charge: String! +} + +type OrderSubscriptionPayload { + mutation: MutationType! + node: Order + updatedFields: [String!] + previousValues: OrderPreviousValues +} + +input OrderSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [OrderSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [OrderSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [OrderSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: OrderWhereInput +} + +input OrderUpdateInput { + total: Int + charge: String + items: OrderItemUpdateManyInput + user: UserUpdateOneWithoutOrdersInput +} + +input OrderUpdateManyWithoutUserInput { + create: [OrderCreateWithoutUserInput!] + connect: [OrderWhereUniqueInput!] + disconnect: [OrderWhereUniqueInput!] + delete: [OrderWhereUniqueInput!] + update: [OrderUpdateWithWhereUniqueWithoutUserInput!] + upsert: [OrderUpsertWithWhereUniqueWithoutUserInput!] +} + +input OrderUpdateWithoutUserDataInput { + total: Int + charge: String + items: OrderItemUpdateManyInput +} + +input OrderUpdateWithWhereUniqueWithoutUserInput { + where: OrderWhereUniqueInput! + data: OrderUpdateWithoutUserDataInput! +} + +input OrderUpsertWithWhereUniqueWithoutUserInput { + where: OrderWhereUniqueInput! + update: OrderUpdateWithoutUserDataInput! + create: OrderCreateWithoutUserInput! +} + +input OrderWhereInput { + """Logical AND on all given filters.""" + AND: [OrderWhereInput!] + + """Logical OR on all given filters.""" + OR: [OrderWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [OrderWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID + total: Int + + """All values that are not equal to given value.""" + total_not: Int + + """All values that are contained in given list.""" + total_in: [Int!] + + """All values that are not contained in given list.""" + total_not_in: [Int!] + + """All values less than the given value.""" + total_lt: Int + + """All values less than or equal the given value.""" + total_lte: Int + + """All values greater than the given value.""" + total_gt: Int + + """All values greater than or equal the given value.""" + total_gte: Int + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime + updatedAt: DateTime + + """All values that are not equal to given value.""" + updatedAt_not: DateTime + + """All values that are contained in given list.""" + updatedAt_in: [DateTime!] + + """All values that are not contained in given list.""" + updatedAt_not_in: [DateTime!] + + """All values less than the given value.""" + updatedAt_lt: DateTime + + """All values less than or equal the given value.""" + updatedAt_lte: DateTime + + """All values greater than the given value.""" + updatedAt_gt: DateTime + + """All values greater than or equal the given value.""" + updatedAt_gte: DateTime + charge: String + + """All values that are not equal to given value.""" + charge_not: String + + """All values that are contained in given list.""" + charge_in: [String!] + + """All values that are not contained in given list.""" + charge_not_in: [String!] + + """All values less than the given value.""" + charge_lt: String + + """All values less than or equal the given value.""" + charge_lte: String + + """All values greater than the given value.""" + charge_gt: String + + """All values greater than or equal the given value.""" + charge_gte: String + + """All values containing the given string.""" + charge_contains: String + + """All values not containing the given string.""" + charge_not_contains: String + + """All values starting with the given string.""" + charge_starts_with: String + + """All values not starting with the given string.""" + charge_not_starts_with: String + + """All values ending with the given string.""" + charge_ends_with: String + + """All values not ending with the given string.""" + charge_not_ends_with: String + items_every: OrderItemWhereInput + items_some: OrderItemWhereInput + items_none: OrderItemWhereInput + user: UserWhereInput +} + +input OrderWhereUniqueInput { + id: ID +} + +"""Information about pagination in a connection.""" +type PageInfo { + """When paginating forwards, are there more items?""" + hasNextPage: Boolean! + + """When paginating backwards, are there more items?""" + hasPreviousPage: Boolean! + + """When paginating backwards, the cursor to continue.""" + startCursor: String + + """When paginating forwards, the cursor to continue.""" + endCursor: String +} + +enum Permission { + ADMIN + USER + ITEMCREATE + ITEMUPDATE + ITEMDELETE + PERMISSIONUPDATE +} + +type Query { + users(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [User]! + cartItems(where: CartItemWhereInput, orderBy: CartItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [CartItem]! + items(where: ItemWhereInput, orderBy: ItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Item]! + orderItems(where: OrderItemWhereInput, orderBy: OrderItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [OrderItem]! + orders(where: OrderWhereInput, orderBy: OrderOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Order]! + user(where: UserWhereUniqueInput!): User + cartItem(where: CartItemWhereUniqueInput!): CartItem + item(where: ItemWhereUniqueInput!): Item + orderItem(where: OrderItemWhereUniqueInput!): OrderItem + order(where: OrderWhereUniqueInput!): Order + usersConnection(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): UserConnection! + cartItemsConnection(where: CartItemWhereInput, orderBy: CartItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): CartItemConnection! + itemsConnection(where: ItemWhereInput, orderBy: ItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): ItemConnection! + orderItemsConnection(where: OrderItemWhereInput, orderBy: OrderItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): OrderItemConnection! + ordersConnection(where: OrderWhereInput, orderBy: OrderOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): OrderConnection! + + """Fetches an object given its ID""" + node( + """The ID of an object""" + id: ID! + ): Node +} + +type Subscription { + user(where: UserSubscriptionWhereInput): UserSubscriptionPayload + cartItem(where: CartItemSubscriptionWhereInput): CartItemSubscriptionPayload + item(where: ItemSubscriptionWhereInput): ItemSubscriptionPayload + orderItem(where: OrderItemSubscriptionWhereInput): OrderItemSubscriptionPayload + order(where: OrderSubscriptionWhereInput): OrderSubscriptionPayload +} + +type User implements Node { + id: ID! + email: String! + password: String! + name: String! + orders(where: OrderWhereInput, orderBy: OrderOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Order!] + resetToken: String + resetTokenExpiry: String + cart(where: CartItemWhereInput, orderBy: CartItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [CartItem!] + createdAt: DateTime! + updatedAt: DateTime! + permissions: [Permission!] +} + +"""A connection to a list of items.""" +type UserConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [UserEdge]! + aggregate: AggregateUser! +} + +input UserCreateInput { + email: String! + password: String! + name: String! + resetToken: String + resetTokenExpiry: String + permissions: UserCreatepermissionsInput + orders: OrderCreateManyWithoutUserInput + cart: CartItemCreateManyWithoutUserInput +} + +input UserCreateOneInput { + create: UserCreateInput + connect: UserWhereUniqueInput +} + +input UserCreateOneWithoutCartInput { + create: UserCreateWithoutCartInput + connect: UserWhereUniqueInput +} + +input UserCreateOneWithoutOrdersInput { + create: UserCreateWithoutOrdersInput + connect: UserWhereUniqueInput +} + +input UserCreatepermissionsInput { + set: [Permission!] +} + +input UserCreateWithoutCartInput { + email: String! + password: String! + name: String! + resetToken: String + resetTokenExpiry: String + permissions: UserCreatepermissionsInput + orders: OrderCreateManyWithoutUserInput +} + +input UserCreateWithoutOrdersInput { + email: String! + password: String! + name: String! + resetToken: String + resetTokenExpiry: String + permissions: UserCreatepermissionsInput + cart: CartItemCreateManyWithoutUserInput +} + +"""An edge in a connection.""" +type UserEdge { + """The item at the end of the edge.""" + node: User! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum UserOrderByInput { + id_ASC + id_DESC + email_ASC + email_DESC + password_ASC + password_DESC + name_ASC + name_DESC + resetToken_ASC + resetToken_DESC + resetTokenExpiry_ASC + resetTokenExpiry_DESC + createdAt_ASC + createdAt_DESC + updatedAt_ASC + updatedAt_DESC +} + +type UserPreviousValues { + id: ID! + email: String! + password: String! + name: String! + resetToken: String + resetTokenExpiry: String + createdAt: DateTime! + updatedAt: DateTime! + permissions: [Permission!] +} + +type UserSubscriptionPayload { + mutation: MutationType! + node: User + updatedFields: [String!] + previousValues: UserPreviousValues +} + +input UserSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [UserSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [UserSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [UserSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: UserWhereInput +} + +input UserUpdateDataInput { + email: String + password: String + name: String + resetToken: String + resetTokenExpiry: String + permissions: UserUpdatepermissionsInput + orders: OrderUpdateManyWithoutUserInput + cart: CartItemUpdateManyWithoutUserInput +} + +input UserUpdateInput { + email: String + password: String + name: String + resetToken: String + resetTokenExpiry: String + permissions: UserUpdatepermissionsInput + orders: OrderUpdateManyWithoutUserInput + cart: CartItemUpdateManyWithoutUserInput +} + +input UserUpdateOneInput { + create: UserCreateInput + connect: UserWhereUniqueInput + delete: Boolean + update: UserUpdateDataInput + upsert: UserUpsertNestedInput +} + +input UserUpdateOneWithoutCartInput { + create: UserCreateWithoutCartInput + connect: UserWhereUniqueInput + delete: Boolean + update: UserUpdateWithoutCartDataInput + upsert: UserUpsertWithoutCartInput +} + +input UserUpdateOneWithoutOrdersInput { + create: UserCreateWithoutOrdersInput + connect: UserWhereUniqueInput + delete: Boolean + update: UserUpdateWithoutOrdersDataInput + upsert: UserUpsertWithoutOrdersInput +} + +input UserUpdatepermissionsInput { + set: [Permission!] +} + +input UserUpdateWithoutCartDataInput { + email: String + password: String + name: String + resetToken: String + resetTokenExpiry: String + permissions: UserUpdatepermissionsInput + orders: OrderUpdateManyWithoutUserInput +} + +input UserUpdateWithoutOrdersDataInput { + email: String + password: String + name: String + resetToken: String + resetTokenExpiry: String + permissions: UserUpdatepermissionsInput + cart: CartItemUpdateManyWithoutUserInput +} + +input UserUpsertNestedInput { + update: UserUpdateDataInput! + create: UserCreateInput! +} + +input UserUpsertWithoutCartInput { + update: UserUpdateWithoutCartDataInput! + create: UserCreateWithoutCartInput! +} + +input UserUpsertWithoutOrdersInput { + update: UserUpdateWithoutOrdersDataInput! + create: UserCreateWithoutOrdersInput! +} + +input UserWhereInput { + """Logical AND on all given filters.""" + AND: [UserWhereInput!] + + """Logical OR on all given filters.""" + OR: [UserWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [UserWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID + email: String + + """All values that are not equal to given value.""" + email_not: String + + """All values that are contained in given list.""" + email_in: [String!] + + """All values that are not contained in given list.""" + email_not_in: [String!] + + """All values less than the given value.""" + email_lt: String + + """All values less than or equal the given value.""" + email_lte: String + + """All values greater than the given value.""" + email_gt: String + + """All values greater than or equal the given value.""" + email_gte: String + + """All values containing the given string.""" + email_contains: String + + """All values not containing the given string.""" + email_not_contains: String + + """All values starting with the given string.""" + email_starts_with: String + + """All values not starting with the given string.""" + email_not_starts_with: String + + """All values ending with the given string.""" + email_ends_with: String + + """All values not ending with the given string.""" + email_not_ends_with: String + password: String + + """All values that are not equal to given value.""" + password_not: String + + """All values that are contained in given list.""" + password_in: [String!] + + """All values that are not contained in given list.""" + password_not_in: [String!] + + """All values less than the given value.""" + password_lt: String + + """All values less than or equal the given value.""" + password_lte: String + + """All values greater than the given value.""" + password_gt: String + + """All values greater than or equal the given value.""" + password_gte: String + + """All values containing the given string.""" + password_contains: String + + """All values not containing the given string.""" + password_not_contains: String + + """All values starting with the given string.""" + password_starts_with: String + + """All values not starting with the given string.""" + password_not_starts_with: String + + """All values ending with the given string.""" + password_ends_with: String + + """All values not ending with the given string.""" + password_not_ends_with: String + name: String + + """All values that are not equal to given value.""" + name_not: String + + """All values that are contained in given list.""" + name_in: [String!] + + """All values that are not contained in given list.""" + name_not_in: [String!] + + """All values less than the given value.""" + name_lt: String + + """All values less than or equal the given value.""" + name_lte: String + + """All values greater than the given value.""" + name_gt: String + + """All values greater than or equal the given value.""" + name_gte: String + + """All values containing the given string.""" + name_contains: String + + """All values not containing the given string.""" + name_not_contains: String + + """All values starting with the given string.""" + name_starts_with: String + + """All values not starting with the given string.""" + name_not_starts_with: String + + """All values ending with the given string.""" + name_ends_with: String + + """All values not ending with the given string.""" + name_not_ends_with: String + resetToken: String + + """All values that are not equal to given value.""" + resetToken_not: String + + """All values that are contained in given list.""" + resetToken_in: [String!] + + """All values that are not contained in given list.""" + resetToken_not_in: [String!] + + """All values less than the given value.""" + resetToken_lt: String + + """All values less than or equal the given value.""" + resetToken_lte: String + + """All values greater than the given value.""" + resetToken_gt: String + + """All values greater than or equal the given value.""" + resetToken_gte: String + + """All values containing the given string.""" + resetToken_contains: String + + """All values not containing the given string.""" + resetToken_not_contains: String + + """All values starting with the given string.""" + resetToken_starts_with: String + + """All values not starting with the given string.""" + resetToken_not_starts_with: String + + """All values ending with the given string.""" + resetToken_ends_with: String + + """All values not ending with the given string.""" + resetToken_not_ends_with: String + resetTokenExpiry: String + + """All values that are not equal to given value.""" + resetTokenExpiry_not: String + + """All values that are contained in given list.""" + resetTokenExpiry_in: [String!] + + """All values that are not contained in given list.""" + resetTokenExpiry_not_in: [String!] + + """All values less than the given value.""" + resetTokenExpiry_lt: String + + """All values less than or equal the given value.""" + resetTokenExpiry_lte: String + + """All values greater than the given value.""" + resetTokenExpiry_gt: String + + """All values greater than or equal the given value.""" + resetTokenExpiry_gte: String + + """All values containing the given string.""" + resetTokenExpiry_contains: String + + """All values not containing the given string.""" + resetTokenExpiry_not_contains: String + + """All values starting with the given string.""" + resetTokenExpiry_starts_with: String + + """All values not starting with the given string.""" + resetTokenExpiry_not_starts_with: String + + """All values ending with the given string.""" + resetTokenExpiry_ends_with: String + + """All values not ending with the given string.""" + resetTokenExpiry_not_ends_with: String + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime + updatedAt: DateTime + + """All values that are not equal to given value.""" + updatedAt_not: DateTime + + """All values that are contained in given list.""" + updatedAt_in: [DateTime!] + + """All values that are not contained in given list.""" + updatedAt_not_in: [DateTime!] + + """All values less than the given value.""" + updatedAt_lt: DateTime + + """All values less than or equal the given value.""" + updatedAt_lte: DateTime + + """All values greater than the given value.""" + updatedAt_gt: DateTime + + """All values greater than or equal the given value.""" + updatedAt_gte: DateTime + orders_every: OrderWhereInput + orders_some: OrderWhereInput + orders_none: OrderWhereInput + cart_every: CartItemWhereInput + cart_some: CartItemWhereInput + cart_none: CartItemWhereInput +} + +input UserWhereUniqueInput { + id: ID + email: String +} diff --git a/finished-application/backend/src/index.js b/finished-application/backend/src/index.js new file mode 100644 index 0000000..94911ec --- /dev/null +++ b/finished-application/backend/src/index.js @@ -0,0 +1,41 @@ +/* eslint-disable */ +require('dotenv').config({ path: 'variables.env' }); +/* eslint-enable */ +const jwt = require('jsonwebtoken'); +const createServer = require('./createServer'); +const db = require('./db'); +const cookieParser = require('cookie-parser'); + +const server = createServer(); + +server.express.use(cookieParser()); + +server.express.use((req, res, next) => { + const { token } = req.cookies; + if (token) { + const { userId } = jwt.verify(token, process.env.APP_SECRET); + req.userId = userId; + } + next(); +}); + +// 2. Get User from their ID +server.express.use(async (req, res, next) => { + if (!req.userId) return next(); + const user = await db.query.user({ where: { id: req.userId } }, `{ id, permissions, email, name }`); + req.user = user; + next(); +}); + +server.start( + { + cors: { + credentials: true, + origin: process.env.FRONTEND_URL, + }, + port: 4444, + }, + deets => { + console.log(`Server is running on http://localhost:${deets.port}`); + } +); diff --git a/finished-application/backend/src/mail.js b/finished-application/backend/src/mail.js new file mode 100644 index 0000000..fc4a47b --- /dev/null +++ b/finished-application/backend/src/mail.js @@ -0,0 +1,28 @@ +const nodemailer = require('nodemailer'); + +const transport = nodemailer.createTransport({ + host: 'smtp.mailtrap.io', + port: 2525, + auth: { + user: 'c41bab08214808', + pass: '04992d4af4bdcf', + }, +}); + +const makeANiceEmail = text => ` + <div className="email" style=" + border:1px solid black; + padding: 20px; + font-family: sans-serif; + line-height: 2; + font-size: 20px; + "> + <h2>Hello There</h2> + <p>${text}</p> + + <p>😘 Wes Bos</p> + </div> +`; + +exports.transport = transport; +exports.makeANiceEmail = makeANiceEmail; diff --git a/finished-application/backend/src/resolvers/Mutation.js b/finished-application/backend/src/resolvers/Mutation.js new file mode 100644 index 0000000..0f1d9ba --- /dev/null +++ b/finished-application/backend/src/resolvers/Mutation.js @@ -0,0 +1,354 @@ +const bcrypt = require('bcryptjs'); +const jwt = require('jsonwebtoken'); +const { hasPermission } = require('../utils'); +const { randomBytes } = require('crypto'); +const { promisify } = require('util'); +const mail = require('../mail'); +const stripe = require('../stripe'); + +const mutations = { + // Signup Mutations + async signup(parent, args, ctx, info) { + args.email = args.email.toLowerCase(); + const password = await bcrypt.hash(args.password, 10); + const user = await ctx.db.mutation.createUser( + { + data: { + ...args, + password, + permissions: { set: ['USER'] }, + }, + }, + info + ); + const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET); + ctx.response.cookie('token', token, { + maxAge: 1000 * 60 * 60 * 24 * 365, + httpOnly: true, + }); + return user; + }, + + async signout(parent, args, ctx, info) { + ctx.response.clearCookie('token'); + return { message: 'goodbye!' }; + }, + + async signin(parent, { email, password }, ctx, info) { + const user = await ctx.db.query.user({ where: { email } }); + if (!user) { + throw new Error(`No such user found for email: ${email}`); + } + + const valid = await bcrypt.compare(password, user.password); + if (!valid) { + throw new Error('Invalid password'); + } + // set the cookie + const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET); + ctx.response.cookie('token', token, { + maxAge: 1000 * 60 * 60 * 24 * 365, + httpOnly: true, + }); + return user; + }, + + // Create An Item + async createItem(parent, args, ctx, info) { + if (!ctx.request.userId) { + throw new Error('You must be logged in to create an item'); + } + + const item = await ctx.db.mutation.createItem( + { + data: { + user: { + connect: { + id: ctx.request.userId, + }, + }, + ...args, + }, + }, + info + ); + return item; + }, + + async deleteItem(parent, args, ctx, info) { + const where = { + id: args.id, + }; + // 1. find the item + const item = await ctx.db.query.item({ where }, `{ user {id}, title, id, description }`); + // 2. if they 1. Don't own it AND 2. aren't an admin + if (item.user.id !== ctx.request.user.id && !ctx.request.user.permissions.includes('ADMIN')) { + throw new Error("You aren't allowed to delete that item!"); + } + + // 3. remove any orderItems this item is in + + return ctx.db.mutation.deleteItem({ where }, info); + }, + + async updateItem(parent, args, ctx, info) { + const user = ctx.request.user; + const item = await ctx.db.query.item({ where: { id: args.id } }, `{ user { id } }`); + + if (item.user.id !== user.id || !hasPermission(user, ['ADMIN'])) { + throw new Error('You are not allowed to update that item!'); + } + + const updates = { ...args }; + // remove the ID because you can't update that + delete updates.id; + return ctx.db.mutation.updateItem( + { + where: { id: args.id }, + data: { + ...updates, + }, + }, + info + ); + }, + + // Send password request + async requestReset(parent, args, ctx, info) { + // 1. find if there is a user with that email + const user = await ctx.db.query.user({ where: { email: args.email } }); + + if (!user) { + throw new Error(`No user with the email ${args.email}`); + } + // 2. Set a reset token, and a reset date + const resetToken = (await promisify(randomBytes)(20)).toString('hex'); + const resetTokenExpiry = Date.now() + 3600000; // 1 hour from now + console.log({ resetToken, resetTokenExpiry }); + const res = await ctx.db.mutation.updateUser({ + where: { email: args.email }, + data: { resetToken, resetTokenExpiry }, + }); + + // 3. Send them their token via email + const mailRes = await mail.transport.sendMail({ + from: 'wesbos@gmail.com', + to: user.email, + subject: 'Your password reset token', + html: mail.makeANiceEmail( + `Your password reset link is here! \n\n<a href="${process.env + .FRONTEND_URL}/reset?resetToken=${resetToken}">Click Here to reset</a>` + ), + }); + return res.updateUser; + }, + + async resetPassword(parent, args, ctx, info) { + // 1. Check that the passwords match + if (args.password !== args.confirmPassword) { + throw new Error('Passwords do not match'); + } + + // 2. Check that this is a legit resetToken + // 3. Check that it's not expired + // Note: If we didn't need the user here, we could also use db.exists() + const [user] = await ctx.db.query.users({ + where: { + resetToken: args.resetToken, + resetTokenExpiry_gte: Date.now() - 3600000, // within the last hour + }, + }); + + if (!user) { + throw new Error('This token is either invalid or expired.'); + } + + // 4. Hash the password + const password = await bcrypt.hash(args.password, 10); + + // 5. Update the users password + // clean up the resetToken fields at the same time + const updatedUser = await ctx.db.mutation.updateUser({ + where: { email: user.email }, + data: { + password, + resetToken: null, + resetTokenExpiry: null, + }, + }); + const token = jwt.sign({ userId: updatedUser.id }, process.env.APP_SECRET); + ctx.response.cookie('token', token, { + maxAge: 1000 * 60 * 60 * 24 * 365, + httpOnly: true, + }); + + // 6. send back the User for the GraphQL request on the client + return updatedUser; + }, + /* + Add to cart + */ + async addToCart(parent, args, ctx, info) { + const userId = ctx.request.userId; + + if (!userId) { + throw new Error('You must be signed in to add to cart!'); + } + + // 1. Check if there is a CartItem for this user and item already + const [existingCartItem] = await ctx.db.query.cartItems({ + where: { + user: { id: userId }, + item: { id: args.id }, + }, + }); + + if (existingCartItem) { + return ctx.db.mutation.updateCartItem( + { + where: { id: existingCartItem.id }, + data: { quantity: existingCartItem.quantity + 1 }, + }, + info + ); + } + + // Otherwise create a new cartItem + return ctx.db.mutation.createCartItem( + { + data: { + user: { + connect: { + id: userId, + }, + }, + item: { + connect: { id: args.id }, + }, + }, + }, + info + ); + }, + + // delete that cart item + async removeFromCart(parent, args, ctx, info) { + console.log(args.id); + // 1. Find the CartItem + const cartItem = await ctx.db.query.cartItem( + { + where: { id: args.id }, + }, + `{ id, user { id, permissions }}` + ); + // 2. Check they own it + if (cartItem.user.id !== ctx.request.userId) { + throw new Error("Cheatin' huh"); + } + // 3. Delete it + return ctx.db.mutation.deleteCartItem( + { + where: { + id: args.id, + }, + }, + info + ); + }, + + async createOrder(parent, args, ctx, info) { + const userId = ctx.request.userId; + const user = await ctx.db.query.user( + { where: { id: userId } }, + '{ id, name, email, cart { id, quantity, item { title, price, id, description, image } }}' + ); + // 1. Recalculate the total for the price + const amount = user.cart.reduce( + (tally, cartItem) => tally + cartItem.item.price * cartItem.quantity, + 0 + ); + // 2. Create a stripe charge + const charge = await stripe.charges.create({ + amount, + currency: 'usd', + source: args.token, + }); + + // 3. convert the items they want to OrderItems + const orderItems = user.cart.map(cartItem => { + const orderItem = { + quantity: cartItem.quantity, + // copy all the item details so it's there forever + ...cartItem.item, + item: { + // relationship to the Item incase we need it + connect: { id: cartItem.item.id }, + }, + user: { connect: { id: user.id } }, + }; + // scrub the ID from it because the orderItem will have it's own ID + delete orderItem.id; + return orderItem; + }); + + // 4. Create the Order + const order = await ctx.db.mutation.createOrder({ + data: { + total: charge.amount, + charge: charge.id, + items: { + create: orderItems, + }, + user: { + connect: { + id: user.id, + }, + }, + }, + }); + + // 5. Clean up, clear the users cart and send back { user, order } + // Delete the users current cart items + const cartItemIds = user.cart.map(cartItem => cartItem.id); + await ctx.db.mutation.deleteManyCartItems({ + where: { + id_in: cartItemIds, + }, + }); + + // 6. Send the order back to the client + return order; + }, + + async updateUser(parent, args, ctx, info) { + const userId = ctx.request.userId; + const updatedUser = await ctx.db.mutation.updateUser( + { + data: args, + where: { id: userId }, + }, + info + ); + return updatedUser; + }, + + async updatePermissions(parent, args, ctx, info) { + const userId = ctx.request.userId; + const currentUser = await ctx.db.query.user({ where: { id: userId } }, info); + if (!currentUser) throw new Error('You Must be logged in to updat permissions!'); + hasPermission(currentUser, ['ADMIN', 'PERMISSIONUPDATE']); + return ctx.db.mutation.updateUser( + { + data: { + permissions: { + set: args.permissions, + }, + }, + where: { id: args.userId }, + }, + info + ); + }, +}; + +module.exports = mutations; diff --git a/finished-application/backend/src/resolvers/Query.js b/finished-application/backend/src/resolvers/Query.js new file mode 100644 index 0000000..51c2c42 --- /dev/null +++ b/finished-application/backend/src/resolvers/Query.js @@ -0,0 +1,62 @@ +const { hasPermission } = require('../utils'); + +const { forwardTo } = require('prisma-binding'); + +const Query = { + items: forwardTo('db'), + itemsConnection: forwardTo('db'), + + async order(parent, args, ctx, info) { + // 1. make sure they are signed in + if (!ctx.request.userId) { + throw new Error('You Must be signed in to view an order'); + } + + // 2. Create the query + const where = { + id: args.id, + user: { + id: ctx.request.userId, + }, + }; + // 3. Fire off the query + const [order] = await ctx.db.query.orders({ where }, info); + + // 4. Check that they are allowed to view the order + if (order.user.id !== ctx.request.userId || hasPermission(ctx.request.user, ['ADMIN'])) { + throw new Error("You don't have permission"); + } + // 5. If everything checks out, return the order + return order; + }, + + me(parent, args, ctx, info) { + if (!ctx.request.userId) { + return null; // don't error out, just return nothing + } + + return ctx.db.query.user( + { + where: { id: ctx.request.userId }, + }, + info + ); + }, + + async orders(parent, args, ctx, info) { + const { userId } = ctx.request; + if (!userId) { + throw new Error('You must be signed in to see your orders'); + } + return ctx.db.query.orders( + { + where: { + user: { id: userId }, + }, + }, + info + ); + }, +}; + +module.exports = Query; diff --git a/finished-application/backend/src/schema.graphql b/finished-application/backend/src/schema.graphql new file mode 100644 index 0000000..07533eb --- /dev/null +++ b/finished-application/backend/src/schema.graphql @@ -0,0 +1,39 @@ +# The Below line looks like a comment, but it's not! +# import * from './generated/prisma.graphql' + +type Query { + me: User + order(id: ID!): Order! + orders(where: OrderWhereInput, orderBy: OrderOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Order]! + items(where: ItemWhereInput, orderBy: ItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Item]! + itemsConnection(where: ItemWhereInput, orderBy: ItemOrderByInput, first: Int, last: Int, skip: Int): ItemConnection! +} + +type Mutation { + signup(email: String!, password: String!, name: String!): User! + signin(email: String!, password: String!): User! + requestReset(email: String!): User + resetPassword(resetToken: String!, password: String!, confirmPassword: String!): User! + createItem(title: String, description: String, price: Int, image: String, largeImage: String): Item! + deleteItem(id: ID!): Item! + updateItem(id: ID!, title: String, description: String, price: Int): Item! + addToCart(id: ID!): CartItem + removeFromCart(id: ID!): CartItem + createOrder(token: String!): Order! + updateUser(name: String): User + signout: SuccessMessage + updatePermissions(permissions: [Permission], userId: ID!): User +} + +type SuccessMessage { + message: String +} + +type User { + id: ID! + email: String! + name: String! + orders: [Order!]! + cart: [CartItem!]! + permissions: [Permission]! +} diff --git a/finished-application/backend/src/stripe.js b/finished-application/backend/src/stripe.js new file mode 100644 index 0000000..c6be8ca --- /dev/null +++ b/finished-application/backend/src/stripe.js @@ -0,0 +1 @@ +module.exports = require('stripe')(process.env.STRIPE_SECRET); diff --git a/finished-application/backend/src/utils.js b/finished-application/backend/src/utils.js new file mode 100644 index 0000000..faf028b --- /dev/null +++ b/finished-application/backend/src/utils.js @@ -0,0 +1,17 @@ +function hasPermission(user, permissionsNeeded) { + const matchedPermissions = user.permissions.filter(permissionTheyHave => + permissionsNeeded.includes(permissionTheyHave) + ); + if (!matchedPermissions.length) { + throw new Error(`You do not have sufficient permissions + + : ${permissionsNeeded} + + You Have: + + ${user.permissions} + `); + } +} + +exports.hasPermission = hasPermission; |
