5 Commits

Author SHA1 Message Date
Riccardo
5368f40b04 Progress 2021-08-29 10:39:39 +02:00
Riccardo
2f119e43bd Changes 2021-08-16 11:06:02 +02:00
Riccardo
78db95aa5f Some cleaning 2021-08-16 10:08:43 +02:00
Riccardo
959c6dbc33 Fix 2021-08-16 09:06:16 +02:00
Riccardo
93169095c0 gesJav-wocxo3-vedgyg 2021-08-16 08:54:17 +02:00
21 changed files with 6469 additions and 6326 deletions

View File

@@ -5,8 +5,8 @@ import AppointmentList from './appointment/AppointmentList';
import CreateAppointment from './appointment/CreateAppointment';
import UpdateAppointemnt from './appointment/UpdateAppointment';
import Calendar from './Calendar';
import Search from './Search';
import { Switch, Route } from 'react-router-dom';
// import ProductList from './ProductList';
const App = () => {
return (
@@ -14,12 +14,12 @@ const App = () => {
<Header />
<div className="ph3 pv1 background-gray">
<Switch>
{/* <Route exact path="/" component={ProductList} /> */}
<Route exact path="/" component={AppointmentList} />
<Route exact path="/create" component={CreateAppointment} />
<Route exact path="/update/:_id" component={UpdateAppointemnt} />
<Route exact path="/login" component={Login} />
<Route exact path="/calendar" component={Calendar} />
<Route exact path="/search" component={Search} />
</Switch>
</div>
</div>

View File

@@ -1,14 +0,0 @@
import React from 'react';
const Product = (props) => {
const { product } = props;
return (
<div>
<div>
<b>{product.title}</b>: only {product.qty}!
</div>
</div>
);
};
export default Product;

View File

@@ -1,40 +0,0 @@
import React from 'react';
import Product from './Product';
import { useQuery, gql } from '@apollo/client';
const FEED_QUERY = gql`
{
allProducts{
title
qty
}
}
`;
const ProductList = () => {
const { data } = useQuery(FEED_QUERY);
console.log("Data:", data);
if (data !== undefined) {
return (
<div>
{
data.allProducts.map((product) => (
<Product key={product.id} product={product} />
))
}
</div>
);
} else {
return (
<div>
Rendering...
</div>
)
}
};
export default ProductList;

View File

@@ -1,63 +1,39 @@
import React, { useState } from 'react';
import { useMutation } from '@apollo/client';
import { useLazyQuery } from '@apollo/client';
import gql from 'graphql-tag';
import Appointment from './Appointment';
import { Link } from 'react-router-dom';
import Appointment from './appointment/Appointment';
const FEED_SEARCH_QUERY = gql`
query FeedSearchQuery($filter: String!) {
feed(filter: $filter) {
id
links {
id
appointments {
_id
title
description
type
createdBy {
id
username
}
# follows {
# id
# user {
# id
# }
# }
}
}
}
`;
const Search = () => {
const [searchFilter, setSearchFilter] = useState('');
const [executeSearch, { data }] = useLazyQuery(
FEED_SEARCH_QUERY
);
return (
<>
<div>
Search
<input
type="text"
onChange={(e) => setSearchFilter(e.target.value)}
/>
<button
onClick={() =>
executeSearch({
variables: { filter: searchFilter }
})
}
>
OK
</button>
</div>
{/* {data &&
data.feed.appointments.map((appointment, index) => (
<Link key={appointment.id} link={appointment} index={index} />
))} */}
</>
);
const [searchFilter, setSearchFilter] = useState('');
const [executeSearch, { data }] = useLazyQuery(
FEED_SEARCH_QUERY
);
return (
<>
<div>
Search
<input type="text" onChange={(e) => setSearchFilter(e.target.value)}/>
<button onClick={() => executeSearch({ variables: { filter: searchFilter } })}>OK</button>
</div>
{data &&
data.feed.appointments.map((appointment, index) => (
<Appointment key={appointment.id} appointment={appointment} index={index} />
))}
</>
);
};
export default Search;

View File

@@ -21,15 +21,16 @@ const Appointment = (props) => {
onCompleted: () => history.push('/')
})
const updateAppointment = () => {
let path = `/update/${appointment._id}`;
history.push(path);
}
// const updateAppointment = () => {
// let path = `/update/${appointment._id}`;
// history.push(path);
// }
return (
<div>
<div>
<b>{appointment.title}</b> starts at {appointment.start}, ends at {appointment.end}. It is described as "{appointment.description}"<button onClick={deleteAppointment}>DELETE</button><button onClick={updateAppointment}>EDIT</button>
<b>{appointment.title}</b> starts at {appointment.start}, ends at {appointment.end}. It is described as "{appointment.description}"<button onClick={deleteAppointment}>DELETE</button>
{/* <button onClick={updateAppointment}>EDIT</button> */}
</div>
</div>
);

View File

@@ -16,7 +16,7 @@ export const APPOINTMENTS_QUERY = gql`
const AppointmentList = () => {
const { data, loading } = useQuery(APPOINTMENTS_QUERY);
const { data } = useQuery(APPOINTMENTS_QUERY);
if (data !== undefined) {
return (

View File

@@ -43,34 +43,34 @@ const CreateAppointment = () => {
start: formState.start,
end: formState.end
},
// update: (cache, { data: { createAppointment } }) => {
// const take = APPOINTMENTS_PER_PAGE;
// const skip = 0;
// const orderBy = { createdAt: 'desc' };
update: (cache, { data: { createAppointment } }) => {
const take = APPOINTMENTS_PER_PAGE;
const skip = 0;
const orderBy = { createdAt: 'desc' };
// const data = cache.readQuery({
// query: APPOINTMENTS_QUERY,
// variables: {
// take,
// skip,
// orderBy
// }
// });
const data = cache.readQuery({
query: APPOINTMENTS_QUERY,
variables: {
take,
skip,
orderBy
}
});
// cache.writeQuery({
// query: APPOINTMENTS_QUERY,
// data: {
// allAppointments: {
// appointments: [createAppointment, ...data.allAppointments]
// }
// },
// variables: {
// take,
// skip,
// orderBy
// }
// });
// },
cache.writeQuery({
query: APPOINTMENTS_QUERY,
data: {
allAppointments: {
appointments: [createAppointment, ...data.allAppointments]
}
},
variables: {
take,
skip,
orderBy
}
});
},
onCompleted: () => history.push('/')
});

View File

@@ -1,8 +1,8 @@
import React, { useState } from 'react';
import { useHistory } from 'react-router';
import { useMutation, gql, useQuery } from '@apollo/client';
import { APPOINTMENTS_PER_PAGE } from '../../constants';
import { APPOINTMENTS_QUERY } from './AppointmentList';
// import { APPOINTMENTS_PER_PAGE } from '../../constants';
// import { APPOINTMENTS_QUERY } from './AppointmentList';
import Datetime from 'react-datetime';
import "react-datetime/css/react-datetime.css";

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { useHistory } from 'react-router';
import { Link, withRouter } from 'react-router-dom';
import { Link } from 'react-router-dom';
import { AUTH_TOKEN } from '../../constants';
const Header = () => {
@@ -17,6 +17,9 @@ const Header = () => {
<div className="flex flex-fixed">
<Link to="/create" className="ml1 no-underline black">New</Link>
</div>
{/* <div className="flex flex-fixed">
<Link to="/search" className="ml1 no-underline black">Search</Link>
</div> */}
<div className="flex flex-fixed">
{authToken ? (
<div className="ml1 pointer black"

View File

@@ -15,7 +15,7 @@ import {
} from '@apollo/client';
const httpLink = createHttpLink({
uri: 'http://localhost:4000/djhb58fytkh476dk45yh49'
uri: 'http://localhost:4000/graphql'
});
const authLink = setContext((_, { headers }) => {

View File

@@ -23,14 +23,14 @@
"dotenv": "^8.2.0",
"esm": "^3.2.25",
"express": "^4.17.1",
"express-graphql": "^0.12.0",
"graphql": "^15.4.0",
"graphql-compose": "^7.23.0",
"graphql-compose-connection": "^8.0.1",
"graphql-compose-mongoose": "^9.0.0",
"graphql-depth-limit": "^1.1.0",
"graphql-middleware": "^6.0.0",
"graphql-tools": "^7.0.2",
"express-graphql": "*",
"graphql": "*",
"graphql-compose": "*",
"graphql-compose-connection": "*",
"graphql-compose-mongoose": "*",
"graphql-depth-limit": "*",
"graphql-middleware": "*",
"graphql-tools": "*",
"jsonwebtoken": "8.5.1",
"migrate": "^1.7.0",
"mocha": "^8.2.1",
@@ -55,4 +55,4 @@
"nodemon": "^2.0.6",
"prettier": "^2.2.1"
}
}
}

View File

@@ -9,14 +9,17 @@ import './utils/db.js';
import fs from 'fs';
import path from 'path';
import cors from 'cors';
// import getUserId from './utils';
import jwt from 'jsonwebtoken';
const APP_SECRET = 'GraphQL-is-aw3some';
const moduleURL = new URL(import.meta.url);
const __dirname = path.dirname(moduleURL.pathname);
const app = express();
const pubsub = new PubSub();
dotenv.config();
function getTokenPayload(token) {
return jwt.verify(token, APP_SECRET);
return jwt.verify(token, process.env.APP_SECRET);
}
function getUserId(req, authToken) {
@@ -38,19 +41,10 @@ function getUserId(req, authToken) {
throw new Error('Not authenticated');
}
const moduleURL = new URL(import.meta.url);
const __dirname = path.dirname(moduleURL.pathname);
const app = express();
const pubsub = new PubSub();
dotenv.config();
app.use(cors());
app.use('/djhb58fytkh476dk45yh49', graphqlHTTP({
schema: schema,
app.use('/graphql', graphqlHTTP({
schema,
validationRules: [depthLimit(3)],
graphiql: true
}));
@@ -63,17 +57,7 @@ const server = new ApolloServer({
// schema,
cors: true,
playground: process.env.NODE_ENV === 'development' ? true : false,
context: ({ req }) => {
// if (!db) {
// try {
// if (!dbClient.isConnected()) await dbClient.connect()
// mongo = dbClient.db('Calendar') // database name
// console.log(db);
// } catch (e) {
// console.log('--->error while connecting with graphql context (db)', e)
// }
return {
context: ({ req }) => ({
...req,
mongoose,
pubsub,
@@ -81,25 +65,24 @@ const server = new ApolloServer({
req && req.headers.authorization
? getUserId(req)
: null
}),
subscriptions: {
onConnect: (connectionParams) => {
if (connectionParams.authToken) {
return {
mongoose,
userId: getUserId(
null,
connectionParams.authToken
)
};
} else {
return {
mongoose
};
}
}
},
// subscriptions: {
// onConnect: (connectionParams) => {
// if (connectionParams.authToken) {
// return {
// mongoose,
// userId: getUserId(
// null,
// connectionParams.authToken
// )
// };
// } else {
// return {
// mongoose
// };
// }
// }
// },
introspection: true,
tracing: true,
path: '/',
@@ -121,39 +104,5 @@ server.applyMiddleware({
});
app.listen({ port: process.env.PORT }, () => {
console.log(`🚀 Server listening on port ${process.env.PORT}`);
console.log(`😷 Health checks available at ${process.env.HEALTH_ENDPOINT}`);
console.log(`Server listening on port ${process.env.PORT}`);
});
// const { graphqlHTTP } = require('express-graphql');
// const mongoose = require("mongoose");
// const graphqlSchema = require("./graphql/schema/schema")
// const appointmentResolvers = require("./graphql/resolvers/appointment")
// const userResolvers = require("./graphql/resolvers/user")
// var MongoClient = require('mongodb', { useUnifiedTopology: true }).MongoClient;
// // import { MongoClient } from 'mongodb'
// const Query = require('./resolvers/Query');
// const Mutation = require('./resolvers/Mutation');
// const Subscription = require('./resolvers/Subscription');
// const User = require('./resolvers/User');
// const Appointment = require('./resolvers/Appointment');
// const Follow = require('./resolvers/Follow');
// const fs = require('fs');
// const path = require('path');
// const { getUserId } = require('./utils');
// const graphqlResolvers = {
// appointmentResolvers,
// userResolvers
// };
// // const resolvers = {
// // Query,
// // Mutation,
// // Subscription,
// // User,
// // Appointment,
// // Follow
// // };

View File

@@ -24,6 +24,11 @@ const AppointmentSchema = new Schema({
deleted: {
type: Boolean,
required: false
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "user",
required: false
}
});
export default mongoose.model('appointment', AppointmentSchema);

View File

@@ -1,12 +0,0 @@
import mongoose from 'mongoose';
const Schema = mongoose.Schema;
const ProductSchema = new Schema({
title: {
type: String,
required: true
},
qty: {
type: Number
}
});
export default mongoose.model('product', ProductSchema);

View File

@@ -1,33 +1,25 @@
// import Appointment from '../../client/src/components/Appointment.js';
import Product from './models/product.js';
import Appointment from './models/appointment.js';
import User from './models/user.js'
// import { createAppointment } from './resolvers/Mutation.js';
import bcrypt from 'bcrypt';
import User from './models/user.js';
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';
import { createAppointment } from './resolvers/Mutation';
export const resolvers = {
Query: {
async allAppointments() {
return await Appointment.find({ deleted: false })
// return await Appointment.find();
return await Appointment.find({ deleted: false });
},
async oneAppointment(root, args, context, info) {
async oneAppointment(root, args) {
return await Appointment.findOne({
_id: args._id
});
},
async allProducts() {
return await Product.find();
},
async allUsers() {
return await User.find();
},
},
Mutation: {
async signup(root, args, context, info) {
var user = await User.create(args);
async signup(root, args) {
const user = await User.create(args);
user.password = user.generateHash(args.password);
user.save();
@@ -39,11 +31,7 @@ export const resolvers = {
};
},
async login(parent, args, context, info) {
console.log(context);
const { userId } = context;
console.log(userId);
async login(parent, args) {
const user = await User.findOne({
email: args.email
});
@@ -63,47 +51,15 @@ export const resolvers = {
};
},
async createAppointment(parent, args, context, info) {
console.log(context);
const { userId } = context;
console.log("userID", userId);
args.deleted = false;
args.createdBy = userId;
return await Appointment.create(args);
async createAppointment(parent, args, context) {
return await createAppointment(parent, args, context);
},
async updateAppointment(parent, args, context, info) {
console.log(args);
async updateAppointment(parent, args) {
return await Appointment.findOneAndUpdate({
args
}, args, {
new: true
})
},
async deleteAppointment(parent, args, context, info) {
return await Appointment.findOneAndUpdate({ _id: args._id }, { deleted: true })
// return await Appointment.deleteOne({ _id: args._id });
},
async createProduct(root, {
input
}) {
return await Product.create(input);
},
async updateProduct(root, {
_id,
input
}) {
return await Product.findOneAndUpdate({
_id
}, input, {
new: true
})
},
async deleteProduct(root, {
_id
}) {
return await Product.findOneAndRemove({
_id
});
},
}
}
};

View File

@@ -1,28 +1,25 @@
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const { APP_SECRET } = require('../utils');
import dotenv from 'dotenv';
import appointment from '../models/appointment';
function createAppointment(parent, args, context, info) {
dotenv.config();
async function createAppointment(parent, args, context) {
const { userId } = context;
const newAppointment = context.mongo.appointment.create({
data: {
title: args.title,
description: args.description,
createdBy: { connect: { id: userId } }
}
});
return newAppointment;
args.deleted = false;
args.createdBy = userId;
console.log(parent, args, context);
return await appointment.create(args);
}
async function signup(parent, args, context, info) {
async function signup(parent, args, context) {
const password = await bcrypt.hash(args.password, 10);
const user = await context.mongo.user.create({
data: { ...args, password }
});
const token = jwt.sign({ userId: user.id }, APP_SECRET);
const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET);
return {
token,
@@ -30,7 +27,7 @@ async function signup(parent, args, context, info) {
};
}
async function login(parent, args, context, info) {
async function login(parent, args, context) {
const user = await context.mongo.user.findUnique({
where: { email: args.email }
});
@@ -46,7 +43,7 @@ async function login(parent, args, context, info) {
throw new Error('Invalid password');
}
const token = jwt.sign({ userId: user.id }, APP_SECRET);
const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET);
return {
token,
@@ -54,18 +51,18 @@ async function login(parent, args, context, info) {
};
}
async function follow(parent, args, context, info) {
async function follow(parent, args, context) {
const { userId } = context;
const follow = await context.mongo.follow.findUnique({
where: {
linkId_userId: {
linkId: Number(args.linkId),
userId: userId
userId
}
}
});
if (Boolean(follow)) {
if (follow) {
throw new Error(
`Already followed the appointment: ${args.linkId}`
);

View File

@@ -1,4 +1,4 @@
async function feed(parent, args, context, info) {
async function feed(parent, args, context) {
const where = args.filter
? {

View File

@@ -1,26 +1,22 @@
function newLinkSubscribe(parent, args, context, info) {
return context.pubsub.asyncIterator("NEW_LINK")
function newAppointmentSubscribe(parent, args, context) {
return context.pubsub.asyncIterator("NEW_APPOINTMENT");
}
const newAppointment = {
subscribe: newLinkSubscribe,
resolve: payload => {
return payload
},
}
subscribe: newAppointmentSubscribe,
resolve: payload => payload,
};
function newFollowSubscribe(parent, args, context, info) {
return context.pubsub.asyncIterator("NEW_FOLLOW")
function newFollowSubscribe(parent, args, context) {
return context.pubsub.asyncIterator("NEW_FOLLOW");
}
const newFollow = {
subscribe: newFollowSubscribe,
resolve: payload => {
return payload
},
}
resolve: payload => payload,
};
module.exports = {
newAppointment,
newFollow
}
};

View File

@@ -6,11 +6,8 @@ type Query {
take: Int
orderBy: AppointmentOrderByInput
): Feed!
allProducts: [Product]
allAppointments: [Appointment]
oneAppointment(
_id: ID!
): Appointment
oneAppointment(_id: ID!): Appointment
allUsers: [User]
users: [User!]!
}
@@ -31,7 +28,7 @@ type Mutation {
deleted: Boolean
): Appointment!
updateAppointment(
_id: ID!,
_id: ID!
title: String!
description: String
type: String!
@@ -39,31 +36,10 @@ type Mutation {
end: DateTime!
deleted: Boolean
): Appointment
deleteAppointment(
_id: ID!
) : Appointment
createProduct(
input: ProductInput
) : Product
updateProduct(
_id: ID!,
input: ProductInput
): Product
deleteProduct(
_id: ID!
) : Product
signup(
email: String!
password: String!
username: String!
): AuthPayload
login(
email: String!,
password: String!
): AuthPayload
follow(
appointmentId: ID!
): Follow
deleteAppointment(_id: ID!): Appointment
signup(email: String!, password: String!, username: String!): AuthPayload
login(email: String!, password: String!): AuthPayload
follow(appointmentId: ID!): Follow
}
type Subscription {
@@ -77,13 +53,13 @@ type User {
username: String!
email: String!
password: String!
# appointments: [Appointment!]!
appointments: [Appointment!]!
}
input UserInput{
input UserInput {
username: String!
email: String!
password: String!
# appointments: [Appointment!]!
appointments: [AppointmentInput!]!
}
type AuthPayload {
token: String
@@ -101,7 +77,6 @@ type Appointment {
deleted: Boolean
createdBy: User
# follows: [Follow!]!
# createdAt: DateTime!
}
input AppointmentInput {
title: String!
@@ -110,24 +85,12 @@ input AppointmentInput {
start: DateTime!
end: DateTime!
deleted: Boolean
}
}
input AppointmentOrderByInput {
title: Sort
desc: Sort
# createdAt: Sort
}
# Product schemas
type Product {
_id: ID!
title: String!
qty: Int
}
input ProductInput {
title: String!
qty: Int
}
# Follow schemas
type Follow {
_id: ID!

View File

@@ -1,8 +1,10 @@
import jwt from 'jsonwebtoken';
const APP_SECRET = 'GraphQL-is-aw3some';
import dotenv from 'dotenv';
dotenv.config();
function getTokenPayload(token) {
return jwt.verify(token, APP_SECRET);
return jwt.verify(token, process.env.APP_SECRET);
}
function getUserId(req, authToken) {
@@ -25,6 +27,5 @@ function getUserId(req, authToken) {
}
module.exports = {
APP_SECRET,
getUserId
};

File diff suppressed because it is too large Load Diff