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);
}