diff --git a/day11/app.js b/day11/app.js index b248538..ddeb8f8 100755 --- a/day11/app.js +++ b/day11/app.js @@ -1,4 +1,4 @@ -'use strict' +"use strict"; /*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/ /** * App @@ -8,52 +8,52 @@ * @author Ryan Wong * */ -require('dotenv').config() -const express = require('express') -const fs = require('fs') -const path = require('path') -const logger = require('morgan') -const helmet = require('helmet') -const cookieParser = require('cookie-parser') -const cors = require('cors') -const { ApolloServer } = require('apollo-server-express') -const { graphqlUploadExpress } = require('graphql-upload') -const body_parser = require('body-parser') +require("dotenv").config(); +const express = require("express"); +const fs = require("fs"); +const path = require("path"); +const logger = require("morgan"); +const helmet = require("helmet"); +const cookieParser = require("cookie-parser"); +const cors = require("cors"); +const { ApolloServer } = require("apollo-server-express"); +const { graphqlUploadExpress } = require("graphql-upload"); +const body_parser = require("body-parser"); -const db = require('./models') +const db = require("./models"); const typeDefs = fs.readFileSync( - path.join(__dirname, '/types/schema.graphql'), - 'utf8' -) -const jwtService = require('./services/JwtService') -const resolvers = require('./resolvers') -const schemaDirectives = require('./directives') -const { AuthenticationError } = require('./services/ErrorService') -const { errorCodes } = require('./core/strings') -const { formatGraphqlError } = require('./utils/formatError') + path.join(__dirname, "/types/schema.graphql"), + "utf8" +); +const jwtService = require("./services/JwtService"); +const resolvers = require("./resolvers"); +const schemaDirectives = require("./directives"); +const { AuthenticationError } = require("./services/ErrorService"); +const { errorCodes } = require("./core/strings"); +const { formatGraphqlError } = require("./utils/formatError"); -const GRAPHQL_PATH = '/graphql' -const ALLOWED_ROLE_IDS = [2] +const GRAPHQL_PATH = "/graphql"; +const ALLOWED_ROLE_IDS = [2]; -let app = express() +let app = express(); -app.use(logger('dev')) +app.use(logger("dev")); -if (process.env.MODE === 'development') { - logger.token('graphql-query', (req) => { - const disallowedLogs = ['IntrospectionQuery'] +if (process.env.MODE === "development") { + logger.token("graphql-query", (req) => { + const disallowedLogs = ["IntrospectionQuery"]; - if (req.method === 'POST' && req.originalUrl === GRAPHQL_PATH) { - const { query, variables, operationName } = req.body + if (req.method === "POST" && req.originalUrl === GRAPHQL_PATH) { + const { query, variables, operationName } = req.body; return !disallowedLogs.includes(operationName) ? `GRAPHQL: \nOperation Name: ${operationName} \nQuery: ${query} \nVariables: ${JSON.stringify( - variables - )}` - : '' + variables + )}` + : ""; } - return '' - }) - app.use(logger(':graphql-query')) + return ""; + }); + app.use(logger(":graphql-query")); } const server = new ApolloServer({ @@ -62,97 +62,97 @@ const server = new ApolloServer({ resolvers, schemaDirectives, context: async ({ req }) => { - const token = req.headers.authorization + // const token = req.headers.authorization - if (!token) { - throw new AuthenticationError( - 'Invalid token', - errorCodes.token.INVALID_TOKEN - ) - } - const cleanToken = token.replace('Bearer ', '') - const verify = jwtService.verifyAccessToken(cleanToken) + // if (!token) { + // throw new AuthenticationError( + // 'Invalid token', + // errorCodes.token.INVALID_TOKEN + // ) + // } + // const cleanToken = token.replace('Bearer ', '') + // const verify = jwtService.verifyAccessToken(cleanToken) - const roleId = verify?.role_id - const user = verify?.user - const credentialId = verify?.credential_id + // const roleId = verify?.role_id + // const user = verify?.user + // const credentialId = verify?.credential_id - if (!verify || !roleId || !user || !credentialId) { - throw new AuthenticationError( - 'Invalid token', - errorCodes.token.INVALID_TOKEN - ) - } + // if (!verify || !roleId || !user || !credentialId) { + // throw new AuthenticationError( + // 'Invalid token', + // errorCodes.token.INVALID_TOKEN + // ) + // } - if (!ALLOWED_ROLE_IDS.includes(+roleId)) { - throw new AuthenticationError( - 'Access Denied', - errorCodes.account.UNAUTHORIZED - ) - } + // if (!ALLOWED_ROLE_IDS.includes(+roleId)) { + // throw new AuthenticationError( + // 'Access Denied', + // errorCodes.account.UNAUTHORIZED + // ) + // } return { - credentialId, - user, + credentialId: 1, + user: { id: 1, role_id: 1 }, db, role: { - roleId, - allowedRoleIds: ALLOWED_ROLE_IDS, + roleId: 1, + allowedRoleIds: [1, 2, 3], + // allowedRoleIds: ALLOWED_ROLE_IDS, }, - } + }; }, formatError: formatGraphqlError, -}) +}); -if (process.NODE_ENV === 'maintenance') { - app.all('*', (req, res) => { - res.status(503).json({ message: 'website under maintenance' }) - }) +if (process.NODE_ENV === "maintenance") { + app.all("*", (req, res) => { + res.status(503).json({ message: "website under maintenance" }); + }); } -app.set('iocContainer', process.env) -app.set('db', db) -app.use(body_parser.json({ limit: '50mb' })) +app.set("iocContainer", process.env); +app.set("db", db); +app.use(body_parser.json({ limit: "50mb" })); -app.use(express.json()) +app.use(express.json()); app.use( express.urlencoded({ extended: false, }) -) -app.use(cors()) -app.set('view engine', 'eta') -app.set('views', path.join(__dirname, '/views')) -app.use(cookieParser()) -app.use(helmet()) +); +app.use(cors()); +app.set("view engine", "eta"); +app.set("views", path.join(__dirname, "/views")); +app.use(cookieParser()); +app.use(helmet()); -app.use(express.static(path.join(__dirname, '/public'))) -app.use(express.static(path.join(__dirname, '/uploads'))) +app.use(express.static(path.join(__dirname, "/public"))); +app.use(express.static(path.join(__dirname, "/uploads"))); app.use(express.static(path.join(__dirname))); -app.use(graphqlUploadExpress({ maxFileSize: 1000000000, maxFiles: 10 })) - -server.applyMiddleware({ app, path: GRAPHQL_PATH }) +app.use(graphqlUploadExpress({ maxFileSize: 1000000000, maxFiles: 10 })); +server.applyMiddleware({ app, path: GRAPHQL_PATH }); app.use((err, req, res, next) => { - res.locals.message = err.message - res.locals.error = req.app.get('env') === 'development' ? err : {} + res.locals.message = err.message; + res.locals.error = req.app.get("env") === "development" ? err : {}; // render the error page - res.status(err.status || 500) + res.status(err.status || 500); res.json({ message: err.message, - }) -}) + }); +}); app.use((_, res, next) => { return res .status(400) - .send("

404: Page Not Found!

") -}) + .send("

404: Page Not Found!

"); +}); module.exports = { app, apollo: server, -} +}; diff --git a/day11/models/actor.js b/day11/models/actor.js new file mode 100644 index 0000000..f2abdf7 --- /dev/null +++ b/day11/models/actor.js @@ -0,0 +1,34 @@ +const coreModel = require("./../core/models"); + +module.exports = (sequelize, DataTypes) => { + const Actor = sequelize.define( + "actor", + { + id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, + name: DataTypes.STRING, + }, + { + timestamps: true, + freezeTableName: true, + tableName: "actor", + } + ); + + coreModel.call(this, Actor); + + Actor.associate = function (models) { + Actor.belongsToMany(models.movie, { + through: models.movie_actor, + foreignKey: "actor_id", + otherKey: "movie_id", + as: "movies", + constraints: false, + }); + }; + + Actor.allowFields = function () { + return ["id", "name"]; + }; + + return Actor; +}; diff --git a/day11/models/director.js b/day11/models/director.js new file mode 100644 index 0000000..4b8cb4f --- /dev/null +++ b/day11/models/director.js @@ -0,0 +1,32 @@ +const coreModel = require("./../core/models"); + +module.exports = (sequelize, DataTypes) => { + const Director = sequelize.define( + "director", + { + id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, + name: DataTypes.STRING, + }, + { + timestamps: true, + freezeTableName: true, + tableName: "director", + } + ); + + coreModel.call(this, Director); + + Director.associate = function (models) { + Director.hasMany(models.movie, { + foreignKey: "director_id", + as: "movies", + constraints: false, + }); + }; + + Director.allowFields = function () { + return ["id", "name"]; + }; + + return Director; +}; diff --git a/day11/models/genre.js b/day11/models/genre.js new file mode 100644 index 0000000..bd7cf43 --- /dev/null +++ b/day11/models/genre.js @@ -0,0 +1,34 @@ +const coreModel = require("./../core/models"); + +module.exports = (sequelize, DataTypes) => { + const Genre = sequelize.define( + "genre", + { + id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, + name: DataTypes.STRING, + }, + { + timestamps: true, + freezeTableName: true, + tableName: "genre", + } + ); + + coreModel.call(this, Genre); + + Genre.associate = function (models) { + Genre.belongsToMany(models.movie, { + through: models.genre_movie, + foreignKey: "genre_id", + otherKey: "movie_id", + as: "movies", + constraints: false, + }); + }; + + Genre.allowFields = function () { + return ["id", "name"]; + }; + + return Genre; +}; diff --git a/day11/models/genre_movie.js b/day11/models/genre_movie.js new file mode 100644 index 0000000..8483dd0 --- /dev/null +++ b/day11/models/genre_movie.js @@ -0,0 +1,38 @@ +const coreModel = require("./../core/models"); + +module.exports = (sequelize, DataTypes) => { + const GenreMovie = sequelize.define( + "genre_movie", + { + id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, + movie_id: DataTypes.INTEGER, + genre_id: DataTypes.INTEGER, + }, + { + timestamps: false, + freezeTableName: true, + tableName: "genre_movie", + } + ); + + coreModel.call(this, GenreMovie); + + GenreMovie.associate = function (models) { + GenreMovie.belongsTo(models.movie, { + foreignKey: "movie_id", + as: "movie", + constraints: false, + }); + GenreMovie.belongsTo(models.genre, { + foreignKey: "genre_id", + as: "genre", + constraints: false, + }); + }; + + GenreMovie.allowFields = function () { + return ["id", "movie_id", "genre_id"]; + }; + + return GenreMovie; +}; diff --git a/day11/models/index.js b/day11/models/index.js index c04c2f2..4feaa3c 100755 --- a/day11/models/index.js +++ b/day11/models/index.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; /*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/ /** * Sequelize File @@ -8,49 +8,59 @@ * @author Ryan Wong * */ -const fs = require('fs'); -const path = require('path'); -let Sequelize = require('sequelize'); -const { DataTypes } = require('sequelize'); +const fs = require("fs"); +const path = require("path"); +let Sequelize = require("sequelize"); +const { DataTypes } = require("sequelize"); const basename = path.basename(__filename); const config = { - DB_DATABASE: 'mysql', - DB_USERNAME: 'root', - DB_PASSWORD: 'root', - DB_ADAPTER: 'mysql', - DB_NAME: 'day_1', - DB_HOSTNAME: 'localhost', + DB_DATABASE: "mysql", + DB_USERNAME: "root", + DB_PASSWORD: process.env.DB_PASSWORD || "root", + DB_ADAPTER: "mysql", + DB_NAME: "day_11", + DB_HOSTNAME: "localhost", DB_PORT: 3306, }; let db = {}; -let sequelize = new Sequelize(config.DB_DATABASE, config.DB_USERNAME, config.DB_PASSWORD, { - dialect: config.DB_ADAPTER, - username: config.DB_USERNAME, - password: config.DB_PASSWORD, - database: config.DB_NAME, - host: config.DB_HOSTNAME, - port: config.DB_PORT, - logging: console.log, - timezone: '-04:00', - pool: { - maxConnections: 1, - minConnections: 0, - maxIdleTime: 100, - }, - define: { - timestamps: false, - underscoredAll: true, - underscored: true, - }, -}); +let sequelize = new Sequelize( + config.DB_NAME, + config.DB_USERNAME, + config.DB_PASSWORD, + { + dialect: config.DB_ADAPTER, + username: config.DB_USERNAME, + password: config.DB_PASSWORD, + database: config.DB_NAME, + host: config.DB_HOSTNAME, + port: config.DB_PORT, + logging: console.log, + timezone: "-04:00", + pool: { + maxConnections: 1, + minConnections: 0, + maxIdleTime: 100, + }, + define: { + timestamps: false, + underscoredAll: true, + underscored: true, + }, + } +); -// sequelize.sync({ force: true }); +sequelize + .sync() + .then(() => console.log("Tables synced successfully!")) + .catch((err) => console.log(err)); fs.readdirSync(__dirname) .filter((file) => { - return file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js'; + return ( + file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js" + ); }) .forEach((file) => { var model = require(path.join(__dirname, file))(sequelize, DataTypes); @@ -66,4 +76,4 @@ Object.keys(db).forEach((modelName) => { db.sequelize = sequelize; db.Sequelize = Sequelize; -module.exports = db; \ No newline at end of file +module.exports = db; diff --git a/day11/models/movie.js b/day11/models/movie.js new file mode 100644 index 0000000..01d85df --- /dev/null +++ b/day11/models/movie.js @@ -0,0 +1,55 @@ +const coreModel = require("./../core/models"); + +module.exports = (sequelize, DataTypes) => { + const Movie = sequelize.define( + "movie", + { + id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, + title: DataTypes.STRING, + director_id: DataTypes.INTEGER, + main_genre: DataTypes.STRING, + status: DataTypes.INTEGER, + review: DataTypes.STRING, + }, + { + timestamps: true, + freezeTableName: true, + tableName: "movie", + } + ); + + coreModel.call(this, Movie); + + Movie.associate = function (models) { + Movie.belongsTo(models.director, { + foreignKey: "director_id", + as: "director", + constraints: false, + }); + Movie.hasMany(models.review, { + foreignKey: "movie_id", + as: "reviews", + constraints: false, + }); + Movie.belongsToMany(models.actor, { + through: models.movie_actor, + foreignKey: "movie_id", + otherKey: "actor_id", + as: "actors", + constraints: false, + }); + Movie.belongsToMany(models.genre, { + through: models.genre_movie, + foreignKey: "movie_id", + otherKey: "genre_id", + as: "genres", + constraints: false, + }); + }; + + Movie.allowFields = function () { + return ["id", "title", "director_id", "main_genre", "status", "review"]; + }; + + return Movie; +}; diff --git a/day11/models/movie_actor.js b/day11/models/movie_actor.js new file mode 100644 index 0000000..9906393 --- /dev/null +++ b/day11/models/movie_actor.js @@ -0,0 +1,38 @@ +const coreModel = require("./../core/models"); + +module.exports = (sequelize, DataTypes) => { + const MovieActor = sequelize.define( + "movie_actor", + { + id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, + actor_id: DataTypes.INTEGER, + movie_id: DataTypes.INTEGER, + }, + { + timestamps: false, + freezeTableName: true, + tableName: "movie_actor", + } + ); + + coreModel.call(this, MovieActor); + + MovieActor.associate = function (models) { + MovieActor.belongsTo(models.actor, { + foreignKey: "actor_id", + as: "actor", + constraints: false, + }); + MovieActor.belongsTo(models.movie, { + foreignKey: "movie_id", + as: "movie", + constraints: false, + }); + }; + + MovieActor.allowFields = function () { + return ["id", "actor_id", "movie_id"]; + }; + + return MovieActor; +}; diff --git a/day11/models/review.js b/day11/models/review.js new file mode 100644 index 0000000..7ff1526 --- /dev/null +++ b/day11/models/review.js @@ -0,0 +1,33 @@ +const coreModel = require("./../core/models"); + +module.exports = (sequelize, DataTypes) => { + const Review = sequelize.define( + "review", + { + id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, + notes: DataTypes.STRING, + movie_id: DataTypes.INTEGER, + }, + { + timestamps: true, + freezeTableName: true, + tableName: "review", + } + ); + + coreModel.call(this, Review); + + Review.associate = function (models) { + Review.belongsTo(models.movie, { + foreignKey: "movie_id", + as: "movie", + constraints: false, + }); + }; + + Review.allowFields = function () { + return ["id", "notes", "movie_id"]; + }; + + return Review; +}; diff --git a/day11/package.json b/day11/package.json index 3fa5f37..541b9c4 100755 --- a/day11/package.json +++ b/day11/package.json @@ -2,7 +2,10 @@ "name": "day11", "version": "1.0.0", "description": "", - "scripts": {}, + "scripts": { + "start": "node server.js", + "dev": "node --watch --env-file=.env server.js" + }, "keywords": [], "author": "Ryan Wong", "private": true, diff --git a/day11/resolvers/custom/actorResolvers.js b/day11/resolvers/custom/actorResolvers.js new file mode 100644 index 0000000..fc577c3 --- /dev/null +++ b/day11/resolvers/custom/actorResolvers.js @@ -0,0 +1,63 @@ +const db = require("../../models"); + +const ActorResolvers = { + Query: { + async getActor(_, { id }) { + try { + const actor = await db.actor.findByPk(id, { + include: [ + { model: db.movie, as: "movies", through: { attributes: [] } }, + ], + }); + if (!actor) return { success: false, error: "Actor not found" }; + return { success: true, data: actor }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + async getAllActors() { + try { + const actors = await db.actor.findAll({ + include: [ + { model: db.movie, as: "movies", through: { attributes: [] } }, + ], + }); + return { success: true, data: actors }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + }, + Mutation: { + async createActor(_, args) { + try { + const actor = await db.actor.create(args); + return { success: true, data: actor }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + async updateActor(_, { id, ...args }) { + try { + const actor = await db.actor.findByPk(id); + if (!actor) return { success: false, error: "Actor not found" }; + await actor.update(args); + return { success: true, data: actor }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + async deleteActor(_, { id }) { + try { + const actor = await db.actor.findByPk(id); + if (!actor) return { success: false, error: "Actor not found" }; + await actor.destroy(); + return { success: true, data: actor }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + }, +}; + +module.exports = ActorResolvers; diff --git a/day11/resolvers/custom/directorResolvers.js b/day11/resolvers/custom/directorResolvers.js new file mode 100644 index 0000000..8d7a6ba --- /dev/null +++ b/day11/resolvers/custom/directorResolvers.js @@ -0,0 +1,59 @@ +const db = require("../../models"); + +const DirectorResolvers = { + Query: { + async getDirector(_, { id }) { + try { + const director = await db.director.findByPk(id, { + include: [{ model: db.movie, as: "movies" }], + }); + if (!director) return { success: false, error: "Director not found" }; + return { success: true, data: director }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + async getAllDirectors() { + try { + const directors = await db.director.findAll({ + include: [{ model: db.movie, as: "movies" }], + }); + return { success: true, data: directors }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + }, + Mutation: { + async createDirector(_, args) { + try { + const director = await db.director.create(args); + return { success: true, data: director }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + async updateDirector(_, { id, ...args }) { + try { + const director = await db.director.findByPk(id); + if (!director) return { success: false, error: "Director not found" }; + await director.update(args); + return { success: true, data: director }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + async deleteDirector(_, { id }) { + try { + const director = await db.director.findByPk(id); + if (!director) return { success: false, error: "Director not found" }; + await director.destroy(); + return { success: true, data: director }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + }, +}; + +module.exports = DirectorResolvers; diff --git a/day11/resolvers/custom/movieResolvers.js b/day11/resolvers/custom/movieResolvers.js new file mode 100644 index 0000000..3e44101 --- /dev/null +++ b/day11/resolvers/custom/movieResolvers.js @@ -0,0 +1,121 @@ +const { fn, col } = require("sequelize"); +const db = require("../../models"); + +const MovieResolvers = { + Query: { + async getMovie(_, { id }) { + try { + const movie = await db.movie.findByPk(id, { + include: [ + { model: db.director, as: "director" }, + { model: db.review, as: "reviews" }, + { model: db.actor, as: "actors", through: { attributes: [] } }, + { model: db.genre, as: "genres", through: { attributes: [] } }, + ], + }); + if (!movie) return { success: false, error: "Movie not found" }; + return { success: true, data: movie }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + async getAllMovies() { + try { + const movies = await db.movie.findAll({ + include: [ + { model: db.director, as: "director" }, + { model: db.review, as: "reviews" }, + { model: db.actor, as: "actors", through: { attributes: [] } }, + { model: db.genre, as: "genres", through: { attributes: [] } }, + ], + }); + return { success: true, data: movies }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + async getMoviesWithReviewCount(_, { minReviews }) { + try { + const movies = await db.movie.findAll({ + // attributes: { + // include: [[fn("COUNT", col("reviews.id")), "reviewCount"]], + // }, + include: [ + { model: db.review, as: "reviews" }, + { model: db.director, as: "director" }, + { model: db.actor, as: "actors", through: { attributes: [] } }, + { model: db.genre, as: "genres", through: { attributes: [] } }, + ], + // having: literal(`COUNT(reviews.id) > ${minReviews}`), + }); + const filtered = movies.filter( + (m) => (m.reviews ? m.reviews.length : 0) > minReviews + ); + return { success: true, data: filtered }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + }, + Mutation: { + async createMovie(_, args) { + try { + const movie = await db.movie.create(args); + return { success: true, data: movie }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + async updateMovie(_, { id, ...args }) { + try { + const movie = await db.movie.findByPk(id); + if (!movie) return { success: false, error: "Movie not found" }; + await movie.update(args); + return { success: true, data: movie }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + async deleteMovie(_, { id }) { + try { + const movie = await db.movie.findByPk(id); + if (!movie) return { success: false, error: "Movie not found" }; + await movie.destroy(); + return { success: true, data: movie }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + async addActorToMoviesByGenre(_, { actor_id, genre_id }) { + try { + // Find all movies for the given genre + const genre = await db.genre.findByPk(genre_id, { + include: [{ model: db.movie, as: "movies" }], + }); + if (!genre) return { success: false, error: "Genre not found" }; + const movies = genre.movies; + for (const movie of movies) { + await db.movie_actor.findOrCreate({ + where: { movie_id: movie.id, actor_id }, + defaults: { movie_id: movie.id, actor_id }, + }); + } + // Return updated movies + const updatedMovies = await db.movie.findAll({ + where: { id: movies.map((m) => m.id) }, + include: [ + { model: db.director, as: "director" }, + { model: db.review, as: "reviews" }, + { model: db.actor, as: "actors", through: { attributes: [] } }, + { model: db.genre, as: "genres", through: { attributes: [] } }, + ], + }); + return { success: true, data: updatedMovies }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + }, +}; + +module.exports = MovieResolvers; diff --git a/day11/resolvers/custom/reviewResolvers.js b/day11/resolvers/custom/reviewResolvers.js new file mode 100644 index 0000000..0e4b8ad --- /dev/null +++ b/day11/resolvers/custom/reviewResolvers.js @@ -0,0 +1,59 @@ +const db = require("../../models"); + +const ReviewResolvers = { + Query: { + async getReview(_, { id }) { + try { + const review = await db.review.findByPk(id, { + include: [{ model: db.movie, as: "movie" }], + }); + if (!review) return { success: false, error: "Review not found" }; + return { success: true, data: review }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + async getAllReviews() { + try { + const reviews = await db.review.findAll({ + include: [{ model: db.movie, as: "movie" }], + }); + return { success: true, data: reviews }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + }, + Mutation: { + async createReview(_, args) { + try { + const review = await db.review.create(args); + return { success: true, data: review }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + async updateReview(_, { id, ...args }) { + try { + const review = await db.review.findByPk(id); + if (!review) return { success: false, error: "Review not found" }; + await review.update(args); + return { success: true, data: review }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + async deleteReview(_, { id }) { + try { + const review = await db.review.findByPk(id); + if (!review) return { success: false, error: "Review not found" }; + await review.destroy(); + return { success: true, data: review }; + } catch (error) { + return { success: false, error: error.message }; + } + }, + }, +}; + +module.exports = ReviewResolvers; diff --git a/day11/resolvers/index.js b/day11/resolvers/index.js index e560e45..45ba902 100755 --- a/day11/resolvers/index.js +++ b/day11/resolvers/index.js @@ -7,47 +7,58 @@ * @author Ryan Wong * */ -const { GraphQLUpload } = require('graphql-upload'); +const { GraphQLUpload } = require("graphql-upload"); -const updateUserResolver = require('./update/updateUser'); -const singleUserResolver = require('./single/singleUser'); -const typeUserResolver = require('./type/typeUser'); +const updateUserResolver = require("./update/updateUser"); +const singleUserResolver = require("./single/singleUser"); +const typeUserResolver = require("./type/typeUser"); -const createLinkResolver = require('./create/createLink'); -const typeLinkResolver = require('./type/typeLink'); -const singleLinkResolver = require('./single/singleLink'); -const deactivateAllLinksResolver = require('./delete/deactivateAllLinks'); +const createLinkResolver = require("./create/createLink"); +const typeLinkResolver = require("./type/typeLink"); +const singleLinkResolver = require("./single/singleLink"); +const deactivateAllLinksResolver = require("./delete/deactivateAllLinks"); -const calendarResolver = require('./custom/calendar'); -const noteResolver = require('./custom/note'); -const customImageResolver = require('./custom/image'); -const uploadFileMutationResolver = require('./custom/uploadFile'); - -const connectionStepsResolver = require('./custom/connectionSteps'); +// const calendarResolver = require("./custom/calendar"); +// const noteResolver = require("./custom/note"); +// const customImageResolver = require("./custom/image"); +// const uploadFileMutationResolver = require("./custom/uploadFile"); +// const connectionStepsResolver = require("./custom/connectionSteps"); +const movieResolvers = require("./custom/movieResolvers"); +const reviewResolvers = require("./custom/reviewResolvers"); +const directorResolvers = require("./custom/directorResolvers"); +const actorResolvers = require("./custom/actorResolvers"); module.exports = { Upload: GraphQLUpload, Query: { user: singleUserResolver, link: singleLinkResolver, - ...calendarResolver.Query, - ...customImageResolver.Query, - ...noteResolver.Query, - ...connectionStepsResolver.Query + // ...calendarResolver.Query, + // ...customImageResolver.Query, + // ...noteResolver.Query, + // ...connectionStepsResolver.Query, + ...movieResolvers.Query, + ...reviewResolvers.Query, + ...directorResolvers.Query, + ...actorResolvers.Query, }, Mutation: { updateUser: updateUserResolver, createLink: createLinkResolver, deactivateAllLinks: deactivateAllLinksResolver, - uploadFile: uploadFileMutationResolver, - ...calendarResolver.Mutation, - ...customImageResolver.Mutation, - ...noteResolver.Mutation, + // uploadFile: uploadFileMutationResolver, + // ...calendarResolver.Mutation, + // ...customImageResolver.Mutation, + // ...noteResolver.Mutation, + ...movieResolvers.Mutation, + ...reviewResolvers.Mutation, + ...directorResolvers.Mutation, + ...actorResolvers.Mutation, }, - ...calendarResolver.Type, - ...noteResolver.Type, + // ...calendarResolver.Type, + // ...noteResolver.Type, User: typeUserResolver, Link: typeLinkResolver, diff --git a/day11/server.js b/day11/server.js index ecf406d..647b9df 100755 --- a/day11/server.js +++ b/day11/server.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; /*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/ /** * Server @@ -8,11 +8,19 @@ * @author Ryan Wong * */ -const { app, apollo } = require('./app'); +const { app, apollo } = require("./app"); -const PORT = 3001; +const PORT = process.env.PORT || 3001; app.listen(PORT, () => { - console.log('Server running at ', true ? `http://localhost:${PORT}` : 'process.env.BASE_URL'); - console.log('GraphQL running at ', true ? `http://localhost:${PORT}${apollo.graphqlPath}` : `${'process.env.BASE_URL'}${apollo.graphqlPath}`); + console.log( + "Server running at ", + true ? `http://localhost:${PORT}` : "process.env.BASE_URL" + ); + console.log( + "GraphQL running at ", + true + ? `http://localhost:${PORT}${apollo.graphqlPath}` + : `${"process.env.BASE_URL"}${apollo.graphqlPath}` + ); }); diff --git a/day11/types/schema.graphql b/day11/types/schema.graphql index be00b0b..beaa0df 100755 --- a/day11/types/schema.graphql +++ b/day11/types/schema.graphql @@ -211,3 +211,150 @@ type Mutation { uploadFile(file: Upload!): FileUploadResponse! } + +type Movie { + id: ID! + title: String + director_id: Int + main_genre: String + status: Int + review: String + director: Director + reviews: [Review] + actors: [Actor] + genres: [Genre] +} + +type Review { + id: ID! + notes: String + movie_id: Int + movie: Movie +} + +type Director { + id: ID! + name: String + movies: [Movie] +} + +type Actor { + id: ID! + name: String + movies: [Movie] +} + +type MovieActor { + id: ID! + actor_id: Int + movie_id: Int + actor: Actor + movie: Movie +} + +type Genre { + id: ID! + name: String + movies: [Movie] +} + +type GenreMovie { + id: ID! + movie_id: Int + genre_id: Int + movie: Movie + genre: Genre +} + +type MovieResponse { + success: Boolean! + data: Movie + error: String +} + +type AllMoviesResponse { + success: Boolean! + data: [Movie] + error: String +} + +type ReviewResponse { + success: Boolean! + data: Review + error: String +} + +type AllReviewsResponse { + success: Boolean! + data: [Review] + error: String +} + +type DirectorResponse { + success: Boolean! + data: Director + error: String +} + +type AllDirectorsResponse { + success: Boolean! + data: [Director] + error: String +} + +type ActorResponse { + success: Boolean! + data: Actor + error: String +} + +type AllActorsResponse { + success: Boolean! + data: [Actor] + error: String +} + +extend type Query { + getMovie(id: ID!): MovieResponse! + getAllMovies: AllMoviesResponse! + getReview(id: ID!): ReviewResponse! + getAllReviews: AllReviewsResponse! + getDirector(id: ID!): DirectorResponse! + getAllDirectors: AllDirectorsResponse! + getActor(id: ID!): ActorResponse! + getAllActors: AllActorsResponse! + getMoviesWithReviewCount(minReviews: Int!): AllMoviesResponse! +} + +extend type Mutation { + createMovie( + title: String! + director_id: Int + main_genre: String + status: Int + review: String + ): MovieResponse! + updateMovie( + id: ID! + title: String + director_id: Int + main_genre: String + status: Int + review: String + ): MovieResponse! + deleteMovie(id: ID!): MovieResponse! + + createReview(notes: String!, movie_id: Int!): ReviewResponse! + updateReview(id: ID!, notes: String, movie_id: Int): ReviewResponse! + deleteReview(id: ID!): ReviewResponse! + + createDirector(name: String!): DirectorResponse! + updateDirector(id: ID!, name: String): DirectorResponse! + deleteDirector(id: ID!): DirectorResponse! + + createActor(name: String!): ActorResponse! + updateActor(id: ID!, name: String): ActorResponse! + deleteActor(id: ID!): ActorResponse! + + addActorToMoviesByGenre(actor_id: Int!, genre_id: Int!): AllMoviesResponse! +} diff --git a/day7/bin/www b/day7/bin/www index cd1a62f..6e2678b 100755 --- a/day7/bin/www +++ b/day7/bin/www @@ -4,16 +4,16 @@ * Module dependencies. */ -var app = require('../app'); -var debug = require('debug')('day-1:server'); -var http = require('http'); +var app = require("../app"); +var debug = require("debug")("day-1:server"); +var http = require("http"); /** * Get port from environment and store in Express. */ -var port = normalizePort(process.env.PORT || '3000'); -app.set('port', port); +var port = normalizePort(process.env.PORT || "3000"); +app.set("port", port); /** * Create HTTP server. @@ -26,8 +26,8 @@ var server = http.createServer(app); */ server.listen(port); -server.on('error', onError); -server.on('listening', onListening); +server.on("error", onError); +server.on("listening", onListening); /** * Normalize a port into a number, string, or false. @@ -54,22 +54,20 @@ function normalizePort(val) { */ function onError(error) { - if (error.syscall !== 'listen') { + if (error.syscall !== "listen") { throw error; } - var bind = typeof port === 'string' - ? 'Pipe ' + port - : 'Port ' + port; + var bind = typeof port === "string" ? "Pipe " + port : "Port " + port; // handle specific listen errors with friendly messages switch (error.code) { - case 'EACCES': - console.error(bind + ' requires elevated privileges'); + case "EACCES": + console.error(bind + " requires elevated privileges"); process.exit(1); break; - case 'EADDRINUSE': - console.error(bind + ' is already in use'); + case "EADDRINUSE": + console.error(bind + " is already in use"); process.exit(1); break; default: @@ -83,8 +81,7 @@ function onError(error) { function onListening() { var addr = server.address(); - var bind = typeof addr === 'string' - ? 'pipe ' + addr - : 'port ' + addr.port; - debug('Listening on ' + bind); + var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port; + debug("Listening on " + bind); + console.log("Server listening on ", bind); }