From eabff7cd396d1f37ceb929e666f082ce57f07919 Mon Sep 17 00:00:00 2001 From: Wes Bos Date: Fri, 2 Feb 2018 15:23:20 -0500 Subject: updateS --- backend/database/datamodel.graphql | 1 + backend/database/prisma.yml | 8 ++-- backend/package.json | 8 +++- backend/src/generated/prisma.graphql | 22 ++++++++++ backend/src/index.js | 10 ++--- backend/src/resolvers/Mutation.js | 31 +++++++++++++- backend/src/resolvers/Query.js | 18 +++++--- backend/src/schema.graphql | 20 ++++++++- backend/tests/item.test.js | 81 ++++++++++++++++++++++++++++++++++++ frontend/.vscode/settings.json | 3 ++ frontend/components/CreateItem.js | 14 +++---- frontend/components/Header.js | 16 +++---- frontend/components/Item.js | 32 +++++++++----- frontend/components/Items.js | 48 ++++++++++----------- frontend/components/Nav.js | 7 +++- frontend/components/Page.js | 14 +++---- frontend/components/Pagination.js | 25 +++++------ frontend/config.js | 2 +- frontend/enhancers/enhancers.js | 13 ++++++ frontend/pages/index.js | 15 +++---- frontend/pages/items.js | 3 +- frontend/queries/fragments.js | 6 --- frontend/queries/index.js | 71 +++++++++++++++++++------------ 23 files changed, 333 insertions(+), 135 deletions(-) create mode 100644 backend/tests/item.test.js create mode 100644 frontend/.vscode/settings.json create mode 100644 frontend/enhancers/enhancers.js diff --git a/backend/database/datamodel.graphql b/backend/database/datamodel.graphql index 605e242..c383b23 100644 --- a/backend/database/datamodel.graphql +++ b/backend/database/datamodel.graphql @@ -16,6 +16,7 @@ type User { posts: [Post!]! website: String age: Int + dummy: String } type Item { diff --git a/backend/database/prisma.yml b/backend/database/prisma.yml index 6570be2..ce652ef 100644 --- a/backend/database/prisma.yml +++ b/backend/database/prisma.yml @@ -1,12 +1,12 @@ # the name for the service (will be part of the service's HTTP endpoint) -service: my-own +service: sick-fits # the cluster and stage the service is deployed to stage: ${env:PRISMA_STAGE} # to disable authentication: -# disableAuth: true -secret: ${env:PRISMA_SECRET} +disableAuth: true +# secret: ${env:PRISMA_SECRET} # the file path pointing to your data model datamodel: datamodel.graphql @@ -17,5 +17,3 @@ datamodel: datamodel.graphql cluster: ${env:PRISMA_CLUSTER} - -disableAuth: true \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index ca26206..86ad2b6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -2,9 +2,11 @@ "name": "my-own", "scripts": { "start": "nodemon -e js,graphql -x node -r dotenv/config src/index.js", - "debug": "nodemon -e js,graphql -x node --inspect -r dotenv/config src/index.js", + "debug": + "nodemon -e js,graphql -x node --inspect -r dotenv/config src/index.js", "playground": "graphql playground", - "dev": "npm-run-all --parallel start playground" + "dev": "npm-run-all --parallel start playground", + "test": "jest" }, "dependencies": { "bcryptjs": "2.4.3", @@ -15,6 +17,8 @@ "devDependencies": { "dotenv": "4.0.0", "graphql-cli": "2.13.0", + "graphql-request": "^1.4.1", + "jest-cli": "^22.1.4", "nodemon": "1.14.11", "npm-run-all": "4.1.2", "prisma": "1.0.11" diff --git a/backend/src/generated/prisma.graphql b/backend/src/generated/prisma.graphql index 50aaa97..e59a1c9 100644 --- a/backend/src/generated/prisma.graphql +++ b/backend/src/generated/prisma.graphql @@ -30,6 +30,7 @@ type User implements Node { posts(where: PostWhereInput, orderBy: PostOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Post!] website: String age: Int + dummy: String } @@ -420,6 +421,7 @@ input UserCreateInput { name: String! website: String age: Int + dummy: String posts: PostCreateManyWithoutAuthorInput } @@ -434,6 +436,7 @@ input UserCreateWithoutPostsInput { name: String! website: String age: Int + dummy: String } type UserEdge { @@ -454,6 +457,8 @@ enum UserOrderByInput { website_DESC age_ASC age_DESC + dummy_ASC + dummy_DESC updatedAt_ASC updatedAt_DESC createdAt_ASC @@ -467,6 +472,7 @@ type UserPreviousValues { name: String! website: String age: Int + dummy: String } type UserSubscriptionPayload { @@ -492,6 +498,7 @@ input UserUpdateInput { name: String website: String age: Int + dummy: String posts: PostUpdateManyWithoutAuthorInput } @@ -510,6 +517,7 @@ input UserUpdateWithoutPostsDataInput { name: String website: String age: Int + dummy: String } input UserUpdateWithoutPostsInput { @@ -604,6 +612,20 @@ input UserWhereInput { age_lte: Int age_gt: Int age_gte: Int + dummy: String + dummy_not: String + dummy_in: [String!] + dummy_not_in: [String!] + dummy_lt: String + dummy_lte: String + dummy_gt: String + dummy_gte: String + dummy_contains: String + dummy_not_contains: String + dummy_starts_with: String + dummy_not_starts_with: String + dummy_ends_with: String + dummy_not_ends_with: String posts_every: PostWhereInput posts_some: PostWhereInput posts_none: PostWhereInput diff --git a/backend/src/index.js b/backend/src/index.js index c73cb74..fefe598 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -24,10 +24,10 @@ const server = new GraphQLServer({ }), }); -// This is an example of custom express middlware -server.express.use((req, res, next) => { - console.log("Hi I'm middlware"); - next(); -}); +// // This is an example of custom express middlware +// server.express.use((req, res, next) => { +// // console.log("Hi I'm middlware"); +// next(); +// }); server.start(() => console.log('Server is running on http://localhost:4000')); diff --git a/backend/src/resolvers/Mutation.js b/backend/src/resolvers/Mutation.js index 2796cf8..fbae6fe 100644 --- a/backend/src/resolvers/Mutation.js +++ b/backend/src/resolvers/Mutation.js @@ -35,8 +35,35 @@ const mutations = { }, // Creation of Post Mutations - createItem: forwardTo('db'), - deleteItem: forwardTo('db'), + async createItem(parent, args, ctx, info) { + return ctx.db.mutation.createItem({ data: { ...args } }, info); + }, + + async deleteItem(parent, args, ctx, info) { + console.log('gonna delete one'); + console.log(args); + return ctx.db.mutation.deleteItem( + { + where: { + id: args.id, + }, + }, + info + ); + }, + + async updateItem(parent, args, ctx, info) { + console.log('Updating an item'); + const updates = { ...args }; + delete updates.id; + return ctx.db.mutation.updateItem({ + where: { id: args.id }, + data: { + ...updates, + }, + }); + }, + async createDraft(parent, { title, text }, ctx, info) { // const userId = getUserId(ctx); const userId = getUserId(ctx); diff --git a/backend/src/resolvers/Query.js b/backend/src/resolvers/Query.js index 9d9d142..a2eb749 100644 --- a/backend/src/resolvers/Query.js +++ b/backend/src/resolvers/Query.js @@ -1,18 +1,26 @@ const { getUserId, Context } = require('../utils'); +const { forwardTo } = require('prisma-binding'); const Query = { + items(parent, args, ctx, info) { + // check auth + return ctx.db.query.items({ ...args }, info); + }, + + itemsConnection: forwardTo('db'), + feed(parent, args, ctx, info) { - return ctx.db.query.posts({ where: { isPublished: true } }, info); + return ctx.db.query.posts({}, info); }, drafts(parent, args, ctx, info) { - const id = getUserId(ctx); + // const id = getUserId(ctx); const where = { isPublished: false, - author: { - id, - }, + // author: { + // id, + // }, }; return ctx.db.query.posts({ where }, info); diff --git a/backend/src/schema.graphql b/backend/src/schema.graphql index 788d490..993236e 100644 --- a/backend/src/schema.graphql +++ b/backend/src/schema.graphql @@ -1,10 +1,25 @@ -# import Post, Item, ItemCreateInput from './generated/prisma.graphql' +# import ItemWhereInput, ItemOrderByInput, allItems, Post, Item, ItemCreateInput, ItemOrderByInput, ItemWhereInput from './generated/prisma.graphql' type Query { feed: [Post!]! drafts: [Post!]! post(id: ID!): Post! me: User + itemsConnection( + where: ItemWhereInput + orderBy: ItemOrderByInput + skip: Int + after: String + before: String + first: Int + last: Int + ): ItemConnection! + items( + where: ItemWhereInput + orderBy: ItemOrderByInput + skip: Int + first: Int + ): [Item]! } # import Mutation from './generated/prisma.graphql' @@ -16,8 +31,9 @@ type Mutation { publish(id: ID!): Post! deletePost(id: ID!): Post! updatePost(id: ID!, title: String, text: String): Post! - createItem(data: ItemCreateInput): Item! + createItem(title: String, description: String, price: Int): Item! deleteItem(id: ID!): Item! + updateItem(id: ID!, title: String, description: String, price: Int): Item! } type AuthPayload { diff --git a/backend/tests/item.test.js b/backend/tests/item.test.js new file mode 100644 index 0000000..0e47419 --- /dev/null +++ b/backend/tests/item.test.js @@ -0,0 +1,81 @@ +const { request } = require('graphql-request'); + +let id; + +test('Creating an item', async () => { + const query = ` + mutation createItem { + createItem(title: "wes", description:"Bos", price: 500) { + title + description + price + id + } + } + `; + + const { createItem } = await request('http://localhost:4000', query); + + expect(createItem.title).toEqual('wes'); + expect(createItem.description).toEqual('Bos'); + expect(createItem).toHaveProperty('id'); + expect(createItem.price).toEqual(500); + id = createItem.id; +}); + +test('Get an array of items', async () => { + const query = ` + query getAllItems { + items { + title + id + description + price + } + } + `; + + const { items } = await request('http://localhost:4000', query); + expect(items.length).toBeGreaterThan(0); +}); + +test('Query a specific item', async () => { + const query = ` + query findAnItem { + items(where: { + id: "${id}" + }) { + title + id + description + price + } + } + `; + + const { items } = await request('http://localhost:4000', query); + const item = items[0]; + expect(item.title).toEqual('wes'); + expect(item.description).toEqual('Bos'); + expect(item.price).toEqual(500); +}); + +// Test Delete that item +test('delete an item', async () => { + const query = ` + mutation remove { + deleteItem(id: "${id}") { + id + } + } + `; + const res = await request('http://localhost:4000', query); + expect(res.deleteItem.id).toEqual(id); +}); + +// mutation updateItem { +// updateItem(id: "cjd21afpr47m00172nigtlkw8", title: "WES from playground") { +// id +// title +// } +// } diff --git a/frontend/.vscode/settings.json b/frontend/.vscode/settings.json new file mode 100644 index 0000000..a92f73f --- /dev/null +++ b/frontend/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "eslint.enable": false +} \ No newline at end of file diff --git a/frontend/components/CreateItem.js b/frontend/components/CreateItem.js index 27b62e5..e4aa77c 100644 --- a/frontend/components/CreateItem.js +++ b/frontend/components/CreateItem.js @@ -7,10 +7,10 @@ import { fileEndpoint } from '../config'; class CreateLink extends Component { state = { - description: '', - title: '', + description: 'test', + title: 'testing title', image: '', - price: 0, + price: 500, fullPrice: 0, loading: false, error: { @@ -41,20 +41,19 @@ class CreateLink extends Component { _createLink = async e => { e.preventDefault(); // pull the values from state - const { description, title, image, price, fullPrice } = this.state; + const { description, title, price } = this.state; // create a mutation // TODO: handle any errors // turn loading on this.setState({ loading: true }); try { + console.log('About to call create item mutation'); const res = await this.props.createItemMutation({ // pass in those variables from state variables: { description, title, price: parseInt(price), - fullPrice, - imageId: image, }, }); } catch (error) { @@ -98,7 +97,7 @@ class CreateLink extends Component { type="text" placeholder="The desc for this item" /> - @@ -117,6 +116,7 @@ export default graphql(CREATE_ITEM_MUTATION, { // This is much Better / efficient // Notice how the variable is called createItem - that is because createItem is the name of the query! update: (proxy, { data: { createItem } }) => { + console.log('UPDATING'); const data = proxy.readQuery({ query: ALL_ITEMS_QUERY }); // data is our store, allItems is our sub-"state", it's just an array. We can just add it to console.log({ createItem }); diff --git a/frontend/components/Header.js b/frontend/components/Header.js index cc4cca7..5624fb5 100644 --- a/frontend/components/Header.js +++ b/frontend/components/Header.js @@ -19,19 +19,18 @@ class Header extends Component { // This fetches the new data, but doesn't populate the user via props // this.props.currentUserQuery.refetch(); // This fetches the new data, and populates the user via props - setTimeout(this.props.currentUserQuery.refetch, 1); + // setTimeout(this.props.currentUserQuery.refetch, 1); } render() { - const user = this.props.currentUserQuery.user || {}; - const { email = '' } = user; + // const user = this.props.currentUserQuery.user || {}; + // const { email = '' } = user; return (
-

+ {/*

Signed in as {email} -

- - +

*/} + {/* */}
); @@ -39,4 +38,5 @@ class Header extends Component { } const userEnhancer = graphql(CURRENT_USER_QUERY, { name: 'currentUserQuery' }); -export default compose(userEnhancer)(Header); +// export default compose(userEnhancer)(Header); +export default Header; diff --git a/frontend/components/Item.js b/frontend/components/Item.js index 1b47fcf..eb19918 100644 --- a/frontend/components/Item.js +++ b/frontend/components/Item.js @@ -1,10 +1,10 @@ -import styled from 'styled-components'; -import slugify from 'slugify'; -import { Link } from '../routes'; -import AddToCart from './AddToCart'; -import makeImage from '../lib/image'; -import TakeMyMoney from './TakeMyMoney'; -import formatMoney from '../lib/formatMoney'; +import styled from "styled-components"; +import slugify from "slugify"; +import { Link } from "../routes"; +import AddToCart from "./AddToCart"; +import makeImage from "../lib/image"; +import TakeMyMoney from "./TakeMyMoney"; +import formatMoney from "../lib/formatMoney"; const Item = styled.div` background: #f3f3f3; @@ -16,13 +16,19 @@ const Item = styled.div` const ItemComponent = ({ item }) => ( - {item.image ? {item.title} : null} + {item.image ? ( + {item.title} + ) : null}

