Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["es2016", "env"],
"plugins": ["transform-object-rest-spread"]
}
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BUYERS_URL=mongodb://test:graphql12345@ds157819.mlab.com:57819/graphql-test
3 changes: 3 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "standard"
}
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ typings/
# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
# # dotenv environment variables file
# .env

# next.js build output
.next
3 changes: 3 additions & 0 deletions Models/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Todos from './todos'

export { Todos }
11 changes: 11 additions & 0 deletions Models/todos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import mongoose, { Schema } from 'mongoose'

const todoSchema = new Schema({
description: { type: String, required: true },
completed: Boolean,
priority: Number
}, {
timestamps: true
})

export default mongoose.model('Todos', todoSchema)
99 changes: 59 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# MadeByCodera GraphQL Test
# GraphQL Api Test Task

## Description
**npm run dev** - to start dev server

You are tasked with writing the GraphQL api for a new Todo List mobile app.
http://localhost:4001/graphql/v1/playground - test playground

### Todo GraphQL Type
The structure of the Todo GraphQL Type according to the frontend developer should look like the following:

Field | Data Type | Description
------------ | ------------- | -------------
Expand All @@ -16,43 +15,63 @@ completed | Boolean | Indicates if the todo is complete. Defaults to fa
priority | Int | 1 is the highest priority. Defaults to 1.

### Todo GraphQL Query and Mutations
And the frontend developer is counting on you to implement the following 5 methods under the GraphQL api endpoint:
1. **List Todos** - Query - Retrieved todos can be sorted by the `priority`, `createdAt`, or `description` fields in ascending or descending order. By default the todos are unsorted. In addition, the todos can be filtered by the `completed` field.
2. **Create todo** - Mutation - `description` is required. `priority` is optional and if not provided should default to 1. The rest of the fields: `id`, `createdAt`, and `completed` should have defaults supplied for them as noted in the Todo GraphQL Type mentioned above.
3. **Update todo** - Mutation - Should update a todo based on the `id` provided in the request. `description` and/or `priority` fields can be updated. `priority` must be 1 or greater if sent in request.
4. **Mark todo complete** - Mutation - Should update a todo's `complete` field to `true` based on the `id` provided in the request.
4. **Delete todo** - Mutation - Should delete a todo based on the `id` provided in the request.

### Documentation

The front end developer would also like a little bit of documentation to help him use the api. The easiest way to provide documentation is to add comments to each query, mutation, and type that is defined in the GraphQL schema. The following multi-line comments example highlights how you should be declaring comments in the query, mutation, or type definitions:

1. **List Todos** - Mutation to update existing Todo
Examples
```
"""
<my comments go here>
"""
{
todos {
_id,
createdAt,
priority,
completed
}
}
```
```
{
todo(id: "5d35ab185c1d0452a888ea63") {
_id
}
}
```

