feat: complete day 11
This commit is contained in:
+92
-92
@@ -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
|
||||
)}`
|
||||
: ''
|
||||
: "";
|
||||
}
|
||||
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("<h3 style='text-align:center';>404: Page Not Found!</h3>")
|
||||
})
|
||||
.send("<h3 style='text-align:center';>404: Page Not Found!</h3>");
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
app,
|
||||
apollo: server,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
+26
-16
@@ -1,4 +1,4 @@
|
||||
'use strict';
|
||||
"use strict";
|
||||
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/
|
||||
/**
|
||||
* Sequelize File
|
||||
@@ -8,24 +8,28 @@
|
||||
* @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, {
|
||||
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,
|
||||
@@ -33,7 +37,7 @@ let sequelize = new Sequelize(config.DB_DATABASE, config.DB_USERNAME, config.DB_
|
||||
host: config.DB_HOSTNAME,
|
||||
port: config.DB_PORT,
|
||||
logging: console.log,
|
||||
timezone: '-04:00',
|
||||
timezone: "-04:00",
|
||||
pool: {
|
||||
maxConnections: 1,
|
||||
minConnections: 0,
|
||||
@@ -44,13 +48,19 @@ let sequelize = new Sequelize(config.DB_DATABASE, config.DB_USERNAME, config.DB_
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
+4
-1
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
+35
-24
@@ -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,
|
||||
|
||||
+13
-5
@@ -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}`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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!
|
||||
}
|
||||
|
||||
+16
-19
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user