{item.title} @@ -54,7 +60,13 @@ const ItemComponent = ({ item }) => ( - + ); diff --git a/frontend/components/Items.js b/frontend/components/Items.js index 053c9af..3e2c738 100644 --- a/frontend/components/Items.js +++ b/frontend/components/Items.js @@ -1,21 +1,25 @@ -import { Component } from 'react'; -import { withApollo, graphql, compose } from 'react-apollo'; -import styled from 'styled-components'; -import Pagination from './Pagination'; -import Item from './Item'; +import { Component } from "react"; +import { withApollo, graphql, compose } from "react-apollo"; +import styled from "styled-components"; +import Pagination from "./Pagination"; +import Item from "./Item"; +import { itemEnhancer } from "../enhancers/enhancers"; -import { ALL_ITEMS_QUERY, DELETE_ITEM_MUTATION } from '../queries'; +import { ALL_ITEMS_QUERY, DELETE_ITEM_MUTATION } from "../queries"; -const Title = styled.h1`font-size: 10px;`; +const Title = styled.h1` + font-size: 10px; +`; const Items = styled.div` display: grid; - grid-template-columns: repeat(4, calc(33% - 20px)); + grid-template-columns: repeat(4, 1fr); grid-gap: 20px; `; class ItemList extends Component { componentDidMount() { + console.log(this.props.allItemsQuery); this.prefetchNextItems(this.props.page); } @@ -32,8 +36,8 @@ class ItemList extends Component { this.props.client.query({ query: ALL_ITEMS_QUERY, variables: { - skip: page * 3 - 3, - }, + skip: page * 3 - 3 + } }); }; @@ -50,13 +54,15 @@ class ItemList extends Component { } // 3 - const itemsToRender = this.props.allItemsQuery.allItems; + const itemsToRender = this.props.allItemsQuery.items; return (
Items For Sale - {itemsToRender.map((item, i) => )} + + {itemsToRender.map((item, i) => )} +
); } @@ -66,20 +72,8 @@ class ItemList extends Component { // We export the graphQL HOC - this will fetch the data and inject it into the ItemList compeont via props -// Create some Enhancers -const itemEnhancer = graphql(ALL_ITEMS_QUERY, { - name: 'allItemsQuery', - options({ page }) { - return { - variables: { - skip: page * 3 - 3, - }, - }; - }, -}); - const deleteItemEnhancer = graphql(DELETE_ITEM_MUTATION, { - name: 'removeItemMutation', + name: "removeItemMutation", options: { update: (proxy, { data: { deleteItem } }) => { // grab the data from our cache @@ -90,8 +84,8 @@ const deleteItemEnhancer = graphql(DELETE_ITEM_MUTATION, { // and then "set state" (update cache), so it will update wherever we have used this data on the page proxy.writeQuery({ query: ALL_ITEMS_QUERY, data }); - }, - }, + } + } }); export default withApollo(compose(itemEnhancer, deleteItemEnhancer)(ItemList)); diff --git a/frontend/components/Nav.js b/frontend/components/Nav.js index d1f341d..25965c2 100644 --- a/frontend/components/Nav.js +++ b/frontend/components/Nav.js @@ -1,5 +1,5 @@ -import Link from 'next/link'; -import styled from 'styled-components'; +import Link from "next/link"; +import styled from "styled-components"; const StyledUl = styled.ul` margin: 0; @@ -28,6 +28,9 @@ const Nav = () => ( Home + + Just Items + Sign Up diff --git a/frontend/components/Page.js b/frontend/components/Page.js index f45d421..ceb0939 100644 --- a/frontend/components/Page.js +++ b/frontend/components/Page.js @@ -1,8 +1,8 @@ -import styled from 'styled-components'; -import Header from './Header'; -import Meta from './Meta'; -import Nav from './Nav'; -import CartList from './CartList'; +import styled from "styled-components"; +import Header from "./Header"; +import Meta from "./Meta"; +import Nav from "./Nav"; +import CartList from "./CartList"; const StyledPage = styled.div` font-family: sans-serif; @@ -15,8 +15,8 @@ const Page = ({ children }) => (