This will make it very easy for the frontend developer to see them when inspecting the running GraphQL server using [GraphQL Playground](https://www.apollographql.com/docs/apollo-server/features/graphql-playground/)

As demonstrated in the following screenshot

[![graphql-playground-example.png](https://i.postimg.cc/rw6HMzmt/graphql-playground-example.png)](https://postimg.cc/VdRgFfXY)

### Testing

The front end developer would also like to see examples of using this GraphQL api to help him make his own requests, as well as show to him that the api will work as he expects. You may do this however you see fit. The simplest way possible would be to write variations of query and mutation requests in GraphQL Playground and then click `COPY CURL`, pasting the curl command into a shell script that you could provide at the root of the repo called `test.sh`.

[![graphql-test-copy-curl-example.png](https://i.postimg.cc/8c0vxzyb/graphql-test-copy-curl-example.png)](https://postimg.cc/BP2tK4f8)

## Instructions
1. Fork this repository. Do all of your work on your personal fork. Then create a new pull request on your personal fork
2. You must implement this graphql backend using [Apollo Server](https://www.apollographql.com/docs/apollo-server/)
3. You can use whatever data structure you are most comfortable with for storing the data such as in-memory, MySQL, Postgres, SQLite, MongoDB, etc... . Using Postgres is preferred and considered a plus, since that is what we use currently.
4. This repo should be an npm project such that we can install the dependencies that you define for the server in order to run it using Node.

Here is an example of creating a pull request from a fork of a repository:
[![pull-request-example.png](https://i.postimg.cc/QCgrr53S/pull-request-example.png)](https://postimg.cc/RJ0Y7Wqn)

## NOTE
If you are not storing the data in-memory, please commit a sql dump file of the database schema to the repo. Please add a note of where this file is located in this `README.md` if the sql dump is not located at the root of the repo. Your submission will be **DISCARDED** if you **DO NOT** commit a sql dump file for your implementation, if it uses a database.
}
2. **createTodo** - Mutation to create new Todo.
```
mutation {
createTodo(description: "test", completed: false, priority: 2) {
description,
completed,
_id
}
}
```
3. **updateTodo** - Mutation - Should update a todo based on the `id` provided in the request. `description` and/or `priority` fields can be updated. `priority` must be 1 or greater if sent in request.
```
mutation {
updateTodo(id: "5d35b16969e04e56fbf19302") {
priority,
_id
}
}
```
4. **markCompleted** - Mutation to mark existing Todo as completed.
```
mutation {
markCompleted(id: "5d35b16969e04e56fbf19302") {
_id,
createdAt,
priority,
completed
}
}
```
4. **deleteTodo** - Mutation to delete existing Todo based on id.
```
mutation {
deleteTodo(id: "5d359accf055c247a7748bd9") {
_id
}
}
```
3 changes: 3 additions & 0 deletions Resolvers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import todos from './todos'

export default [todos]
96 changes: 96 additions & 0 deletions Resolvers/todos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Todos } from '../Models'

const sortByFields = {
description: true,
createdAt: true,
priority: true
}
const sortOrders = ['asc', 'desc', 'ascending', 'descending']

export default {
Query: {
todos: async (
_,
{ sortBy = 'createdAt', order = sortOrders[1], completed = false }
) => {
try {
const sortQuery = {}
// deciding if we should sort and what is the order
if (sortBy && sortByFields[sortBy]) {
sortQuery[sortBy] = sortOrders.includes(order) ? order : sortOrders[1]
}

const todos = await Todos
.find({
completed
})
.sort(sortQuery)
.lean(true).exec()
return todos
} catch (e) {
console.log(e)
return []
}
},
todo: async (_, { id }, { models }) => {
try {
const todo = await Todos.findById(id).lean(true).exec()
return todo
} catch (e) {
return {}
}
}
},

Mutation: {
createTodo: async (
_,
{ description, completed, priority = 1 },
{ models }
) => {
try {
const todo = new Todos({
description,
completed: completed || false,
priority
})
const newTodo = await todo.save()
return newTodo
} catch (e) {
console.log('Error', e)
throw e
}
},
deleteTodo: async (_, { id }) => {
try {
const deletedData = await Todos.deleteOne({ _id: id }).exec()
console.log('deletedData', deletedData)
return { _id: id }
} catch (e) {
console.log('Error', e)
throw e
}
},
updateTodo: async (_, { id, ...data }) => {
try {
const { priority } = data
if (priority === 0 || (priority && priority < 1)) throw new Error('priority must be 1 or greater')

const updatedTodo = await Todos.findOneAndUpdate({ _id: id }, data, { new: true }).exec()
return updatedTodo
} catch (e) {
console.log('Error', e)
throw e
}
},
markCompleted: async (_, { id }) => {
try {
const updatedTodo = await Todos.findOneAndUpdate({ _id: id }, { completed: true }, { new: true }).exec()
return updatedTodo
} catch (e) {
console.log('Error', e)
throw e
}
}
}
}
3 changes: 3 additions & 0 deletions Schemas/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import todos from './todos'

export default [todos]
33 changes: 33 additions & 0 deletions Schemas/todos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { gql } from 'apollo-server-express'

export default gql`
type Status {
_id: String!
}

type Todo {
_id: ID!
description: String!
createdAt: Date
completed: Boolean
priority: Int!
}
scalar Date
type Query {
"Query to fetch Todos. can be sorted by 'description', 'createdAt' and 'priority' and filtered by 'completed' status"
todos(completed: Boolean, sortBy: String, order: String): [Todo!]
"Query to fetch Todo by id"
todo(id: ID!): Todo
}

type Mutation {
"Mutation to create new Todo"
createTodo(description: String!, completed: Boolean, priority: Int): Todo
"Mutation to update existing Todo"
updateTodo(id: String!, description: String, priority: Int): Todo
"Mutation to mark existing Todo as completed"
markCompleted(id: String!): Todo
"Mutation to delete existing Todo based on id"
deleteTodo(id: String!): Status
}
`
39 changes: 39 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import express from 'express'
import { ApolloServer } from 'apollo-server-express'
import cors from 'cors'
import mongoose from 'mongoose'

import schema from './Schemas'
import resolvers from './Resolvers'
import * as models from './Models'

const app = express()

app.use(cors())

const apolloConfig = {
context: { models },
typeDefs: schema,
resolvers
}
const server = new ApolloServer(apolloConfig)

server.applyMiddleware({ app, path: '/graphql/v1/playground' })

mongoose.connection.on('connected', () => {
console.log('Connected to DB')
})
mongoose.connection.on('disconnected', () => {
console.log('Disconnected')
})
mongoose.connection.on('error', err => {
console.log('DB Connection Error: ', err)
})

mongoose
.connect(process.env.BUYERS_URL, { useNewUrlParser: true })
.then(() => {
app.listen({ port: 4001 }, () => {
console.log('Apollo Server on http://localhost:4001/graphql/v1/playground')
})
})
4 changes: 4 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
require('babel-register')
require('babel-polyfill')
require('dotenv').config()
require('./app.js')
Loading